From f55811ba84c3f41b777dfc98d4e6f93c3e2e7f1d Mon Sep 17 00:00:00 2001 From: Karsten Rink <karsten.rink@ufz.de> Date: Sat, 22 Feb 2014 23:24:38 +0100 Subject: [PATCH] storing error codes in bitset and adding MeshQualityController call to GUI --- Gui/DataView/MshView.cpp | 17 +++- Gui/DataView/MshView.h | 9 +- Gui/mainwindow.cpp | 3 - MeshLib/Elements/Element.h | 1 + MeshLib/Elements/TemplateHex.tpp | 15 ++-- MeshLib/Elements/TemplateLine.tpp | 4 +- MeshLib/Elements/TemplatePrism.tpp | 8 +- MeshLib/Elements/TemplatePyramid.tpp | 9 +- MeshLib/Elements/TemplateQuad.tpp | 10 +-- MeshLib/Elements/TemplateTet.tpp | 4 +- MeshLib/Elements/TemplateTri.tpp | 4 +- MeshLib/MeshEnums.cpp | 1 + MeshLib/MeshEnums.h | 15 +--- MeshLib/MeshQuality/ElementErrorCode.h | 88 +++++++++++++++++++ MeshLib/MeshQuality/MeshQualityController.cpp | 32 ++++++- MeshLib/MeshQuality/MeshQualityController.h | 2 +- 16 files changed, 166 insertions(+), 56 deletions(-) create mode 100644 MeshLib/MeshQuality/ElementErrorCode.h diff --git a/Gui/DataView/MshView.cpp b/Gui/DataView/MshView.cpp index 78a89272456..53a675b72c4 100644 --- a/Gui/DataView/MshView.cpp +++ b/Gui/DataView/MshView.cpp @@ -28,6 +28,7 @@ #include "MshModel.h" #include "OGSError.h" #include "MeshSurfaceExtraction.h" +#include "MeshQuality/MeshQualityController.h" #include "ImportFileTypes.h" #include "LastSavedFileDirectory.h" @@ -119,7 +120,8 @@ void MshView::contextMenuEvent( QContextMenuEvent* event ) QMenu direct_cond_menu("DIRECT Conditions"); QAction* editMeshAction = menu.addAction("Edit mesh..."); QAction* editValuesAction = menu.addAction("Edit material groups..."); - QAction* checkMeshAction = menu.addAction("Check mesh quality..."); + QAction* testMeshAction = menu.addAction("Test mesh"); + QAction* meshQualityAction = menu.addAction("Calculate element quality..."); QAction* surfaceMeshAction (NULL); if (mesh_dim==3) surfaceMeshAction = menu.addAction("Extract surface"); @@ -136,9 +138,10 @@ void MshView::contextMenuEvent( QContextMenuEvent* event ) QAction* addDirectAction = direct_cond_menu.addAction("Add..."); QAction* loadDirectAction = direct_cond_menu.addAction("Load..."); //menu.addSeparator(); - connect(editMeshAction, SIGNAL(triggered()), this, SLOT(openMshEditDialog())); + connect(editMeshAction, SIGNAL(triggered()), this, SLOT(openMeshEditDialog())); connect(editValuesAction, SIGNAL(triggered()), this, SLOT(openValuesEditDialog())); - connect(checkMeshAction, SIGNAL(triggered()), this, SLOT(checkMeshQuality())); + connect(testMeshAction, SIGNAL(triggered()), this, SLOT(testMesh())); + connect(meshQualityAction, SIGNAL(triggered()), this, SLOT(checkMeshQuality())); if (mesh_dim==3) connect(surfaceMeshAction, SIGNAL(triggered()), this, SLOT(extractSurfaceMesh())); connect(addDirectAction, SIGNAL(triggered()), this, SLOT(addDIRECTSourceTerms())); @@ -152,7 +155,7 @@ void MshView::contextMenuEvent( QContextMenuEvent* event ) } } -void MshView::openMshEditDialog() +void MshView::openMeshEditDialog() { MshModel* model = static_cast<MshModel*>(this->model()); QModelIndex index = this->selectionModel()->currentIndex(); @@ -263,6 +266,12 @@ void MshView::addDIRECTSourceTerms() emit requestCondSetupDialog(grid->getName(), GeoLib::GEOTYPE::INVALID, 0, false); } +void MshView::testMesh() +{ + QModelIndex index = this->selectionModel()->currentIndex(); + MeshLib::Mesh* mesh = const_cast<MeshLib::Mesh*>(static_cast<MshModel*>(this->model())->getMesh(index)); + MeshLib::MeshQualityController::testElementGeometry(*mesh); +} void MshView::loadDIRECTSourceTerms() { diff --git a/Gui/DataView/MshView.h b/Gui/DataView/MshView.h index 3c4b907a580..f485fcf78dd 100644 --- a/Gui/DataView/MshView.h +++ b/Gui/DataView/MshView.h @@ -61,7 +61,7 @@ private: private slots: /// Opens a dialog for editing meshes. - void openMshEditDialog(); + void openMeshEditDialog(); /// Opens a dialog for editing material groups. void openValuesEditDialog(); @@ -85,9 +85,10 @@ private slots: /// Calls the FileDialog to save a mesh to a file. int writeToFile() const; - /** - * checks the mesh quality - */ + /// Calls a MeshQualityController + void testMesh(); + + /// Calls the dialog for calculating an element quality metric void checkMeshQuality(); signals: diff --git a/Gui/mainwindow.cpp b/Gui/mainwindow.cpp index 26e223cf2f4..ecd49586695 100644 --- a/Gui/mainwindow.cpp +++ b/Gui/mainwindow.cpp @@ -1278,9 +1278,6 @@ void MainWindow::showDataExplorerSettingsDialog() void MainWindow::FEMTestStart() { - - MeshLib::MeshQualityController mqc(*(const_cast<MeshLib::Mesh*>(this->_meshModels->getMesh("mshtest")))); - } diff --git a/MeshLib/Elements/Element.h b/MeshLib/Elements/Element.h index f894809fcad..bb2fa7fe4e3 100644 --- a/MeshLib/Elements/Element.h +++ b/MeshLib/Elements/Element.h @@ -19,6 +19,7 @@ #include <limits> #include "MeshEnums.h" #include "Mesh.h" +#include "MeshQuality/ElementErrorCode.h" namespace MeshLib { diff --git a/MeshLib/Elements/TemplateHex.tpp b/MeshLib/Elements/TemplateHex.tpp index 1a2cec24523..3ff5075d194 100644 --- a/MeshLib/Elements/TemplateHex.tpp +++ b/MeshLib/Elements/TemplateHex.tpp @@ -158,15 +158,18 @@ unsigned TemplateHex<NNODES,CELLHEXTYPE>::identifyFace(Node* nodes[3]) const template <unsigned NNODES, CellType CELLHEXTYPE> ElementErrorCode TemplateHex<NNODES,CELLHEXTYPE>::isValid() const { - ElementErrorCode error_code(ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_volume < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; - + error_code.set(ElementErrorFlag::ZeroVolume); + for (unsigned i=0; i<6; ++i) { - const MeshLib::Quad* quad (dynamic_cast<const MeshLib::Quad*>(this->getFace(i))); - error_code = error_code | quad->isValid(); - delete quad; + if (!error_code.all()) + { + const MeshLib::Quad* quad (dynamic_cast<const MeshLib::Quad*>(this->getFace(i))); + error_code |= quad->isValid(); + delete quad; + } } return error_code; } diff --git a/MeshLib/Elements/TemplateLine.tpp b/MeshLib/Elements/TemplateLine.tpp index 1ebf309b1a4..eada00fb893 100644 --- a/MeshLib/Elements/TemplateLine.tpp +++ b/MeshLib/Elements/TemplateLine.tpp @@ -50,9 +50,9 @@ TemplateLine<NNODES,CELLLINETYPE>::~TemplateLine() template <unsigned NNODES, CellType CELLLINETYPE> ElementErrorCode TemplateLine<NNODES,CELLLINETYPE>::isValid() const { - ElementErrorCode error_code (ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_length < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; + error_code.set(ElementErrorFlag::ZeroVolume); return error_code; } diff --git a/MeshLib/Elements/TemplatePrism.tpp b/MeshLib/Elements/TemplatePrism.tpp index 85ee56cfbfc..a5f5ab250a8 100644 --- a/MeshLib/Elements/TemplatePrism.tpp +++ b/MeshLib/Elements/TemplatePrism.tpp @@ -167,17 +167,17 @@ unsigned TemplatePrism<NNODES,CELLPRISMTYPE>::identifyFace(Node* nodes[3]) const template <unsigned NNODES, CellType CELLPRISMTYPE> ElementErrorCode TemplatePrism<NNODES,CELLPRISMTYPE>::isValid() const { - ElementErrorCode error_code(ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_volume < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; + error_code.set(ElementErrorFlag::ZeroVolume); for (unsigned i=1; i<4; ++i) { const MeshLib::Quad* quad (dynamic_cast<const MeshLib::Quad*>(this->getFace(i))); if (quad) - error_code = error_code | quad->isValid(); + error_code |= quad->isValid(); else - error_code = error_code | ElementErrorCode::NodeOrder; + error_code.set(ElementErrorFlag::NodeOrder); delete quad; } return error_code; diff --git a/MeshLib/Elements/TemplatePyramid.tpp b/MeshLib/Elements/TemplatePyramid.tpp index d572550b265..658601dc154 100644 --- a/MeshLib/Elements/TemplatePyramid.tpp +++ b/MeshLib/Elements/TemplatePyramid.tpp @@ -169,21 +169,20 @@ unsigned TemplatePyramid<NNODES,CELLPYRAMIDTYPE>::identifyFace(Node* nodes[3]) c template <unsigned NNODES, CellType CELLPYRAMIDTYPE> ElementErrorCode TemplatePyramid<NNODES,CELLPYRAMIDTYPE>::isValid() const { - ElementErrorCode error_code(ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_volume < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; + error_code.set(ElementErrorFlag::ZeroVolume); const MeshLib::Quad* base (dynamic_cast<const MeshLib::Quad*>(this->getFace(4))); if (base) - error_code = error_code | base->isValid(); + error_code |= base->isValid(); else - error_code = error_code | ElementErrorCode::NodeOrder; + error_code.set(ElementErrorFlag::NodeOrder); delete base; return error_code; } - template <unsigned NNODES, CellType CELLPYRAMIDTYPE> Element* TemplatePyramid<NNODES,CELLPYRAMIDTYPE>::reviseElement() const { diff --git a/MeshLib/Elements/TemplateQuad.tpp b/MeshLib/Elements/TemplateQuad.tpp index 49fb74f5d8d..cf1519cfda3 100644 --- a/MeshLib/Elements/TemplateQuad.tpp +++ b/MeshLib/Elements/TemplateQuad.tpp @@ -122,16 +122,14 @@ unsigned TemplateQuad<NNODES,CELLQUADTYPE>::identifyFace(Node* nodes[3]) const template <unsigned NNODES, CellType CELLQUADTYPE> ElementErrorCode TemplateQuad<NNODES,CELLQUADTYPE>::isValid() const { - ElementErrorCode error_code(ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_area < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; - + error_code.set(ElementErrorFlag::ZeroVolume); if (!GeoLib::pointsOnAPlane(*_nodes[0], *_nodes[1], *_nodes[2], *_nodes[3])) - error_code = error_code | ElementErrorCode::NonCoplanar; - + error_code.set(ElementErrorFlag::NonCoplanar); if (!(GeoLib::dividedByPlane(*_nodes[0], *_nodes[2], *_nodes[1], *_nodes[3]) && GeoLib::dividedByPlane(*_nodes[1], *_nodes[3], *_nodes[0], *_nodes[2]))) - error_code = error_code | ElementErrorCode::NonConvex; + error_code.set(ElementErrorFlag::NonConvex); return error_code; } diff --git a/MeshLib/Elements/TemplateTet.tpp b/MeshLib/Elements/TemplateTet.tpp index c83c65cddbc..c9d41d667de 100644 --- a/MeshLib/Elements/TemplateTet.tpp +++ b/MeshLib/Elements/TemplateTet.tpp @@ -147,9 +147,9 @@ unsigned TemplateTet<NNODES,CELLTETTYPE>::identifyFace(Node* nodes[3]) const template <unsigned NNODES, CellType CELLTETTYPE> ElementErrorCode TemplateTet<NNODES,CELLTETTYPE>::isValid() const { - ElementErrorCode error_code(ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_volume < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; + error_code.set(ElementErrorFlag::ZeroVolume); return error_code; } diff --git a/MeshLib/Elements/TemplateTri.tpp b/MeshLib/Elements/TemplateTri.tpp index ee7ecba68f6..af6a8dff99a 100644 --- a/MeshLib/Elements/TemplateTri.tpp +++ b/MeshLib/Elements/TemplateTri.tpp @@ -83,9 +83,9 @@ bool TemplateTri<NNODES,CELLTRITYPE>::isPntInside(GeoLib::Point const& pnt, doub template <unsigned NNODES, CellType CELLTRITYPE> ElementErrorCode TemplateTri<NNODES,CELLTRITYPE>::isValid() const { - ElementErrorCode error_code (ElementErrorCode::NoError); + ElementErrorCode error_code; if (this->_area < std::numeric_limits<double>::epsilon()) - error_code = error_code | ElementErrorCode::ZeroVolume; + error_code.set(ElementErrorFlag::ZeroVolume); return error_code; } diff --git a/MeshLib/MeshEnums.cpp b/MeshLib/MeshEnums.cpp index 0b9bb32b935..295c4c6ca1a 100644 --- a/MeshLib/MeshEnums.cpp +++ b/MeshLib/MeshEnums.cpp @@ -64,3 +64,4 @@ const std::string MeshQualityType2String(const MeshQualityType t) return "Volume"; return "none"; } + diff --git a/MeshLib/MeshEnums.h b/MeshLib/MeshEnums.h index 28d2b83c680..3bf5caecdd9 100644 --- a/MeshLib/MeshEnums.h +++ b/MeshLib/MeshEnums.h @@ -57,7 +57,7 @@ enum class CellType }; /** - * \brief Describes a mesh quality criteria. + * \brief Describes a mesh quality metric. */ enum class MeshQualityType { @@ -68,15 +68,6 @@ enum class MeshQualityType EQUIANGLESKEW }; -enum ElementErrorCode -{ - NoError = 0x00, - ZeroVolume = 0x01, - NonCoplanar = 0x02, - NonConvex = 0x04, - NodeOrder = 0x08 -}; - /// Given a MeshElemType this returns the appropriate string. const std::string MeshElemType2String(const MeshElemType t); @@ -85,8 +76,4 @@ MeshElemType String2MeshElemType(const std::string &s); const std::string MeshQualityType2String(const MeshQualityType t); -inline ElementErrorCode operator|(ElementErrorCode a, ElementErrorCode b) -{return static_cast<ElementErrorCode>(static_cast<int>(a) | static_cast<int>(b));} - - #endif //MESHENUMS_H diff --git a/MeshLib/MeshQuality/ElementErrorCode.h b/MeshLib/MeshQuality/ElementErrorCode.h new file mode 100644 index 00000000000..e516f18e7ab --- /dev/null +++ b/MeshLib/MeshQuality/ElementErrorCode.h @@ -0,0 +1,88 @@ +/** + * \file ElementErrorCode.h + * \author Karsten Rink + * \date 2014-02-21 + * \brief Definition of ElementErrorCodes. + * + * \copyright + * Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#ifndef ELEMENTERRORCODES_H +#define ELEMENTERRORCODES_H + +#include <bitset> +#include <string> + + +/// Possible error flags for mesh elements +enum class ElementErrorFlag +{ + ZeroVolume, + NonCoplanar, + NonConvex, + NodeOrder, + //... add other error flags here + MaxValue // this needs to be last to set the bitset size correctly! +}; + +/// Collects error flags for mesh elements +class ElementErrorCode +{ +public: + ElementErrorCode() {} + ElementErrorCode(std::bitset< static_cast<std::size_t>(ElementErrorFlag::MaxValue) > error_flags) + : _errors(error_flags) {} + + ~ElementErrorCode() {} + + /// Get value for a specific flag + bool get(ElementErrorFlag e) const { return _errors[static_cast<std::size_t>(e)]; } + /// Set a specific flag + void set(ElementErrorFlag e) { _errors[static_cast<std::size_t>(e)] = true; } + /// Reset a specific flag + void reset(ElementErrorFlag e) { _errors[static_cast<std::size_t>(e)] = false; } + + /// Returns true if ANY flag is set, false otherwise + bool any() const { return _errors.any(); } + /// Returns true if ALL flag is set, false otherwise + bool all() const { return _errors.all(); } + /// Returns true if NO flags are set, false otherwise + bool none() const { return _errors.none(); } + + /// Returns the size of the error flag set + std::size_t size() const { return _errors.size(); } + + /// Returns the underlying bitset + const std::bitset< static_cast<std::size_t>(ElementErrorFlag::MaxValue) >& bitset() const { return _errors; } + + //inline bool operator[](const ElementErrorFlag e) { return _errors[static_cast<std::size_t>(e)]; } + //inline const bool operator[](const ElementErrorFlag e) const { return _errors[static_cast<std::size_t>(e)]; } + + inline ElementErrorCode operator|=(ElementErrorCode e) { return _errors |= e.bitset(); } + + /// Returns a string output for a specific error flag + static std::string toString(const ElementErrorFlag e) + { + if (e == ElementErrorFlag::ZeroVolume) + return "zero volume"; + else if (e == ElementErrorFlag::NonCoplanar) + return "non coplanar nodes"; + else if (e == ElementErrorFlag::NonConvex) + return "non-convex geometry"; + else if (e == ElementErrorFlag::NodeOrder) + return "wrong node order"; + return "nonspecified error"; + } + +private: + /// The bit set collecting the error flags + std::bitset< static_cast<std::size_t>(ElementErrorFlag::MaxValue) > _errors; +}; + + +#endif //ELEMENTERRORCODES_H diff --git a/MeshLib/MeshQuality/MeshQualityController.cpp b/MeshLib/MeshQuality/MeshQualityController.cpp index 77ec47128a8..646fce4ac95 100644 --- a/MeshLib/MeshQuality/MeshQualityController.cpp +++ b/MeshLib/MeshQuality/MeshQualityController.cpp @@ -13,9 +13,12 @@ */ +#include <numeric> + #include "MeshQualityController.h" #include "Mesh.h" #include "Node.h" +#include "Elements/Element.h" #include "MeshEditing/removeMeshNodes.h" @@ -26,6 +29,7 @@ namespace MeshLib { MeshQualityController::MeshQualityController(MeshLib::Mesh &mesh) { this->removeUnusedMeshNodes(mesh); + this->testElementGeometry(mesh); } void MeshQualityController::removeUnusedMeshNodes(MeshLib::Mesh &mesh) @@ -44,14 +48,36 @@ void MeshQualityController::removeUnusedMeshNodes(MeshLib::Mesh &mesh) INFO("Removed %d unused mesh nodes.", del_node_idx.size()); } -void MeshQualityController::testElementGeometry(MeshLib::Mesh &mesh) +void MeshQualityController::testElementGeometry(const MeshLib::Mesh &mesh) { - unsigned count(0); + const std::size_t nErrorCodes (static_cast<std::size_t>(ElementErrorFlag::MaxValue)); + unsigned error_count[nErrorCodes] = {{0}}; const std::size_t nElements (mesh.getNElements()); - for (std::size_t i=0; i<nElements; ++i) + const std::vector<MeshLib::Element*> &elements (mesh.getElements()); + + unsigned count(0); + for (unsigned i=0; i<nElements; ++i) { + const ElementErrorCode e = elements[i]->isValid(); + if (e.none()) + continue; + const std::bitset<nErrorCodes> flags (e.bitset()); + for (unsigned i=0; i<nErrorCodes; ++i) + error_count[i] += flags[i]; + } + + const unsigned error_sum (static_cast<unsigned>(std::accumulate(error_count, error_count+nErrorCodes, 0.0))); + if (error_sum != 0) + { + ElementErrorFlag flags[nErrorCodes] = {ElementErrorFlag::ZeroVolume, ElementErrorFlag::NonCoplanar, + ElementErrorFlag::NonConvex, ElementErrorFlag::NodeOrder }; + for (std::size_t i=0; i<nErrorCodes; ++i) + if (error_count[i]) + INFO ("%d elements found with %s.", error_count[i], ElementErrorCode::toString(flags[i])); } + else + INFO ("No errors found."); } } // end namespace MeshLib diff --git a/MeshLib/MeshQuality/MeshQualityController.h b/MeshLib/MeshQuality/MeshQualityController.h index 831624193e5..2bf54121e5c 100644 --- a/MeshLib/MeshQuality/MeshQualityController.h +++ b/MeshLib/MeshQuality/MeshQualityController.h @@ -33,7 +33,7 @@ public: static void removeUnusedMeshNodes(MeshLib::Mesh &mesh); /// Tests if elements are geometrically correct - static void testElementGeometry(MeshLib::Mesh &mesh); + static void testElementGeometry(const MeshLib::Mesh &mesh); private: -- GitLab