From bde82bbbb3bb340bd2369f1ca8ea0a6e13a993c3 Mon Sep 17 00:00:00 2001
From: Karsten Rink <karsten.rink@ufz.de>
Date: Thu, 18 Sep 2014 14:52:26 +0200
Subject: [PATCH] reworked prims-layer-generation to work from top to bottom
 and prefer older (lower) layers in cases of conflicting information

---
 MeshLib/MeshGenerators/MeshLayerMapper.cpp | 183 ++++++++++++++++++---
 MeshLib/MeshGenerators/MeshLayerMapper.h   |  11 ++
 2 files changed, 170 insertions(+), 24 deletions(-)

diff --git a/MeshLib/MeshGenerators/MeshLayerMapper.cpp b/MeshLib/MeshGenerators/MeshLayerMapper.cpp
index 38ea9700502..cb16ee54144 100644
--- a/MeshLib/MeshGenerators/MeshLayerMapper.cpp
+++ b/MeshLib/MeshGenerators/MeshLayerMapper.cpp
@@ -33,9 +33,18 @@
 #include "Elements/Pyramid.h"
 #include "Elements/Prism.h"
 #include "MeshSurfaceExtraction.h"
+#include "MeshQuality/MeshValidation.h"
 
 #include "MathTools.h"
 
+const unsigned MeshLayerMapper::_pyramid_base[3][4] =
+{
+	{1, 3, 4, 2}, // Point 4 missing
+	{2, 4, 3, 0}, // Point 5 missing
+	{0, 3, 4, 1}, // Point 6 missing
+};
+
+
 MeshLib::Mesh* MeshLayerMapper::createStaticLayers(MeshLib::Mesh const& mesh, std::vector<float> const& layer_thickness_vector, std::string const& mesh_name) const
 {
 	std::vector<float> thickness;
@@ -52,7 +61,7 @@ MeshLib::Mesh* MeshLayerMapper::createStaticLayers(MeshLib::Mesh const& mesh, st
 		return nullptr;
 	}
 
-	const size_t nNodes = mesh.getNNodes();
+	const std::size_t nNodes = mesh.getNNodes();
 	// count number of 2d elements in the original mesh
 	const std::size_t nElems (std::count_if(mesh.getElements().begin(), mesh.getElements().end(),
 			[](MeshLib::Element const* elem) { return (elem->getDimension() == 2);}));
@@ -75,36 +84,152 @@ MeshLib::Mesh* MeshLayerMapper::createStaticLayers(MeshLib::Mesh const& mesh, st
             [&z_offset](MeshLib::Node* node){ return new MeshLib::Node((*node)[0], (*node)[1], (*node)[2]-z_offset); });
 
 		// starting with 2nd layer create prism or hex elements connecting the last layer with the current one
-		if (layer_id > 0)
-		{
-			node_offset -= nNodes;
-			const unsigned mat_id (nLayers - layer_id);
+		if (layer_id == 0)
+            continue;
 
-			for (unsigned i = 0; i < nOrgElems; ++i)
-			{
-				const MeshLib::Element* sfc_elem( elems[i] );
-				if (sfc_elem->getDimension() < 2) // ignore line-elements
-					continue;
+        node_offset -= nNodes;
+		const unsigned mat_id (nLayers - layer_id);
+
+		for (unsigned i = 0; i < nOrgElems; ++i)
+		{
+			const MeshLib::Element* sfc_elem( elems[i] );
+			if (sfc_elem->getDimension() < 2) // ignore line-elements
+				continue;
 				
-				const unsigned nElemNodes(sfc_elem->getNNodes());
-				MeshLib::Node** e_nodes = new MeshLib::Node*[2*nElemNodes];
+			const unsigned nElemNodes(sfc_elem->getNNodes());
+			MeshLib::Node** e_nodes = new MeshLib::Node*[2*nElemNodes];
 
-				for (unsigned j=0; j<nElemNodes; ++j)
-				{
-					const unsigned node_id = sfc_elem->getNode(j)->getID() + node_offset;
-					e_nodes[j] = new_nodes[node_id+nNodes];
-					e_nodes[j+nElemNodes] = new_nodes[node_id];
-				}
-				if (sfc_elem->getGeomType() == MeshElemType::TRIANGLE)	// extrude triangles to prism
-					new_elems.push_back (new MeshLib::Prism(e_nodes, mat_id));
-				else if (sfc_elem->getGeomType() == MeshElemType::QUAD)	// extrude quads to hexes
-					new_elems.push_back (new MeshLib::Hex(e_nodes, mat_id));
+			for (unsigned j=0; j<nElemNodes; ++j)
+			{
+				const unsigned node_id = sfc_elem->getNode(j)->getID() + node_offset;
+				e_nodes[j] = new_nodes[node_id+nNodes];
+				e_nodes[j+nElemNodes] = new_nodes[node_id];
 			}
+			if (sfc_elem->getGeomType() == MeshElemType::TRIANGLE)	// extrude triangles to prism
+				new_elems.push_back (new MeshLib::Prism(e_nodes, mat_id));
+			else if (sfc_elem->getGeomType() == MeshElemType::QUAD)	// extrude quads to hexes
+				new_elems.push_back (new MeshLib::Hex(e_nodes, mat_id));
 		}
 	}
 	return new MeshLib::Mesh(mesh_name, new_nodes, new_elems);
 }
 
+MeshLib::Mesh* MeshLayerMapper::createRasterLayers(MeshLib::Mesh const& mesh, std::vector<std::string> const& raster_paths, std::string const& mesh_name) const
+{
+	if (mesh.getDimension() != 2 || !allRastersExist(raster_paths))
+		return false;
+
+	std::vector<GeoLib::Raster const*> rasters;
+	rasters.reserve(raster_paths.size());
+	for (auto path = raster_paths.begin(); path != raster_paths.end(); ++path)
+		rasters.push_back(FileIO::AsciiRasterInterface::getRasterFromASCFile(*path));
+
+	MeshLib::Mesh* result = this->createRasterLayers(mesh, rasters, mesh_name);
+	std::for_each(rasters.begin(), rasters.end(), [](GeoLib::Raster const*const raster){ delete raster; });
+	return result;
+}
+
+MeshLib::Mesh* MeshLayerMapper::createRasterLayers(MeshLib::Mesh const& mesh, std::vector<GeoLib::Raster const*> const& rasters, std::string const& mesh_name) const
+{
+    const std::size_t nLayers(rasters.size());
+	if (nLayers < 1 || mesh.getDimension() != 2)
+	{
+		ERR("MeshLayerMapper::createStaticLayers(): A 2D mesh with nLayers > 0 is required as input.");
+		return nullptr;
+	}
+
+    MeshLib::Mesh* dem_mesh (new MeshLib::Mesh(mesh));
+    if (layerMapping(*dem_mesh, *rasters.back(), 0, 0, 0))
+    {
+        std::size_t const nNodes = mesh.getNNodes();
+	    std::vector<MeshLib::Node*> new_nodes;
+        new_nodes.reserve(nLayers * nNodes);
+
+	    // number of triangles in the original mesh
+	    std::size_t const nElems (std::count_if(mesh.getElements().begin(), mesh.getElements().end(),
+			[](MeshLib::Element const* elem) { return (elem->getGeomType() == MeshElemType::TRIANGLE);}));
+	    std::vector<MeshLib::Element*> new_elems;
+        new_elems.reserve(nElems * (nLayers-1));
+
+        for (std::size_t i=0; i<nLayers; ++i)
+            addLayerToMesh(*dem_mesh, i, *rasters[i], new_nodes, new_elems);
+
+        MeshLib::Mesh* result = new MeshLib::Mesh(mesh_name, new_nodes, new_elems);
+        MeshLib::MeshValidation::removeUnusedMeshNodes(*result);
+        return result;
+
+    }
+}
+
+void MeshLayerMapper::addLayerToMesh(const MeshLib::Mesh &dem_mesh, unsigned layer_id, GeoLib::Raster const& raster, std::vector<MeshLib::Node*> &new_nodes, std::vector<MeshLib::Element*> &new_elems) const
+{
+    std::size_t const nNodes = dem_mesh.getNNodes();
+	std::vector<MeshLib::Node*> const& nodes = dem_mesh.getNodes();
+    int const last_layer_node_offset = (layer_id-1) * nNodes;
+    unsigned const layer_node_offset = layer_id * nNodes;
+
+	// add nodes for new layer
+    for (std::size_t i=0; i<nNodes; ++i)
+    {
+        // min of dem elevation and layer elevation
+        double const elevation = std::min(raster.interpolateValueAtPoint(*nodes[i]), (*nodes[i])[2]);
+
+        if ((layer_id == 0) || 
+            (elevation - (*new_nodes[last_layer_node_offset + i])[2] > std::numeric_limits<double>::epsilon()))
+            new_nodes.push_back(new MeshLib::Node((*nodes[i])[0], (*nodes[i])[1], elevation, (layer_id * nNodes) + i));
+        else
+            new_nodes.push_back(new MeshLib::Node((*nodes[i])[0], (*nodes[i])[1], elevation, new_nodes[last_layer_node_offset +i]->getID()));
+    }
+
+    if (layer_id == 0)
+        return;
+
+	std::vector<MeshLib::Element*> const& elems = dem_mesh.getElements();
+    std::size_t const nElems (dem_mesh.getNElements());
+
+    for (std::size_t i=0; i<nElems; ++i)
+    {
+        MeshLib::Element* elem (elems[i]);
+        if (elem->getGeomType() != MeshElemType::TRIANGLE)
+            continue;
+        unsigned node_counter(3), missing_idx(0);
+        std::array<MeshLib::Node*, 6> new_elem_nodes;
+        for (unsigned j=0; j<3; ++j)
+        {
+            new_elem_nodes[j] = new_nodes[last_layer_node_offset + elem->getNodeIndex(j)];
+            new_elem_nodes[node_counter] = (new_nodes[last_layer_node_offset + elem->getNodeIndex(j) + nNodes]);
+            if (new_elem_nodes[j]->getID() != new_elem_nodes[node_counter]->getID())
+                node_counter++;
+            else
+                missing_idx = j;
+        }
+
+        switch (node_counter)
+        {
+        case 6:
+            new_elems.push_back(new MeshLib::Prism(new_elem_nodes, layer_id));
+            break;
+        case 5:
+            std::array<MeshLib::Node*, 5> pyramid_nodes;
+            pyramid_nodes[0] = new_elem_nodes[_pyramid_base[missing_idx][0]];
+            pyramid_nodes[1] = new_elem_nodes[_pyramid_base[missing_idx][1]];
+            pyramid_nodes[2] = new_elem_nodes[_pyramid_base[missing_idx][2]];
+            pyramid_nodes[3] = new_elem_nodes[_pyramid_base[missing_idx][3]];
+            pyramid_nodes[4] = new_elem_nodes[missing_idx];
+            new_elems.push_back(new MeshLib::Pyramid(pyramid_nodes, layer_id));
+            break;
+        case 4:
+            std::array<MeshLib::Node*, 4> tet_nodes;
+            std::copy(new_elem_nodes.begin(), new_elem_nodes.begin() + node_counter, tet_nodes.begin());
+            new_elems.push_back(new MeshLib::Tet(tet_nodes, layer_id));
+            break;
+        default:
+            continue;
+        }
+    }
+}
+
+
 bool MeshLayerMapper::layerMapping(MeshLib::Mesh &new_mesh, const std::string &rasterfile,
                                    const unsigned nLayers, const unsigned layer_id, double noDataReplacementValue = 0.0) const
 {
@@ -154,7 +279,7 @@ bool MeshLayerMapper::layerMapping(MeshLib::Mesh &new_mesh, const GeoLib::Raster
 		}
 
 		double elevation (raster.interpolateValueAtPoint(*nodes[i]));
-		if (elevation - no_data < std::numeric_limits<double>::epsilon()) 
+		if (std::abs(elevation - no_data) < std::numeric_limits<double>::epsilon()) 
 			elevation = noDataReplacementValue;
 		nodes[i]->updateCoordinates((*nodes[i])[0], (*nodes[i])[1], elevation);
 	}
@@ -307,5 +432,15 @@ MeshLib::Mesh* MeshLayerMapper::blendLayersWithSurface(MeshLib::Mesh &mesh, cons
 	return new MeshLib::Mesh("SubsurfaceMesh", new_nodes, new_elements);
 }
 
-
+bool MeshLayerMapper::allRastersExist(const std::vector<std::string> &raster_paths) const
+{
+	for (auto raster = raster_paths.begin(); raster != raster_paths.end(); ++raster)
+	{
+		std::ifstream file_stream (*raster, std::ifstream::in);
+		if (!file_stream.good())
+			return false;
+		file_stream.close();
+	}
+	return true;
+}
 
diff --git a/MeshLib/MeshGenerators/MeshLayerMapper.h b/MeshLib/MeshGenerators/MeshLayerMapper.h
index aab59cb115c..370e035061a 100644
--- a/MeshLib/MeshGenerators/MeshLayerMapper.h
+++ b/MeshLib/MeshGenerators/MeshLayerMapper.h
@@ -23,6 +23,7 @@ class QImage;
 namespace MeshLib {
     class Mesh;
     class Node;
+    class Element;
 }
 
 /**
@@ -44,6 +45,10 @@ public:
     */
     MeshLib::Mesh* createStaticLayers(MeshLib::Mesh const& mesh, std::vector<float> const& layer_thickness_vector, std::string const& mesh_name = "SubsurfaceMesh") const;
 
+    MeshLib::Mesh* createRasterLayers(MeshLib::Mesh const& mesh, std::vector<std::string> const& raster_paths, std::string const& mesh_name = "SubsurfaceMesh") const;
+
+    MeshLib::Mesh* createRasterLayers(MeshLib::Mesh const& mesh, std::vector<GeoLib::Raster const*> const& rasters, std::string const& mesh_name = "SubsurfaceMesh") const;
+
     /**
     * Maps the z-values of nodes in the designated layer of the given mesh according to the given raster.
     * Note: This only results in a valid mesh if the layers don't intersect each other.
@@ -67,7 +72,13 @@ public:
     MeshLib::Mesh* blendLayersWithSurface(MeshLib::Mesh &mesh, const unsigned nLayers, const std::string &dem_raster) const;
 
 private:
+    /// Adds another layer to the subsurface mesh
+    void addLayerToMesh(const MeshLib::Mesh &mesh_layer, unsigned layer_id, GeoLib::Raster const& raster, std::vector<MeshLib::Node*> &new_nodes, std::vector<MeshLib::Element*> &new_elems) const;
+
+    /// Checks if all raster files actually exist
+	bool allRastersExist(const std::vector<std::string> &raster_paths) const;
 
+    static const unsigned _pyramid_base[3][4];
 };
 
 #endif //MESHLAYERMAPPER_H
-- 
GitLab