Merge branch 'main' into feat/swap-techniques

This commit is contained in:
Djairo Hougee 2024-05-26 11:47:34 +02:00
commit 8f4dd0afc2
14 changed files with 1075 additions and 41 deletions

View File

@ -5,8 +5,14 @@ project(ParticleTrackTrace)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(VTK COMPONENTS
GeovisCore
CommonColor
@ -48,8 +54,6 @@ add_executable(ParticleTrackTrace MACOSX_BUNDLE main.cpp
layers/Layer.h
layers/LGlyphLayer.cpp
layers/LGlyphLayer.h
layers/LColLayer.cpp
layers/LColLayer.h
Program.cpp
Program.h
commands/TimerCallbackCommand.h
@ -74,6 +78,10 @@ add_executable(ParticleTrackTrace MACOSX_BUNDLE main.cpp
advection/Vel.h
advection/kernel/SnapBoundaryConditionKernel.h
advection/kernel/SnapBoundaryConditionKernel.cpp
QT/MainWindow.h
QT/MainWindow.cpp
QT/MainWindow.ui
QT/ui_mainwindow.h
)
execute_process(
@ -94,7 +102,7 @@ find_library(NETCDF_LIB NAMES netcdf-cxx4 netcdf_c++4 PATHS ${NETCDFCXX_LIB_DIR}
# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
target_link_libraries(ParticleTrackTrace ${NETCDF_LIB} ${VTK_LIBRARIES})
target_link_libraries(ParticleTrackTrace ${NETCDF_LIB} ${VTK_LIBRARIES} Qt5::Widgets Qt5::OpenGL VTK::GUISupportQt VTK::IOLegacy)
# vtk_module_autoinit is needed
vtk_module_autoinit(

View File

@ -14,6 +14,7 @@
#include <netcdf>
#include <vtkArrowSource.h>
#include <vtkNew.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkCallbackCommand.h>
#include <vtkInteractorStyleUser.h>
@ -27,11 +28,11 @@ void Program::setWinProperties() {
this->win->SetSize(661, 661);
this->win->SetDesiredUpdateRate(60);
this->interact->SetRenderWindow(this->win);
this->interact->SetRenderWindow(this->win.Get());
this->interact->Initialize();
vtkNew<vtkInteractorStyleUser> style;
interact->SetInteractorStyle(style);
this->interact->SetInteractorStyle(style);
}
void Program::setupTimer(int dt) {
@ -50,15 +51,17 @@ void Program::setupCameraCallback() {
this->interact->AddObserver(vtkCommand::KeyPressEvent, callback);
}
Program::Program(int timerDT) {
this->win = vtkSmartPointer<vtkRenderWindow>::New();
this->interact = vtkSmartPointer<vtkRenderWindowInteractor>::New();
Program::Program(QWidget *parent): QVTKOpenGLNativeWidget(parent) {
this->win = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
// this->interact = vtkSmartPointer<vtkRenderWindowInteractor>::New();
// this->interact = vtkSmartPointer<QVTKInteractor>::New();
setRenderWindow(this->win);
this->interact = win->GetInteractor();
this->cam = createNormalisedCamera();
this->win->SetNumberOfLayers(0);
setWinProperties();
setupTimer(timerDT);
setupTimer(60 * 60); // FIXME: manually tracking this variable is a little stupid.
setupCameraCallback();
}
@ -91,7 +94,7 @@ void Program::updateData(int t) {
void Program::setupInteractions() {
for (Layer *l: layers) {
l->addObservers(interact);
l->addObservers(this->interact);
}
}
@ -100,3 +103,7 @@ void Program::render() {
win->Render();
interact->Start();
}
Program::~Program() {
cout << "deleting program" << endl;
}

View File

@ -1,6 +1,7 @@
#ifndef PROGRAM_H
#define PROGRAM_H
#include <QVTKOpenGLNativeWidget.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
@ -10,7 +11,9 @@
/** This class manages the upper levels of the vtk pipeline; it has attributes for the vtkrenderWindow and a vector of Layers to represent a variable number of vtkRenderers.
* It can also set up a vtkTimer by connecting an instance of TimerCallbackCommand with its contained vtkRenderWindowInteractor.
*/
class Program {
class Program : public QVTKOpenGLNativeWidget {
Q_OBJECT
private:
/** This attribute models a variable number of vtkRenderers, managed through the abstract Layer class.
*/
@ -18,11 +21,13 @@ private:
/** The window this program's layers render to.
*/
vtkSmartPointer<vtkRenderWindow> win;
// vtkSmartPointer<vtkRenderWindow> win;
vtkSmartPointer<vtkGenericOpenGLRenderWindow> win;
/** The interactor through which the layers can interact with the window.
*/
vtkSmartPointer<vtkRenderWindowInteractor> interact;
// vtkSmartPointer<QVTKInteractor> interact;
/** The camera used by all layers for this program.
@ -37,9 +42,6 @@ private:
*/
void setupTimer(int dt);
/** This function adds all interactors of each layer to the interactor/window
*/
void setupInteractions();
/** This function sets up the camera's associated movement callbacks..
*/
@ -48,7 +50,8 @@ private:
public:
/** Constructor.
*/
Program(int timerDT);
Program(QWidget *parent = nullptr);
~Program() override;
/** This function adds a new layer (and thus vtkRenderer) to the program.
* The layer is expected to set its own position in the vtkRenderWindow layer system.
@ -68,11 +71,16 @@ public:
*/
void updateData(int t);
/** This function adds all interactors of each layer to the interactor/window
*/
void setupInteractions();
/**
* This function renders the vtkRenderWindow for the first time.
* Only call this function once!
*/
void render();
};
#endif

View File

@ -0,0 +1,156 @@
#include "MainWindow.h"
#include "ui_mainwindow.h"
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <vtkDataSetReader.h>
#include "../layers/BackgroundImage.h"
#include "../layers/EColLayer.h"
#include "../layers/EGlyphLayer.h"
#include "../layers/LGlyphLayer.h"
#include "../Program.h"
#include "../advection/UVGrid.h"
#include "../advection/kernel/RK4AdvectionKernel.h"
#include "../advection/kernel/SnapBoundaryConditionKernel.h"
using namespace std;
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
cout << "Reading data..." << endl;
string dataPath = "../../../../data";
shared_ptr<UVGrid> uvGrid = make_shared<UVGrid>(dataPath);
auto kernelRK4 = make_unique<RK4AdvectionKernel>(uvGrid);
auto kernelRK4BoundaryChecked = make_unique<SnapBoundaryConditionKernel>(std::move(kernelRK4), uvGrid);
cout << "Starting vtk..." << endl;
auto l = new LGlyphLayer(uvGrid, std::move(kernelRK4BoundaryChecked));
// l->spoofPoints();
l->setDt(3600);
// TODO: implement feature to call this function on widget
// l->cycleGlyphStyle();
Program *program = this->ui->program;
program->addLayer(new BackgroundImage(dataPath + "/map_qgis_1035.png"));
// TODO: implement feature to cycle between layers thru QT
program->addLayer(new EGlyphLayer(uvGrid));
program->addLayer(new EColLayer(uvGrid));
program->addLayer(l);
program->setupInteractions();
}
MainWindow::~MainWindow() {
delete ui;
}
/* --------------------------------------------------------------------
* + QTWidget callbacks +
* --------------------------------------------------------------------*/
void MainWindow::on_FirstButton_clicked(bool checked) {
if (checked) {
cout << "clicked button 1!" << endl;
}
}
void MainWindow::on_SecondButton_clicked(bool checked) {
if (checked) {
cout << "clicked button 2!" << endl;
}
}
void MainWindow::on_ComplementaryButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_ContrastingButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_MonochromaticButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_SaturateButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_DesaturateButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_CircleButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_TriangleButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_SquareButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_HexagonButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_FullySampledButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_RegularlySubsampledButton_clicked(bool checked) {
if (checked) {
}
}
void MainWindow::on_IregularlySubsampledButton_clicked(bool checked) {
if (checked) {
}}

View File

@ -0,0 +1,37 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = 0);
~MainWindow();
private slots:
void on_FirstButton_clicked(bool checked);
void on_SecondButton_clicked(bool checked);
void on_ComplementaryButton_clicked(bool checked);
void on_ContrastingButton_clicked(bool checked);
void on_MonochromaticButton_clicked(bool checked);
void on_SaturateButton_clicked(bool checked);
void on_DesaturateButton_clicked(bool checked);
void on_CircleButton_clicked(bool checked);
void on_TriangleButton_clicked(bool checked);
void on_SquareButton_clicked(bool checked);
void on_HexagonButton_clicked(bool checked);
void on_FullySampledButton_clicked(bool checked);
void on_RegularlySubsampledButton_clicked(bool checked);
void on_IregularlySubsampledButton_clicked(bool checked);
private:
Ui::MainWindow* ui;
};
#endif

View File

@ -0,0 +1,355 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1048</width>
<height>772</height>
</rect>
</property>
<property name="windowTitle">
<string>Simulation</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="settingsBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>280</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="TechniqueBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>300</height>
</size>
</property>
<property name="title">
<string>Technique</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="FirstButton">
<property name="text">
<string>ECol + LGlyph</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="SecondButton">
<property name="text">
<string>EGlyph + LCol</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="ChannelBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>550</height>
</size>
</property>
<property name="title">
<string>Channel Options</string>
</property>
<widget class="QGroupBox" name="ColourBox">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>300</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>120</height>
</size>
</property>
<property name="title">
<string>Color</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QRadioButton" name="ComplementaryButton">
<property name="text">
<string>Complementary </string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="ContrastingButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Contrasting</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="MonochromaticButton">
<property name="text">
<string>Monochromatic</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="SaturationBox">
<property name="geometry">
<rect>
<x>0</x>
<y>160</y>
<width>269</width>
<height>100</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>260</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>100</height>
</size>
</property>
<property name="title">
<string>Saturation</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QRadioButton" name="SaturateButton">
<property name="text">
<string>Fully saturated</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="DesaturateButton">
<property name="text">
<string>Desaturated</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="GlyphBox">
<property name="geometry">
<rect>
<x>0</x>
<y>270</y>
<width>260</width>
<height>150</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>260</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>150</height>
</size>
</property>
<property name="title">
<string>Glyph Shape</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<widget class="QRadioButton" name="radioButton_10">
<property name="text">
<string>Circle</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_11">
<property name="text">
<string>Triangle</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_12">
<property name="text">
<string>Square</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>Hexagon</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="GlyphBox_2">
<property name="geometry">
<rect>
<x>0</x>
<y>430</y>
<width>260</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>260</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>120</height>
</size>
</property>
<property name="title">
<string>Glyph count</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<widget class="QRadioButton" name="radioButton_13">
<property name="text">
<string>Fully sampled</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_14">
<property name="text">
<string>Regularly subsampled</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_15">
<property name="text">
<string>Irregularly sampled</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Program" name="program"/>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>MainView</class>
<extends>QVTKOpenGLNativeWidget</extends>
<header location="global">Program.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,278 @@
/********************************************************************************
** Form generated from reading UI file 'MainWindow.ui'
**
** Created by: Qt User Interface Compiler version 5.15.13
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H
#include "../Program.h"
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QRadioButton>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralWidget;
QHBoxLayout *horizontalLayout;
QGroupBox *settingsBox;
QVBoxLayout *verticalLayout_3;
QGroupBox *TechniqueBox;
QVBoxLayout *verticalLayout_2;
QRadioButton *FirstButton;
QRadioButton *SecondButton;
QGroupBox *ChannelBox;
QGroupBox *ColourBox;
QVBoxLayout *verticalLayout_12;
QRadioButton *ComplementaryButton;
QRadioButton *ContrastingButton;
QRadioButton *MonochromaticButton;
QGroupBox *SaturationBox;
QVBoxLayout *verticalLayout_13;
QRadioButton *SaturateButton;
QRadioButton *DesaturateButton;
QGroupBox *GlyphBox;
QVBoxLayout *verticalLayout_14;
QRadioButton *CircleButton;
QRadioButton *TriangleButton;
QRadioButton *SquareButton;
QRadioButton *HexagonButton;
QGroupBox *GlyphBox_2;
QVBoxLayout *verticalLayout_16;
QRadioButton *FullySampledButton;
QRadioButton *RegularlySubsampledButton;
QRadioButton *IregularlySubsampledButton;
Program *program;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(1048, 772);
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
horizontalLayout = new QHBoxLayout(centralWidget);
horizontalLayout->setSpacing(6);
horizontalLayout->setContentsMargins(11, 11, 11, 11);
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
settingsBox = new QGroupBox(centralWidget);
settingsBox->setObjectName(QString::fromUtf8("settingsBox"));
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(settingsBox->sizePolicy().hasHeightForWidth());
settingsBox->setSizePolicy(sizePolicy);
settingsBox->setMinimumSize(QSize(220, 0));
settingsBox->setMaximumSize(QSize(280, 16777215));
verticalLayout_3 = new QVBoxLayout(settingsBox);
verticalLayout_3->setSpacing(6);
verticalLayout_3->setContentsMargins(11, 11, 11, 11);
verticalLayout_3->setObjectName(QString::fromUtf8("verticalLayout_3"));
TechniqueBox = new QGroupBox(settingsBox);
TechniqueBox->setObjectName(QString::fromUtf8("TechniqueBox"));
sizePolicy.setHeightForWidth(TechniqueBox->sizePolicy().hasHeightForWidth());
TechniqueBox->setSizePolicy(sizePolicy);
TechniqueBox->setMinimumSize(QSize(0, 0));
TechniqueBox->setMaximumSize(QSize(500, 100));
verticalLayout_2 = new QVBoxLayout(TechniqueBox);
verticalLayout_2->setSpacing(6);
verticalLayout_2->setContentsMargins(11, 11, 11, 11);
verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
FirstButton = new QRadioButton(TechniqueBox);
FirstButton->setObjectName(QString::fromUtf8("FirstButton"));
FirstButton->setChecked(true);
verticalLayout_2->addWidget(FirstButton);
SecondButton = new QRadioButton(TechniqueBox);
SecondButton->setObjectName(QString::fromUtf8("SecondButton"));
SecondButton->setChecked(false);
verticalLayout_2->addWidget(SecondButton);
verticalLayout_3->addWidget(TechniqueBox);
ChannelBox = new QGroupBox(settingsBox);
ChannelBox->setObjectName(QString::fromUtf8("ChannelBox"));
ChannelBox->setMinimumSize(QSize(0, 550));
ColourBox = new QGroupBox(ChannelBox);
ColourBox->setObjectName(QString::fromUtf8("ColourBox"));
ColourBox->setGeometry(QRect(0, 30, 300, 120));
sizePolicy.setHeightForWidth(ColourBox->sizePolicy().hasHeightForWidth());
ColourBox->setSizePolicy(sizePolicy);
ColourBox->setMinimumSize(QSize(250, 0));
ColourBox->setMaximumSize(QSize(500, 120));
ColourBox->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter);
verticalLayout_12 = new QVBoxLayout(ColourBox);
verticalLayout_12->setSpacing(6);
verticalLayout_12->setContentsMargins(11, 11, 11, 11);
verticalLayout_12->setObjectName(QString::fromUtf8("verticalLayout_12"));
ComplementaryButton = new QRadioButton(ColourBox);
ComplementaryButton->setObjectName(QString::fromUtf8("ComplementaryButton"));
verticalLayout_12->addWidget(ComplementaryButton);
ContrastingButton = new QRadioButton(ColourBox);
ContrastingButton->setObjectName(QString::fromUtf8("ContrastingButton"));
QSizePolicy sizePolicy1(QSizePolicy::Maximum, QSizePolicy::Fixed);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(ContrastingButton->sizePolicy().hasHeightForWidth());
ContrastingButton->setSizePolicy(sizePolicy1);
verticalLayout_12->addWidget(ContrastingButton);
MonochromaticButton = new QRadioButton(ColourBox);
MonochromaticButton->setObjectName(QString::fromUtf8("MonochromaticButton"));
verticalLayout_12->addWidget(MonochromaticButton);
SaturationBox = new QGroupBox(ChannelBox);
SaturationBox->setObjectName(QString::fromUtf8("SaturationBox"));
SaturationBox->setGeometry(QRect(0, 160, 269, 100));
sizePolicy.setHeightForWidth(SaturationBox->sizePolicy().hasHeightForWidth());
SaturationBox->setSizePolicy(sizePolicy);
SaturationBox->setMinimumSize(QSize(260, 0));
SaturationBox->setMaximumSize(QSize(500, 100));
SaturationBox->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter);
verticalLayout_13 = new QVBoxLayout(SaturationBox);
verticalLayout_13->setSpacing(6);
verticalLayout_13->setContentsMargins(11, 11, 11, 11);
verticalLayout_13->setObjectName(QString::fromUtf8("verticalLayout_13"));
SaturateButton = new QRadioButton(SaturationBox);
SaturateButton->setObjectName(QString::fromUtf8("SaturateButton"));
verticalLayout_13->addWidget(SaturateButton);
DesaturateButton = new QRadioButton(SaturationBox);
DesaturateButton->setObjectName(QString::fromUtf8("DesaturateButton"));
verticalLayout_13->addWidget(DesaturateButton);
GlyphBox = new QGroupBox(ChannelBox);
GlyphBox->setObjectName(QString::fromUtf8("GlyphBox"));
GlyphBox->setGeometry(QRect(0, 270, 260, 150));
sizePolicy.setHeightForWidth(GlyphBox->sizePolicy().hasHeightForWidth());
GlyphBox->setSizePolicy(sizePolicy);
GlyphBox->setMinimumSize(QSize(260, 0));
GlyphBox->setMaximumSize(QSize(500, 150));
GlyphBox->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter);
verticalLayout_14 = new QVBoxLayout(GlyphBox);
verticalLayout_14->setSpacing(6);
verticalLayout_14->setContentsMargins(11, 11, 11, 11);
verticalLayout_14->setObjectName(QString::fromUtf8("verticalLayout_14"));
CircleButton = new QRadioButton(GlyphBox);
CircleButton->setObjectName(QString::fromUtf8("CircleButton"));
verticalLayout_14->addWidget(CircleButton);
TriangleButton = new QRadioButton(GlyphBox);
TriangleButton->setObjectName(QString::fromUtf8("TriangleButton"));
verticalLayout_14->addWidget(TriangleButton);
SquareButton = new QRadioButton(GlyphBox);
SquareButton->setObjectName(QString::fromUtf8("SquareButton"));
verticalLayout_14->addWidget(SquareButton);
HexagonButton = new QRadioButton(GlyphBox);
HexagonButton->setObjectName(QString::fromUtf8("HexagonButton"));
verticalLayout_14->addWidget(HexagonButton);
GlyphBox_2 = new QGroupBox(ChannelBox);
GlyphBox_2->setObjectName(QString::fromUtf8("GlyphBox_2"));
GlyphBox_2->setGeometry(QRect(0, 430, 260, 120));
QSizePolicy sizePolicy2(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy2.setHorizontalStretch(0);
sizePolicy2.setVerticalStretch(0);
sizePolicy2.setHeightForWidth(GlyphBox_2->sizePolicy().hasHeightForWidth());
GlyphBox_2->setSizePolicy(sizePolicy2);
GlyphBox_2->setMinimumSize(QSize(260, 0));
GlyphBox_2->setMaximumSize(QSize(500, 120));
GlyphBox_2->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter);
verticalLayout_16 = new QVBoxLayout(GlyphBox_2);
verticalLayout_16->setSpacing(6);
verticalLayout_16->setContentsMargins(11, 11, 11, 11);
verticalLayout_16->setObjectName(QString::fromUtf8("verticalLayout_16"));
FullySampledButton = new QRadioButton(GlyphBox_2);
FullySampledButton->setObjectName(QString::fromUtf8("FullySampledButton"));
verticalLayout_16->addWidget(FullySampledButton);
RegularlySubsampledButton = new QRadioButton(GlyphBox_2);
RegularlySubsampledButton->setObjectName(QString::fromUtf8("RegularlySubsampledButton"));
verticalLayout_16->addWidget(RegularlySubsampledButton);
IregularlySubsampledButton = new QRadioButton(GlyphBox_2);
IregularlySubsampledButton->setObjectName(QString::fromUtf8("IregularlySubsampledButton"));
verticalLayout_16->addWidget(IregularlySubsampledButton);
verticalLayout_3->addWidget(ChannelBox);
horizontalLayout->addWidget(settingsBox);
program = new Program(centralWidget);
program->setObjectName(QString::fromUtf8("program"));
horizontalLayout->addWidget(program);
MainWindow->setCentralWidget(centralWidget);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "Simulation", nullptr));
settingsBox->setTitle(QCoreApplication::translate("MainWindow", "Settings", nullptr));
TechniqueBox->setTitle(QCoreApplication::translate("MainWindow", "Technique", nullptr));
FirstButton->setText(QCoreApplication::translate("MainWindow", "ECol + LGlyph", nullptr));
SecondButton->setText(QCoreApplication::translate("MainWindow", "EGlyph + LCol", nullptr));
ChannelBox->setTitle(QCoreApplication::translate("MainWindow", "Channel Options", nullptr));
ColourBox->setTitle(QCoreApplication::translate("MainWindow", "Color", nullptr));
ComplementaryButton->setText(QCoreApplication::translate("MainWindow", "Complementary ", nullptr));
ContrastingButton->setText(QCoreApplication::translate("MainWindow", "Contrasting", nullptr));
MonochromaticButton->setText(QCoreApplication::translate("MainWindow", "Monochromatic", nullptr));
SaturationBox->setTitle(QCoreApplication::translate("MainWindow", "Saturation", nullptr));
SaturateButton->setText(QCoreApplication::translate("MainWindow", "Fully saturated", nullptr));
DesaturateButton->setText(QCoreApplication::translate("MainWindow", "Desaturated", nullptr));
GlyphBox->setTitle(QCoreApplication::translate("MainWindow", "Glyph Shape", nullptr));
CircleButton->setText(QCoreApplication::translate("MainWindow", "Circle", nullptr));
TriangleButton->setText(QCoreApplication::translate("MainWindow", "Triangle", nullptr));
SquareButton->setText(QCoreApplication::translate("MainWindow", "Square", nullptr));
HexagonButton->setText(QCoreApplication::translate("MainWindow", "Hexagon", nullptr));
GlyphBox_2->setTitle(QCoreApplication::translate("MainWindow", "Glyph count", nullptr));
FullySampledButton->setText(QCoreApplication::translate("MainWindow", "Fully sampled", nullptr));
RegularlySubsampledButton->setText(QCoreApplication::translate("MainWindow", "Regularly subsampled", nullptr));
IregularlySubsampledButton->setText(QCoreApplication::translate("MainWindow", "Irregularly sampled", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_MAINWINDOW_H

View File

@ -2,6 +2,7 @@
#include <vtkGeneralTransform.h>
#include <vtkGeoProjection.h>
#include <vtkGeoTransform.h>
#include <vtkLookupTable.h>
#include <vtkPointData.h>
#include <vtkAbstractTransform.h>
#include <vtkOutlineSource.h>
@ -24,8 +25,9 @@
#include "../CartographicTransformation.h"
using namespace std;
using std::numbers::pi;
using namespace std;
EColLayer::EColLayer(std::shared_ptr<UVGrid> uvGrid) {
this->ren = vtkSmartPointer<vtkRenderer>::New();
@ -41,9 +43,53 @@ EColLayer::EColLayer(std::shared_ptr<UVGrid> uvGrid) {
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));
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);
}
}
// 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);
lut->SetNanColor(0,0,0,0);
return lut;
}
// TODO: Bit of a superfunction here; can do with some refactoring.
void EColLayer::readCoordinates() {
@ -64,27 +110,26 @@ void EColLayer::readCoordinates() {
for (auto lat : uvGrid->lats) {
double out[3] = {lon, lat, 0};
transform->TransformPoint(out, out);
// cout << "inserting point (" << pointId << "): " << out[0] << " " << out[1] << endl;
points->InsertPoint(pointId++, out[0], out[1], 0);
// logic for adding cells
if (latIndex > 0 and lonIndex > 0 ) {
int idx = latIndex+lonIndex*numLats;
// cout << idx << " at " << lonIndex << " " << latIndex << endl;
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);
// cout << "inserting cell: " << idx-1 << " " << idx-numLats-1 << " " << idx- numLats<< " " << idx << endl;
double coords[12];
points->GetPoint(idx-1, coords);
points->GetPoint(idx-numLats-1, coords+3);
points->GetPoint(idx-numLats, coords+6);
points->GetPoint(idx, coords+9);
// cout << "Inserting cell with points at: (" << coords[0] << " " << coords[1] << "), (" << coords[3] << " " << coords[4] << "), (" << coords[6] << " " << coords[7] << "), (" << coords[9] << " " << coords[10] << ")" << endl;
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++ ) {
@ -95,7 +140,8 @@ void EColLayer::readCoordinates() {
}
u /= 4;
v /= 4;
this->strength->SetTuple1(cellId++, std::sqrt(u*u + v*v));
this->strength->SetTuple1(cellId, std::sqrt(u*u + v*v));
this->direction->SetTuple1(cellId++, atan(u/v)*180/pi);
}
latIndex++;
}
@ -103,16 +149,20 @@ void EColLayer::readCoordinates() {
}
data->GetCellData()->AddArray(this->strength);
data->GetCellData()->SetActiveScalars("strength");
data->GetCellData()->AddArray(this->direction);
// data->GetCellData()->SetActiveScalars("strength");
vtkNew<vtkPolyDataMapper>(mapper);
mapper->SetInputData(data);
mapper->SetLookupTable(buildLutDirs());
mapper->UseLookupTableScalarRangeOn();
mapper->Update();
data->GetCellData()->SetActiveScalars("direction");
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(0, 1, 0);
actor->GetProperty()->SetOpacity(0.2);
actor->GetProperty()->SetOpacity(0.5);
// vtkNew<vtkActor> act2;
// act2->SetMapper(mapper);
@ -137,7 +187,8 @@ void EColLayer::updateData(int t) {
}
u /= 4;
v /= 4;
this->strength->SetTuple1(i++, std::sqrt(u*u + v*v));
this->strength->SetTuple1(i, std::sqrt(u*u + v*v));
this->direction->SetTuple1(i++, atan(u/v)*180/pi);
}
}
this->strength->Modified();

View File

@ -15,6 +15,7 @@
class EColLayer : public Layer {
private:
vtkSmartPointer<vtkDoubleArray> strength;
vtkSmartPointer<vtkDoubleArray> direction;
std::shared_ptr<UVGrid> uvGrid;
int numLats;
int numLons;

View File

@ -33,12 +33,36 @@ vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
// 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> buildLut(int n) {
vtkSmartPointer<vtkLookupTable> buildLutOpacity(int n) {
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(n);
lut->SetTableRange(0, n);
@ -58,6 +82,9 @@ vtkSmartPointer<vtkLookupTable> buildLut(int n) {
}
LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<AdvectionKernel> advectionKernel) {
this->luts.push(buildLutOpacity(512));
this->luts.push(buildLutBrightness(512));
this->ren = vtkSmartPointer<vtkRenderer>::New();
this->ren->SetLayer(2);
@ -94,15 +121,20 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
glyph2D->SetScaleModeToDataScalingOff();
glyph2D->Update();
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(glyph2D->GetOutputPort());
mapper->SetColorModeToMapScalars();
mapper->SetLookupTable(buildLut(512));
mapper->UseLookupTableScalarRangeOn();
mapper->Update();
this->mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
this->mapper->SetInputConnection(glyph2D->GetOutputPort());
this->mapper->SetColorModeToMapScalars();
auto lut = this->luts.front();
mapper->SetLookupTable(lut);
this->luts.pop();
this->luts.push(lut);
this->mapper->UseLookupTableScalarRangeOn();
this->mapper->Update();
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->SetMapper(this->mapper);
this->ren->AddActor(actor);
}
@ -174,6 +206,13 @@ void LGlyphLayer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> intera
}
void LGlyphLayer::cycleGlyphStyle() {
auto lut = this->luts.front();
this->mapper->SetLookupTable(lut);
this->luts.pop();
this->luts.push(lut);
}
void LGlyphLayer::setDt(int dt) {
this->dt = dt;
}

View File

@ -4,6 +4,7 @@
#include "Layer.h"
#include "../advection/kernel/AdvectionKernel.h"
#include "../commands/SpawnPointCallback.h"
#include <queue>
#include <vtkPolyData.h>
#include <vtkInteractorStyle.h>
@ -16,10 +17,12 @@ private:
vtkSmartPointer<vtkPolyData> data;
vtkSmartPointer<vtkIntArray> particlesBeached;
vtkSmartPointer<vtkIntArray> particlesAge;
vtkSmartPointer<vtkPolyDataMapper> mapper;
std::unique_ptr<AdvectionKernel> advector;
std::shared_ptr<UVGrid> uvGrid;
int dt = 3600;
int beachedAtNumberOfTimes = 20;
std::queue<vtkSmartPointer<vtkLookupTable>> luts;
public:
/** Constructor.
@ -41,6 +44,9 @@ public:
void addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) override;
/** This function cycles which lut is used for the layer, according to the lookuptables in the luts attribute.
*/
void cycleGlyphStyle();
/**
* Sets a custom DT value, needed for advect calls to the simulation logic.

View File

@ -18,11 +18,60 @@
#include "advection/kernel/RK4AdvectionKernel.h"
#include "advection/kernel/SnapBoundaryConditionKernel.h"
#include <QApplication>
#include <QVTKOpenGLNativeWidget.h>
#include <QDockWidget>
#include <QGridLayout>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QLabel>
#include <QMainWindow>
#include <QPointer>
#include <QPushButton>
#include <QVBoxLayout>
#include "QT/MainWindow.h"
using namespace std;
#define DT 60 * 60 // 60 sec/min * 60 mins
int main() {
int main(int argc, char* argv[]) {
QSurfaceFormat::setDefaultFormat(QVTKOpenGLNativeWidget::defaultFormat());
QApplication app(argc, argv);
// Main window.
MainWindow mainWindow;
mainWindow.resize(1200, 900);
mainWindow.show();
return app.exec();
// Control area.
// QDockWidget controlDock;
// mainWindow.addDockWidget(Qt::LeftDockWidgetArea, &controlDock);
//
// QLabel controlDockTitle("Control Dock");
// controlDockTitle.setMargin(20);
// controlDock.setTitleBarWidget(&controlDockTitle);
//
// QPointer<QVBoxLayout> dockLayout = new QVBoxLayout();
// QWidget layoutContainer;
// layoutContainer.setLayout(dockLayout);
// controlDock.setWidget(&layoutContainer);
//
// QPushButton randomizeButton;
// randomizeButton.setText("Randomize");
// dockLayout->addWidget(&randomizeButton);
// Render area.
// QPointer<QVTKOpenGLNativeWidget> vtkRenderWidget =
// new QVTKOpenGLNativeWidget();
// mainWindow.setCentralWidget(vtkRenderWidget);
// VTK part.
// vtkNew<vtkGenericOpenGLRenderWindow> window;
// vtkRenderWidget->setRenderWindow(window.Get());
cout << "Reading data..." << endl;
string dataPath = "../../../../data";
shared_ptr<UVGrid> uvGrid = make_shared<UVGrid>(dataPath);
@ -31,17 +80,31 @@ int main() {
cout << "Starting vtk..." << endl;
auto l = new LGlyphLayer(uvGrid, std::move(kernelRK4BoundaryChecked));
l->spoofPoints();
// l->spoofPoints();
l->setDt(DT);
// TODO: implement feature to call this function on widget
// l->cycleGlyphStyle();
unique_ptr<Program> program = make_unique<Program>(DT);
program->addLayer(new BackgroundImage(dataPath + "/map_661-661.png"));
// program->addLayer(new EGlyphLayer(uvGrid));
unique_ptr<Program> program = make_unique<Program>();
program->addLayer(new BackgroundImage(dataPath + "/map_qgis_1035.png"));
// TODO: implement feature to cycle between layers thru QT
program->addLayer(new EGlyphLayer(uvGrid));
program->addLayer(new EColLayer(uvGrid));
program->addLayer(l);
program->render();
program->setupInteractions();
return EXIT_SUCCESS;
// program->render();
// Setup initial status.
// std::mt19937 randEng(0);
// ::Randomize(sphere, mapper, window, randEng);
// connect the buttons
// QObject::connect(&randomizeButton, &QPushButton::released,
// [&]() { ::Randomize(sphere, mapper, window, randEng); });
mainWindow.show();
return app.exec();
}

View File

@ -0,0 +1,25 @@
#ifndef TECHNIQUE_H
#define TECHNIQUE_H
#include "layers/Layer.h"
#include <vtkPolyData.h>
class Technique {
private:
std::vector<Layer *> layers;
vtkSmartPointer<vtkPoints> points;
vtkSmartPointer<vtkPolyData> data;
void setupInteractions();
public:
Technique();
void addLayer(Layer *l);
void removeLayer(Layer *l);
void updateData(int t);
int numberOfLayers();
};
#endif