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