diff --git a/Applications/DataExplorer/DataView/CMakeLists.txt b/Applications/DataExplorer/DataView/CMakeLists.txt index c4b8170b5becc3b3feffa598536e0fd66de27ef7..61bec31972b076d533a13d793718b2510e0e1ef0 100644 --- a/Applications/DataExplorer/DataView/CMakeLists.txt +++ b/Applications/DataExplorer/DataView/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCES GeoTreeModel.cpp GeoTreeView.cpp GMSHPrefsDialog.cpp + Layers2GridDialog.cpp LicenseDialog.cpp LinearEditDialog.cpp LineEditDialog.cpp @@ -68,6 +69,7 @@ set(HEADERS GeoTreeModel.h GeoTreeView.h GMSHPrefsDialog.h + Layers2GridDialog.h LicenseDialog.h LinearEditDialog.h LineEditDialog.h diff --git a/Applications/DataExplorer/DataView/Layers2Grid.ui b/Applications/DataExplorer/DataView/Layers2Grid.ui new file mode 100644 index 0000000000000000000000000000000000000000..b412786552b584f85e3fd2aed200d4832445a64e --- /dev/null +++ b/Applications/DataExplorer/DataView/Layers2Grid.ui @@ -0,0 +1,251 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Layers2Grid</class> + <widget class="QDialog" name="Layers2Grid"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>357</width> + <height>471</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="geometry"> + <rect> + <x>170</x> + <y>430</y> + <width>171</width> + <height>32</height> + </rect> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + <widget class="QListView" name="allMeshView"> + <property name="geometry"> + <rect> + <x>60</x> + <y>60</y> + <width>215</width> + <height>216</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="allMeshLabel"> + <property name="geometry"> + <rect> + <x>30</x> + <y>20</y> + <width>291</width> + <height>41</height> + </rect> + </property> + <property name="text"> + <string><html><head/><body><p align="center"><span style=" font-weight:600;">Meshes for voxel:</span><br/><span style=" font-style:italic;">WARNING: Meshes must be ordered top to bottom</span></p></body></html></string> + </property> + </widget> + <widget class="QGroupBox" name="VoxelSizeBox"> + <property name="geometry"> + <rect> + <x>40</x> + <y>330</y> + <width>281</width> + <height>69</height> + </rect> + </property> + <property name="title"> + <string>Voxel size</string> + </property> + <widget class="QLabel" name="xLengthLabel"> + <property name="geometry"> + <rect> + <x>12</x> + <y>32</y> + <width>16</width> + <height>17</height> + </rect> + </property> + <property name="text"> + <string>x:</string> + </property> + </widget> + <widget class="QLineEdit" name="xlineEdit"> + <property name="geometry"> + <rect> + <x>31</x> + <y>32</y> + <width>51</width> + <height>25</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="yLengthLabel"> + <property name="geometry"> + <rect> + <x>100</x> + <y>32</y> + <width>16</width> + <height>17</height> + </rect> + </property> + <property name="text"> + <string>y:</string> + </property> + </widget> + <widget class="QLineEdit" name="ylineEdit"> + <property name="geometry"> + <rect> + <x>120</x> + <y>32</y> + <width>51</width> + <height>25</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="zLengthLabel"> + <property name="geometry"> + <rect> + <x>190</x> + <y>32</y> + <width>16</width> + <height>17</height> + </rect> + </property> + <property name="text"> + <string>z:</string> + </property> + </widget> + <widget class="QLineEdit" name="zlineEdit"> + <property name="geometry"> + <rect> + <x>210</x> + <y>32</y> + <width>51</width> + <height>25</height> + </rect> + </property> + </widget> + </widget> + <widget class="QPushButton" name="upOrderButton"> + <property name="geometry"> + <rect> + <x>290</x> + <y>140</y> + <width>31</width> + <height>25</height> + </rect> + </property> + <property name="text"> + <string>↑</string> + </property> + </widget> + <widget class="QPushButton" name="downOrderButton"> + <property name="geometry"> + <rect> + <x>290</x> + <y>170</y> + <width>31</width> + <height>25</height> + </rect> + </property> + <property name="text"> + <string>↓</string> + </property> + </widget> + <widget class="QPushButton" name="orderButton"> + <property name="geometry"> + <rect> + <x>30</x> + <y>290</y> + <width>131</width> + <height>31</height> + </rect> + </property> + <property name="text"> + <string>order Meshes</string> + </property> + </widget> + <widget class="QPushButton" name="deleteMeshButton"> + <property name="geometry"> + <rect> + <x>190</x> + <y>290</y> + <width>151</width> + <height>31</height> + </rect> + </property> + <property name="text"> + <string>delete selected mesh</string> + </property> + </widget> + <widget class="QCheckBox" name="dilateBox"> + <property name="geometry"> + <rect> + <x>40</x> + <y>430</y> + <width>71</width> + <height>31</height> + </rect> + </property> + <property name="text"> + <string>dilate</string> + </property> + </widget> + <widget class="QLabel" name="expectedVoxelLabel"> + <property name="geometry"> + <rect> + <x>21</x> + <y>400</y> + <width>291</width> + <height>21</height> + </rect> + </property> + <property name="text"> + <string>Expected Voxel: </string> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Layers2Grid</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Layers2Grid</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Applications/DataExplorer/DataView/Layers2GridDialog.cpp b/Applications/DataExplorer/DataView/Layers2GridDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..92e041be1e6f3aefcbb4ed76c038015e685d688a --- /dev/null +++ b/Applications/DataExplorer/DataView/Layers2GridDialog.cpp @@ -0,0 +1,232 @@ +/** + * \file + * \date 2023-04-26 + * \brief Implementation of the Layers2GridDialog class. + * + * \copyright + * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#include "Layers2GridDialog.h" + +#include <QStringList> +#include <QStringListModel> + +#include "Base/StrictDoubleValidator.h" +#include "GeoLib/AABB.h" +#include "MeshLib/IO/writeMeshToFile.h" +#include "MeshLib/Mesh.h" +#include "MeshLib/Node.h" +#include "MeshModel.h" +#include "MeshToolsLib/MeshGenerators/VoxelGridFromLayeredMeshes.h" + +namespace +{ +std::vector<std::string> getSelectedObjects(QStringList const& list) +{ + std::vector<std::string> indexList; + std::transform(list.begin(), list.end(), std::back_inserter(indexList), + [](auto const& index) { return index.toStdString(); }); + return indexList; +} +} // namespace + +Layers2GridDialog::Layers2GridDialog(MeshModel* mesh_model, QDialog* parent) + : QDialog(parent), _mesh_model(mesh_model) +{ + setupUi(this); + assert(_mesh_model != nullptr); + QStringList MeshList; + + for (int model_index = 0; model_index < mesh_model->rowCount(); + ++model_index) + { + auto const* mesh = + mesh_model->getMesh(mesh_model->index(model_index, 0)); + MeshList.append(QString::fromStdString(mesh->getName())); + } + + if (MeshList.empty()) + { + MeshList.append("[No Mesh available.]"); + this->expectedVoxelLabel->setText("Expected Voxel: undefined"); + } + + _layeredMeshes.setStringList(MeshList); + this->allMeshView->setModel(&_layeredMeshes); + this->allMeshView->setDragDropMode(QAbstractItemView::InternalMove); +} + +void Layers2GridDialog::on_deleteMeshButton_pressed() +{ + QModelIndex const selected = + this->allMeshView->selectionModel()->currentIndex(); + _layeredMeshes.removeRow(selected.row()); + QStringList list = _layeredMeshes.stringList(); + _layeredMeshes.setStringList(list); +} + +void Layers2GridDialog::on_orderButton_pressed() +{ + _layeredMeshes.sort(0); +} + +void Layers2GridDialog::on_upOrderButton_pressed() +{ + QModelIndex selected = this->allMeshView->selectionModel()->currentIndex(); + QStringList list = _layeredMeshes.stringList(); + int row = selected.row(); + if (row > 0) + { + QString list_item = list[row - 1]; + list[row - 1] = selected.data().toString(); + list[row] = list_item; + } + _layeredMeshes.setStringList(list); + this->allMeshView->selectionModel()->setCurrentIndex( + _layeredMeshes.index(row - 1), QItemSelectionModel::SelectCurrent); +} + +void Layers2GridDialog::on_downOrderButton_pressed() +{ + QModelIndex selected = this->allMeshView->selectionModel()->currentIndex(); + QStringList list = _layeredMeshes.stringList(); + int row = selected.row(); + if (row < list.size() - 1 && row != -1) + { + QString list_item = list[row + 1]; + list[row + 1] = selected.data().toString(); + list[row] = list_item; + } + _layeredMeshes.setStringList(list); + this->allMeshView->selectionModel()->setCurrentIndex( + _layeredMeshes.index(row + 1), QItemSelectionModel::SelectCurrent); +} + +void Layers2GridDialog::updateExpectedVoxel() +{ + QString const xinput = this->xlineEdit->text(); + QString const yinput = this->ylineEdit->text(); + QString const zinput = this->zlineEdit->text(); + + if (_layeredMeshes.stringList()[0] == "[No Mesh available.]") + { + this->expectedVoxelLabel->setText("approximated Voxel: undefined"); + return; + } + if (xinput.isEmpty() || yinput.isEmpty() || zinput.isEmpty() || + xinput.toDouble() == 0 || yinput.toDouble() == 0 || + zinput.toDouble() == 0) + { + this->expectedVoxelLabel->setText("approximated Voxel: undefined"); + return; + } + + std::vector<std::string> layered_meshes = + getSelectedObjects(_layeredMeshes.stringList()); + auto* const mesh_top = _mesh_model->getMesh(layered_meshes.front()); + auto* const mesh_bottom = _mesh_model->getMesh(layered_meshes.back()); + auto const& nodes_top = mesh_top->getNodes(); + + GeoLib::AABB const aabb_top(nodes_top.cbegin(), nodes_top.cend()); + auto const& nodes_bottom = mesh_bottom->getNodes(); + GeoLib::AABB const aabb_bottom(nodes_bottom.cbegin(), nodes_bottom.cend()); + auto const min_b = aabb_bottom.getMinPoint(); + auto const max_b = aabb_bottom.getMaxPoint(); + auto const max_t = aabb_top.getMaxPoint(); + double const expectedVoxel = (max_b[0] - min_b[0]) * (max_b[1] - min_b[0]) * + (max_t[2] - min_b[2]) / xinput.toDouble() / + yinput.toDouble() / zinput.toDouble(); + + int const exponent = std::floor(std::log10(abs(expectedVoxel))); + this->expectedVoxelLabel->setText( + "approximated Voxel = " + + QString::number(std::round(expectedVoxel / std::pow(10, exponent))) + + " x 10^" + QString::number(exponent)); +} + +void Layers2GridDialog::on_xlineEdit_textChanged() +{ + updateExpectedVoxel(); +} + +void Layers2GridDialog::on_ylineEdit_textChanged() +{ + updateExpectedVoxel(); +} + +void Layers2GridDialog::on_zlineEdit_textChanged() +{ + updateExpectedVoxel(); +} + +void Layers2GridDialog::accept() +{ + if (this->_layeredMeshes.rowCount() == 1) + { + OGSError::box( + "Please specify the input meshes. \n At least two layers are " + "required to create a 3D Mesh"); + return; + } + + QString const xin = this->xlineEdit->text(); + QString const yin = this->ylineEdit->text(); + QString const zin = this->zlineEdit->text(); + + bool ok; + if (!xin.toDouble(&ok)) + { + OGSError::box( + "At least the x-length of a voxel must be specified.\n If " + "y-/z-input " + "are not specified, equal to 0, or not a real number, they are " + "treated as " + "the x-input."); + return; + } + double const xinput = xin.toDouble(); + double const yinput = (yin.toDouble(&ok)) ? yin.toDouble() : xin.toDouble(); + double const zinput = (zin.toDouble(&ok)) ? zin.toDouble() : xin.toDouble(); + + std::vector<std::string> layered_meshes = + getSelectedObjects(_layeredMeshes.stringList()); + + std::vector<const MeshLib::Mesh*> layers; + layers.reserve(layered_meshes.size()); + + for (auto const& layer : layered_meshes) + { + auto mesh(_mesh_model->getMesh(layer)); + if (mesh == nullptr) + { + OGSError::box( + "Input layer not found. Aborting..."); // write name of layer. + return; + } + layers.push_back(mesh); + } + + bool const dilate = this->dilateBox->isChecked(); + std::array<double, 3> const cellsize = {xinput, yinput, zinput}; + constexpr double minval = std::numeric_limits<double>::max(); + constexpr double maxval = std::numeric_limits<double>::lowest(); + std::pair<MathLib::Point3d, MathLib::Point3d> extent( + MathLib::Point3d{{minval, minval, minval}}, + MathLib::Point3d{{maxval, maxval, maxval}}); + auto mesh(MeshToolsLib::MeshGenerators::VoxelFromLayeredMeshes:: + createVoxelFromLayeredMesh(extent, layers, cellsize, dilate)); + + if (mesh == nullptr) + { + OGSError::box("The VoxelGrid is faulty"); + return; + } + OGSError::box("The VoxelGrid is fine"); + + _mesh_model->addMesh(mesh.release()); + this->done(QDialog::Accepted); +} diff --git a/Applications/DataExplorer/DataView/Layers2GridDialog.h b/Applications/DataExplorer/DataView/Layers2GridDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..14f4f245af37071c0d7d9b0329fb940872137869 --- /dev/null +++ b/Applications/DataExplorer/DataView/Layers2GridDialog.h @@ -0,0 +1,60 @@ +/** + * \file + * \date 2023-04-26 + * \brief Definition of the Layers2GridDialog class. + * + * \copyright + * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#pragma once + +#include <QDialog> +#include <QStringListModel> +#include <string> +#include <vector> + +#include "MeshLib/Elements/ElementErrorCode.h" +#include "ui_Layers2Grid.h" + +class MeshModel; + +/* + * \brief A dialog window for calling methods to create a 3D Voxelgrid from + * multiple 2D vtu meshes + */ +class Layers2GridDialog : public QDialog, private Ui_Layers2Grid +{ + Q_OBJECT + +public: + explicit Layers2GridDialog(MeshModel* mesh_model, + QDialog* parent = nullptr); + +private: + MeshModel* _mesh_model; + QStringListModel _layeredMeshes; + QStringListModel _neglectedMeshes; + +private slots: + /// Instructions if the OK-Button has been pressed. + void accept() override; + /// Instructions if the Cancel-Button has been pressed. + void reject() override { this->done(QDialog::Rejected); }; + /// Instructions if the ">>" button has been pressed. + void on_deleteMeshButton_pressed(); + /// Instructions if the "↑"-button has been pressed. + void on_upOrderButton_pressed(); + /// Instructions if the "↓"-button has been pressed. + void on_downOrderButton_pressed(); + /// Instructions if the "order mesh"-button has been pressed. + void on_orderButton_pressed(); + /// As the x/y/z input changes an estimation of the expected Voxel is given. + void updateExpectedVoxel(); + void on_xlineEdit_textChanged(); + void on_ylineEdit_textChanged(); + void on_zlineEdit_textChanged(); +}; diff --git a/Applications/DataExplorer/mainwindow.cpp b/Applications/DataExplorer/mainwindow.cpp index e4850e0b58e05d583a8d83ddf0cea8d5056a071f..7ae5448b4f98c4b7dd894f40a2a3a4ff105dfc10 100644 --- a/Applications/DataExplorer/mainwindow.cpp +++ b/Applications/DataExplorer/mainwindow.cpp @@ -72,6 +72,7 @@ #include "DataView/DiagramView/DiagramPrefsDialog.h" #include "DataView/GMSHPrefsDialog.h" #include "DataView/GeoOnMeshMappingDialog.h" +#include "DataView/Layers2GridDialog.h" #include "DataView/LicenseDialog.h" #include "DataView/LineEditDialog.h" #include "DataView/MergeGeometriesDialog.h" @@ -1352,6 +1353,12 @@ void MainWindow::showTranslateDataDialog() dlg.exec(); } +void MainWindow::showLayers2GridDialog() +{ + auto dlg = Layers2GridDialog(_meshModel.get()); + dlg.exec(); +} + void MainWindow::convertPointsToStations(std::string const& geo_name) { std::string stn_name = geo_name + " Stations"; diff --git a/Applications/DataExplorer/mainwindow.h b/Applications/DataExplorer/mainwindow.h index 65c050c26ae35c52a087a7be6344b4e0fc2d218e..5dc98f3e78b2764d01273a8c6800f3a8dfca2b3d 100644 --- a/Applications/DataExplorer/mainwindow.h +++ b/Applications/DataExplorer/mainwindow.h @@ -103,6 +103,7 @@ protected slots: void showMergeGeometriesDialog(); void showMeshAnalysisDialog(); void showTranslateDataDialog(); + void showLayers2GridDialog(); void showMeshQualitySelectionDialog( MeshLib::VtkMappedMeshSource* mshSource); void showVisalizationPrefsDialog(); diff --git a/Applications/DataExplorer/mainwindow.ui b/Applications/DataExplorer/mainwindow.ui index d8621471fd715016f548cb2d95ca49b96da29812..3c6591263c41b35f79fdbaccdbb14a3200e5ef3e 100644 --- a/Applications/DataExplorer/mainwindow.ui +++ b/Applications/DataExplorer/mainwindow.ui @@ -132,6 +132,7 @@ <addaction name="actionDiagram_Viewer"/> <addaction name="actionFEM_Test"/> <addaction name="actionTranslating_Data"/> + <addaction name="actionCreate_a_Voxelgrid"/> </widget> <widget class="QMenu" name="menuHelp"> <property name="title"> @@ -475,6 +476,11 @@ <string>Translating Data</string> </property> </action> + <action name="actionCreate_a_Voxelgrid"> + <property name="text"> + <string>Create a Voxelgrid</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> @@ -757,6 +763,22 @@ </hint> </hints> </connection> + <connection> + <sender>actionCreate_a_Voxelgrid</sender> + <signal>triggered()</signal> + <receiver>MainWindowClass</receiver> + <slot>showLayers2GridDialog()</slot> + <hints> + <hint type="sourcelabel"> + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel"> + <x>400</x> + <y>372</y> + </hint> + </hints> + </connection> <connection> <sender>actionCreate_Structured_Mesh</sender> <signal>triggered()</signal>