feat: swappable colour schemes
This commit is contained in:
parent
305a812dfa
commit
23359f9f54
|
|
@ -59,6 +59,8 @@ add_executable(ParticleTrackTrace MACOSX_BUNDLE main.cpp
|
|||
layers/Technique.cpp
|
||||
layers/Technique.h
|
||||
layers/enums.h
|
||||
layers/luts.cpp
|
||||
layers/luts.h
|
||||
Program.cpp
|
||||
Program.h
|
||||
commands/TimerCallbackCommand.h
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ Program::Program(QWidget *parent): QVTKOpenGLNativeWidget(parent) {
|
|||
setRenderWindow(this->win);
|
||||
this->interact = win->GetInteractor();
|
||||
this->cam = createNormalisedCamera();
|
||||
this->activeIdx = -1;
|
||||
this->activeTech = NOTECH;
|
||||
|
||||
this->win->SetNumberOfLayers(0);
|
||||
setWinProperties();
|
||||
|
|
@ -75,38 +75,44 @@ void Program::removeTechnique(Technique *technique) {
|
|||
auto it = std::find(this->techniques.begin(), this->techniques.end(), technique);
|
||||
if (it != this->techniques.end()) {
|
||||
int idx = it - this->techniques.begin();
|
||||
if (idx == this->activeIdx) {
|
||||
if (idx == this->activeTech) {
|
||||
throw std::out_of_range("Can't remove active technique.");
|
||||
}
|
||||
this->techniques.erase(it);
|
||||
this->activeIdx = -1;
|
||||
setActiveTechnique(0);
|
||||
this->activeTech = NOTECH;
|
||||
setActiveTechnique(COLGLYPH);
|
||||
}
|
||||
}
|
||||
|
||||
void Program::requestRender() {
|
||||
this->win->Render();
|
||||
}
|
||||
|
||||
void Program::updateData(int t) {
|
||||
// FIXME: think on how to update techniques; do we update all? just active? unsure.
|
||||
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.
|
||||
if (idx == this->activeIdx) {
|
||||
if (tech == this->activeTech) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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!");
|
||||
}
|
||||
|
||||
if (this->activeIdx >= 0 and this->activeIdx < this->techniques.size())
|
||||
this->techniques[this->activeIdx]->unbind(this->win, this->interact);
|
||||
if (this->activeTech >= 0 and this->activeTech < this->techniques.size())
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ private:
|
|||
/** This attribute models a variable number of vtkRenderers, managed through the abstract Technique class.
|
||||
*/
|
||||
std::vector<Technique *> techniques;
|
||||
int activeIdx;
|
||||
ActiveTechnique activeTech;
|
||||
|
||||
|
||||
/** The window this program's layers render to.
|
||||
|
|
@ -72,10 +72,10 @@ public:
|
|||
*/
|
||||
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(int idx);
|
||||
|
||||
void setActiveTechnique(ActiveTechnique tech);
|
||||
|
||||
vtkSmartPointer<vtkCamera> getCamera();
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ void MainWindow::setupTechniques() {
|
|||
program->addTechnique(technique1);
|
||||
program->addTechnique(technique2);
|
||||
|
||||
program->setActiveTechnique(0);
|
||||
program->setActiveTechnique(COLGLYPH);
|
||||
|
||||
// TODO: implement feature to call this function on widget
|
||||
// l->spoofPoints();
|
||||
|
|
@ -85,6 +85,7 @@ void MainWindow::setupTechniques() {
|
|||
void MainWindow::on_FirstButton_clicked(bool checked) {
|
||||
if (checked) {
|
||||
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) {
|
||||
if (checked) {
|
||||
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) {
|
||||
if (checked) {
|
||||
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) {
|
||||
if (checked) {
|
||||
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) {
|
||||
if (checked) {
|
||||
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()) {
|
||||
t->setSaturationMode(SATURATED);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +143,7 @@ void MainWindow::on_DesaturateButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setSaturationMode(DESATURATED);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +153,7 @@ void MainWindow::on_CircleButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setGlyphStyle(CIRCLE);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +163,7 @@ void MainWindow::on_TriangleButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setGlyphStyle(TRIANGLE);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +173,7 @@ void MainWindow::on_SquareButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setGlyphStyle(SQUARE);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +183,7 @@ void MainWindow::on_HexagonButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setGlyphStyle(HEXAGON);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +193,7 @@ void MainWindow::on_FullySampledButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setSamplingMode(FULLYSAMPLED);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,6 +203,7 @@ void MainWindow::on_RegularlySubsampledButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setSamplingMode(REGULARLYSUBSAMPLED);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +213,7 @@ void MainWindow::on_IregularlySubsampledButton_clicked(bool checked) {
|
|||
for (Technique *t : ui->program->getTechniques()) {
|
||||
t->setSamplingMode(IRREGULARLYSUBSAMPLED);
|
||||
}
|
||||
ui->program->requestRender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ UVGrid::UVGrid(string path) {
|
|||
for (auto vel: views::zip(us, vs)) {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ public:
|
|||
size_t latSize;
|
||||
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.
|
||||
* @return longitudinal spacing
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca
|
|||
points->InsertNextPoint(worldPos[0], worldPos[1], 0);
|
||||
this->particlesBeached->InsertNextValue(0);
|
||||
this->particlesAge->InsertNextValue(0);
|
||||
this->lutIdx->InsertNextValue(0);
|
||||
|
||||
// FIXME: The below lines cause some weird interaction with our vtkTimer.
|
||||
// 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) {
|
||||
this->particlesAge = ints;
|
||||
}
|
||||
|
||||
|
||||
void SpawnPointCallback::setIdx(const vtkSmartPointer<vtkIntArray> &idx) {
|
||||
this->lutIdx = idx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public:
|
|||
void setBeached(const vtkSmartPointer<vtkIntArray> &parts);
|
||||
|
||||
void setAge(const vtkSmartPointer<vtkIntArray> &parts);
|
||||
void setIdx(const vtkSmartPointer<vtkIntArray> &idx);
|
||||
|
||||
void setUVGrid(const std::shared_ptr<UVGrid> &uvGrid);
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ private:
|
|||
vtkSmartPointer<vtkRenderer> ren;
|
||||
vtkSmartPointer<vtkIntArray> particlesBeached;
|
||||
vtkSmartPointer<vtkIntArray> particlesAge;
|
||||
vtkSmartPointer<vtkIntArray> lutIdx;
|
||||
std::shared_ptr<UVGrid> uvGrid;
|
||||
vtkSmartPointer<vtkAbstractTransform> inverseCartographicProjection;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <vtkArrowSource.h>
|
||||
|
||||
#include "../CartographicTransformation.h"
|
||||
#include "luts.h"
|
||||
|
||||
using std::numbers::pi;
|
||||
|
||||
|
|
@ -38,59 +39,41 @@ EColLayer::EColLayer(std::shared_ptr<UVGrid> uvGrid) {
|
|||
this->numLats = uvGrid->latSize;
|
||||
this->numLons = uvGrid->lonSize;
|
||||
|
||||
this->strength = vtkSmartPointer<vtkDoubleArray>::New();
|
||||
this->strength->SetName("strength");
|
||||
this->strength->SetNumberOfComponents(1);
|
||||
this->strength->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));
|
||||
this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
|
||||
this->lutIdx->SetName("lutIdx");
|
||||
this->lutIdx->SetNumberOfComponents(1);
|
||||
this->lutIdx->SetNumberOfTuples((numLats-1)*(numLons-1));
|
||||
|
||||
calcMaxStrength();
|
||||
buildLuts();
|
||||
readCoordinates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a given rgba colour to a range of values [start, end] in the lut.
|
||||
* @param lut : lookuptable to operate on.
|
||||
* @ param start : starting index of range to assign
|
||||
* @ param end: ending index of range to assign
|
||||
* @param r : red value [0,1]
|
||||
* @param g : green value [0,1]
|
||||
* @param n : blue value [0,1]
|
||||
* @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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// builds a 4-way lookuptable, used to encode the directional component
|
||||
vtkSmartPointer<vtkLookupTable> buildLutDirs() {
|
||||
vtkNew<vtkLookupTable> lut;
|
||||
lut->SetNumberOfColors(360);
|
||||
lut->SetTableRange(0, 359);
|
||||
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);
|
||||
void EColLayer::calcMaxStrength() {
|
||||
double u1 = uvGrid->uMin,
|
||||
u2 = uvGrid->uMax,
|
||||
v1 = uvGrid->vMin,
|
||||
v2 = uvGrid->vMax;
|
||||
|
||||
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.
|
||||
void EColLayer::readCoordinates() {
|
||||
vtkNew<vtkPoints> points;
|
||||
|
|
@ -134,35 +117,27 @@ void EColLayer::readCoordinates() {
|
|||
}
|
||||
u /= 4;
|
||||
v /= 4;
|
||||
this->strength->SetTuple1(cellId, std::sqrt(u*u + v*v));
|
||||
this->direction->SetTuple1(cellId++, atan(u/v)*180/pi);
|
||||
this->lutIdx->SetTuple1(cellId++, calcIndex(u,v, this->maxStrength));
|
||||
}
|
||||
latIndex++;
|
||||
}
|
||||
lonIndex++;
|
||||
}
|
||||
|
||||
data->GetCellData()->AddArray(this->strength);
|
||||
data->GetCellData()->AddArray(this->direction);
|
||||
// data->GetCellData()->SetActiveScalars("strength");
|
||||
data->GetCellData()->AddArray(this->lutIdx);
|
||||
data->GetCellData()->SetActiveScalars("lutIdx");
|
||||
|
||||
vtkNew<vtkPolyDataMapper>(mapper);
|
||||
mapper->SetInputData(data);
|
||||
mapper->SetLookupTable(buildLutDirs());
|
||||
mapper->UseLookupTableScalarRangeOn();
|
||||
mapper->Update();
|
||||
data->GetCellData()->SetActiveScalars("direction");
|
||||
this->mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||
this->mapper->SetInputData(data);
|
||||
setColourMode(COMPLEMENTARY);
|
||||
this->mapper->UseLookupTableScalarRangeOn();
|
||||
this->mapper->Update();
|
||||
|
||||
vtkNew<vtkActor> actor;
|
||||
actor->SetMapper(mapper);
|
||||
actor->SetMapper(this->mapper);
|
||||
actor->GetProperty()->SetColor(0, 1, 0);
|
||||
actor->GetProperty()->SetOpacity(0.5);
|
||||
|
||||
// vtkNew<vtkActor> act2;
|
||||
// act2->SetMapper(mapper);
|
||||
// act2->GetProperty()->SetRepresentationToWireframe();
|
||||
// ren->AddActor(act2);
|
||||
|
||||
this->ren->AddActor(actor);
|
||||
}
|
||||
|
||||
|
|
@ -181,9 +156,50 @@ void EColLayer::updateData(int t) {
|
|||
}
|
||||
u /= 4;
|
||||
v /= 4;
|
||||
this->strength->SetTuple1(i, std::sqrt(u*u + v*v));
|
||||
this->direction->SetTuple1(i++, atan(u/v)*180/pi);
|
||||
this->lutIdx->SetTuple1(i++, calcIndex(u,v, this->maxStrength));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,13 @@
|
|||
*/
|
||||
class EColLayer : public Layer {
|
||||
private:
|
||||
vtkSmartPointer<vtkDoubleArray> strength;
|
||||
vtkSmartPointer<vtkDoubleArray> direction;
|
||||
vtkSmartPointer<vtkIntArray> lutIdx;
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper;
|
||||
std::vector<vtkSmartPointer<vtkLookupTable>> tables;
|
||||
ColourMode activeColourMode;
|
||||
SaturationMode activeSaturationMode;
|
||||
std::shared_ptr<UVGrid> uvGrid;
|
||||
double maxStrength;
|
||||
int numLats;
|
||||
int numLons;
|
||||
|
||||
|
|
@ -25,11 +29,13 @@ private:
|
|||
*/
|
||||
void readCoordinates();
|
||||
|
||||
|
||||
/** This private function builds up the lookup table VTK uses when determning what colour each cell ought to be.
|
||||
/** This function calculates the maximum strength values for the associated uvGrid
|
||||
*/
|
||||
// TODO: implement this function.
|
||||
void buildLut();
|
||||
void calcMaxStrength();
|
||||
|
||||
/** This function builds the used lookuptables and adds them to the tables attribute.
|
||||
*/
|
||||
void buildLuts();
|
||||
|
||||
public:
|
||||
/** Constructor.
|
||||
|
|
@ -39,9 +45,37 @@ public:
|
|||
/** updates the map to reflect the given timestamp in the dataset.
|
||||
* @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
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#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() {
|
||||
vtkNew<SpawnPointCallback> newPointCallBack;
|
||||
newPointCallBack->setPoints(this->points);
|
||||
|
|
@ -28,6 +31,7 @@ vtkSmartPointer<SpawnPointCallback> LColLayer::createSpawnPointCallback() {
|
|||
newPointCallBack->setUVGrid(this->uvGrid);
|
||||
newPointCallBack->setBeached(this->particlesBeached);
|
||||
newPointCallBack->setAge(this->particlesAge);
|
||||
newPointCallBack->setIdx(this->lutIdx);
|
||||
return newPointCallBack;
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +84,10 @@ LColLayer::LColLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<AdvectionKe
|
|||
this->particlesAge->SetName("particlesAge");
|
||||
this->particlesAge->SetNumberOfComponents(1);
|
||||
|
||||
this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
|
||||
this->lutIdx->SetName("lutIdx");
|
||||
this->lutIdx->SetNumberOfComponents(1);
|
||||
|
||||
|
||||
// pipeline 2
|
||||
this->data = vtkSmartPointer<vtkPolyData>::New();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ private:
|
|||
vtkSmartPointer<vtkPolyData> data;
|
||||
vtkSmartPointer<vtkIntArray> particlesBeached;
|
||||
vtkSmartPointer<vtkIntArray> particlesAge;
|
||||
vtkSmartPointer<vtkIntArray> lutIdx;
|
||||
vtkSmartPointer<vtkIntArray> cellParticleDensity;
|
||||
vtkSmartPointer<SpawnPointCallback> callback;
|
||||
std::unique_ptr<AdvectionKernel> advector;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <vtkCamera.h>
|
||||
|
||||
#include "../CartographicTransformation.h"
|
||||
#include "luts.h"
|
||||
|
||||
vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
|
||||
vtkNew<SpawnPointCallback> newPointCallBack;
|
||||
|
|
@ -27,62 +28,13 @@ vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
|
|||
newPointCallBack->setUVGrid(this->uvGrid);
|
||||
newPointCallBack->setBeached(this->particlesBeached);
|
||||
newPointCallBack->setAge(this->particlesAge);
|
||||
newPointCallBack->setIdx(this->lutIdx);
|
||||
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) {
|
||||
this->luts.push(buildLutOpacity(512));
|
||||
this->luts.push(buildLutBrightness(512));
|
||||
buildLuts();
|
||||
|
||||
this->ren = vtkSmartPointer<vtkRenderer>::New();
|
||||
this->ren->SetLayer(2);
|
||||
|
|
@ -92,16 +44,14 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
|
|||
data->SetPoints(this->points);
|
||||
|
||||
this->particlesBeached = vtkSmartPointer<vtkIntArray>::New();
|
||||
this->particlesBeached->SetName("particlesBeached");
|
||||
this->particlesBeached->SetNumberOfComponents(0);
|
||||
|
||||
this->particlesAge = vtkSmartPointer<vtkIntArray>::New();
|
||||
this->particlesAge->SetName("particlesAge");
|
||||
this->particlesAge->SetNumberOfComponents(0);
|
||||
|
||||
data->GetPointData()->AddArray(this->particlesBeached);
|
||||
data->GetPointData()->AddArray(this->particlesAge);
|
||||
data->GetPointData()->SetActiveScalars("particlesAge");
|
||||
this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
|
||||
this->lutIdx->SetName("lutIdx");
|
||||
this->lutIdx->SetNumberOfComponents(0);
|
||||
|
||||
data->GetPointData()->AddArray(this->lutIdx);
|
||||
data->GetPointData()->SetActiveScalars("lutIdx");
|
||||
|
||||
advector = std::move(advectionKernel);
|
||||
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->SetColorModeToMapScalars();
|
||||
|
||||
auto lut = this->luts.front();
|
||||
mapper->SetLookupTable(lut);
|
||||
this->luts.pop();
|
||||
this->luts.push(lut);
|
||||
|
||||
setColourMode(COMPLEMENTARY);
|
||||
this->mapper->UseLookupTableScalarRangeOn();
|
||||
this->mapper->Update();
|
||||
|
||||
|
|
@ -140,6 +86,17 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
|
|||
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() {
|
||||
for (int i=0; i < 330; i+=5) {
|
||||
for (int j=0; j < 330; j+=5) {
|
||||
|
|
@ -162,8 +119,8 @@ void LGlyphLayer::updateData(int t) {
|
|||
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()) {
|
||||
// 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->lutIdx->SetTuple1(n, -1);
|
||||
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 (oldX == point[0] and oldY == point[1]) {
|
||||
this->particlesBeached->SetValue(n, beachedFor+1);
|
||||
this->lutIdx->SetTuple1(n, beachedFor == this->beachedAtNumberOfTimes-2 ? calcIndex(age+1, true) : calcIndex(age+1, false));
|
||||
} else {
|
||||
this->particlesBeached->SetValue(n, std::max(beachedFor-1, 0));
|
||||
this->points->SetPoint(n, point);
|
||||
this->lutIdx->SetTuple1(n, calcIndex(age+1, false));
|
||||
modifiedData = true;
|
||||
}
|
||||
} else {
|
||||
this->lutIdx->SetTuple1(n, calcIndex(age+1, true));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
interactor->AddObserver(vtkCommand::LeftButtonPressEvent, this->callback);
|
||||
interactor->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->callback);
|
||||
|
|
@ -212,13 +184,25 @@ void LGlyphLayer::removeObservers(vtkSmartPointer<vtkRenderWindowInteractor> int
|
|||
}
|
||||
|
||||
|
||||
void LGlyphLayer::cycleGlyphStyle() {
|
||||
auto lut = this->luts.front();
|
||||
this->mapper->SetLookupTable(lut);
|
||||
this->luts.pop();
|
||||
this->luts.push(lut);
|
||||
void LGlyphLayer::setColourMode(ColourMode mode) {
|
||||
|
||||
this->activeColourMode = mode;
|
||||
if (this->activeSaturationMode == DESATURATED) return;
|
||||
|
||||
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) {
|
||||
this->dt = dt;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include "Layer.h"
|
||||
#include "../advection/kernel/AdvectionKernel.h"
|
||||
#include "../commands/SpawnPointCallback.h"
|
||||
#include <queue>
|
||||
#include <vtkPolyData.h>
|
||||
#include <vtkInteractorStyle.h>
|
||||
|
||||
|
|
@ -17,14 +16,21 @@ private:
|
|||
vtkSmartPointer<vtkPolyData> data;
|
||||
vtkSmartPointer<vtkIntArray> particlesBeached;
|
||||
vtkSmartPointer<vtkIntArray> particlesAge;
|
||||
vtkSmartPointer<vtkIntArray> lutIdx;
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper;
|
||||
std::unique_ptr<AdvectionKernel> advector;
|
||||
std::shared_ptr<UVGrid> uvGrid;
|
||||
int dt = 3600;
|
||||
int beachedAtNumberOfTimes = 20;
|
||||
std::queue<vtkSmartPointer<vtkLookupTable>> luts;
|
||||
std::vector<vtkSmartPointer<vtkLookupTable>> tables;
|
||||
ColourMode activeColourMode;
|
||||
SaturationMode activeSaturationMode;
|
||||
vtkSmartPointer<SpawnPointCallback> callback;
|
||||
|
||||
|
||||
void buildLuts();
|
||||
int calcIndex(int age, bool beached);
|
||||
|
||||
public:
|
||||
/** Constructor.
|
||||
*/
|
||||
|
|
@ -46,9 +52,8 @@ public:
|
|||
void addObservers(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 cycleGlyphStyle();
|
||||
void setColourMode(ColourMode mode) override;
|
||||
void setSaturationMode(SaturationMode mode) override;
|
||||
|
||||
/**
|
||||
* Sets a custom DT value, needed for advect calls to the simulation logic.
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ void Layer::setCamera(vtkSmartPointer<vtkCamera> cam) {
|
|||
}
|
||||
|
||||
|
||||
// do nothing by default for these functions.
|
||||
void Layer::updateData(int t) {}
|
||||
void Layer::addObservers(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::setGlyphStyle(GlyphStyle style) {}
|
||||
void Layer::setSamplingMode(SamplingMode mode) {}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ public:
|
|||
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 setGlyphStyle(GlyphStyle style);
|
||||
virtual void setSamplingMode(SamplingMode mode);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
l->setColorMode(mode);
|
||||
l->setColourMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "enums.h"
|
||||
|
||||
class Technique {
|
||||
//TODO: comments
|
||||
private:
|
||||
std::vector<Layer *> layers;
|
||||
vtkSmartPointer<vtkCamera> cam;
|
||||
|
|
@ -20,7 +21,7 @@ public:
|
|||
void bind(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 setGlyphStyle(GlyphStyle style);
|
||||
void setSamplingMode(SamplingMode mode);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define ENUMS_H
|
||||
|
||||
enum ActiveTechnique {
|
||||
NOTECH = -1,
|
||||
COLGLYPH = 0,
|
||||
GLYPHCOL = 1,
|
||||
};
|
||||
|
|
@ -16,7 +17,7 @@ enum ColourMode {
|
|||
|
||||
enum SaturationMode {
|
||||
SATURATED = 0,
|
||||
DESATURATED = 1,
|
||||
DESATURATED = 3,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue