Skip to content

Commit

Permalink
[FEATURE] QgsMeshLayer part 1: Reading raw mesh
Browse files Browse the repository at this point in the history
Introducting MDAL, QgsMeshLayer, mesh data providers (mesh_memory, mdal)
to read and visualize raw meshes: vertices and faces. Support dragging
2dm files from browser on canvas to visualize 2dm meshes.
Support for QgsMeshLayer in Python API.
  • Loading branch information
PeterPetrik committed Apr 19, 2018
1 parent 3b59ccc commit 50422a1
Show file tree
Hide file tree
Showing 62 changed files with 2,943 additions and 8 deletions.
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ IF(WITH_CORE)
SET (HAVE_GUI TRUE) # used in qgsconfig.h
ENDIF()

# try to configure and build MDAL support
SET (WITH_INTERNAL_MDAL TRUE CACHE BOOL "Determines whether MDAL support should be built")
IF (NOT WITH_INTERNAL_MDAL)
SET (MDAL_PREFIX "" CACHE PATH "Path to MDAL base directory")
ENDIF (NOT WITH_INTERNAL_MDAL)

# try to configure and build POSTGRESQL support
SET (WITH_POSTGRESQL TRUE CACHE BOOL "Determines whether POSTGRESQL support should be built")
IF (WITH_POSTGRESQL)
Expand Down Expand Up @@ -258,6 +264,10 @@ IF(WITH_CORE)
FIND_PACKAGE(Postgres) # PostgreSQL provider
ENDIF (WITH_POSTGRESQL)

IF (NOT WITH_INTERNAL_MDAL)
FIND_PACKAGE(MDAL REQUIRED) # MDAL provider
ENDIF (NOT WITH_INTERNAL_MDAL)

FIND_PACKAGE(SpatiaLite REQUIRED)

IF (NOT PROJ_FOUND OR NOT GEOS_FOUND OR NOT GDAL_FOUND)
Expand Down
32 changes: 32 additions & 0 deletions cmake/FindMDAL.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Find MDAL
# ~~~~~~~~~
# Copyright (c) 2018, Peter Petrik <zilolv at gmail dot com>
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
#
# Once run this will define:
# MDAL_FOUND - System has MDAL
# MDAL_INCLUDE_DIRS - The MDAL include directories
# MDAL_LIBRARIES - The libraries needed to use MDAL
# MDAL_DEFINITIONS - Compiler switches required for using MDAL

FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(PC_MDAL QUIET libmdal)
SET(MDAL_DEFINITIONS ${PC_MDAL_CFLAGS_OTHER})

FIND_PATH(MDAL_INCLUDE_DIR mdal.h
HINTS ${PC_MDAL_INCLUDEDIR} ${PC_MDAL_INCLUDE_DIRS} ${MDAL_PREFIX}/include
PATH_SUFFIXES libmdal )

FIND_LIBRARY(MDAL_LIBRARY NAMES mdal libmdal
HINTS ${PC_MDAL_LIBDIR} ${PC_MDAL_LIBRARY_DIRS} ${MDAL_PREFIX}/lib)

INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MDAL DEFAULT_MSG
MDAL_LIBRARY MDAL_INCLUDE_DIR)

MARK_AS_ADVANCED(MDAL_INCLUDE_DIR MDAL_LIBRARY )

SET(MDAL_LIBRARIES ${MDAL_LIBRARY} )
SET(MDAL_INCLUDE_DIRS ${MDAL_INCLUDE_DIR} )
91 changes: 91 additions & 0 deletions external/mdal/api/mdal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
MDAL - Mesh Data Abstraction Library (MIT License)
Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com)
*/

#ifndef MDAL_H
#define MDAL_H

#ifdef MDAL_STATIC
# define MDAL_EXPORT
#else
# if defined _WIN32 || defined __CYGWIN__
# ifdef mdal_EXPORTS
# ifdef __GNUC__
# define MDAL_EXPORT __attribute__ ((dllexport))
# else
# define MDAL_EXPORT __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
# endif
# else
# ifdef __GNUC__
# define MDAL_EXPORT __attribute__ ((dllimport))
# else
# define MDAL_EXPORT __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
# endif
# endif
# else
# if __GNUC__ >= 4
# define MDAL_EXPORT __attribute__ ((visibility ("default")))
# else
# define MDAL_EXPORT
# endif
# endif
#endif

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>

/* Statuses */
enum Status
{
None,
// Errors
Err_NotEnoughMemory,
Err_FileNotFound,
Err_UnknownFormat,
Err_IncompatibleMesh,
Err_InvalidData,
Err_MissingDriver,
// Warnings
Warn_UnsupportedElement,
Warn_InvalidElements,
Warn_ElementWithInvalidNode,
Warn_ElementNotUnique,
Warn_NodeNotUnique
};

/* Mesh */
typedef void *MeshH;

//! Return MDAL version
MDAL_EXPORT const char *MDAL_Version();

//! Return last status message
MDAL_EXPORT Status MDAL_LastStatus();

//! Load mesh file. On error see MDAL_LastStatus for error type This effectively loads whole mesh in-memory
MDAL_EXPORT MeshH MDAL_LoadMesh( const char *meshFile );
//! Close mesh, free the memory
MDAL_EXPORT void MDAL_CloseMesh( MeshH mesh );

//! Return vertex count for the mesh
MDAL_EXPORT size_t MDAL_M_vertexCount( MeshH mesh );
//! Return vertex X coord for the mesh
MDAL_EXPORT double MDAL_M_vertexXCoordinatesAt( MeshH mesh, size_t index );
//! Return vertex Y coord for the mesh
MDAL_EXPORT double MDAL_M_vertexYCoordinatesAt( MeshH mesh, size_t index );
//! Return face count for the mesh
MDAL_EXPORT size_t MDAL_M_faceCount( MeshH mesh );
//! Return number of vertices face consist of, e.g. 3 for triangle
MDAL_EXPORT size_t MDAL_M_faceVerticesCountAt( MeshH mesh, size_t index );
//! Return vertex index for face
MDAL_EXPORT size_t MDAL_M_faceVerticesIndexAt( MeshH mesh, size_t face_index, size_t vertex_index );

#ifdef __cplusplus
}
#endif

#endif //MDAL_H
204 changes: 204 additions & 0 deletions external/mdal/frmts/mdal_2dm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
MDAL - Mesh Data Abstraction Library (MIT License)
Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com)
*/

#include <stddef.h>
#include <iosfwd>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <cassert>

#include "mdal_2dm.hpp"
#include "mdal.h"
#include "mdal_utils.hpp"

MDAL::Loader2dm::Loader2dm( const std::string &meshFile ):
mMeshFile( meshFile )
{
}

MDAL::Mesh *MDAL::Loader2dm::load( Status *status )
{
if ( status ) *status = Status::None;

if ( !MDAL::fileExists( mMeshFile ) )
{
if ( status ) *status = Status::Err_FileNotFound;
return 0;
}

std::ifstream in( mMeshFile, std::ifstream::in );
std::string line;
if ( !std::getline( in, line ) || !startsWith( line, "MESH2D" ) )
{
if ( status ) *status = Status::Err_UnknownFormat;
return 0;
}

size_t elemCount = 0;
size_t nodeCount = 0;

// Find out how many nodes and elements are contained in the .2dm mesh file
while ( std::getline( in, line ) )
{
if ( startsWith( line, "E4Q" ) ||
startsWith( line, "E3T" ) )
{
elemCount++;
}
else if ( startsWith( line, "ND" ) )
{
nodeCount++;
}
else if ( startsWith( line, "E2L" ) ||
startsWith( line, "E3L" ) ||
startsWith( line, "E6T" ) ||
startsWith( line, "E8Q" ) ||
startsWith( line, "E9Q" ) )
{
if ( status ) *status = Status::Warn_UnsupportedElement;
elemCount += 1; // We still count them as elements
}
}

// Allocate memory
std::vector<Vertex> vertices( nodeCount );
std::vector<Face> faces( elemCount );

in.clear();
in.seekg( 0, std::ios::beg );

std::vector<std::string> chunks;

size_t elemIndex = 0;
size_t nodeIndex = 0;
std::map<size_t, size_t> elemIDtoIndex;
std::map<size_t, size_t> nodeIDtoIndex;

while ( std::getline( in, line ) )
{
if ( startsWith( line, "E4Q" ) )
{
chunks = split( line, " ", SplitBehaviour::SkipEmptyParts );
assert( elemIndex < elemCount );

size_t elemID = toSizeT( chunks[1] );

std::map<size_t, size_t>::iterator search = elemIDtoIndex.find( elemID );
if ( search != elemIDtoIndex.end() )
{
if ( status ) *status = Status::Warn_ElementNotUnique;
continue;
}
elemIDtoIndex[elemID] = elemIndex;
Face &face = faces[elemIndex];
face.resize( 4 );
// Right now we just store node IDs here - we will convert them to node indices afterwards
for ( size_t i = 0; i < 4; ++i )
face[i] = toSizeT( chunks[i + 2] );

elemIndex++;
}
else if ( startsWith( line, "E3T" ) )
{
chunks = split( line, " ", SplitBehaviour::SkipEmptyParts );
assert( elemIndex < elemCount );

size_t elemID = toSizeT( chunks[1] );

std::map<size_t, size_t>::iterator search = elemIDtoIndex.find( elemID );
if ( search != elemIDtoIndex.end() )
{
if ( status ) *status = Status::Warn_ElementNotUnique;
continue;
}
elemIDtoIndex[elemID] = elemIndex;
Face &face = faces[elemIndex];
face.resize( 3 );
// Right now we just store node IDs here - we will convert them to node indices afterwards
for ( size_t i = 0; i < 3; ++i )
{
face[i] = toSizeT( chunks[i + 2] );
}

elemIndex++;
}
else if ( startsWith( line, "E2L" ) ||
startsWith( line, "E3L" ) ||
startsWith( line, "E6T" ) ||
startsWith( line, "E8Q" ) ||
startsWith( line, "E9Q" ) )
{
// We do not yet support these elements
chunks = split( line, " ", SplitBehaviour::SkipEmptyParts );
assert( elemIndex < elemCount );

size_t elemID = toSizeT( chunks[1] );

std::map<size_t, size_t>::iterator search = elemIDtoIndex.find( elemID );
if ( search != elemIDtoIndex.end() )
{
if ( status ) *status = Status::Warn_ElementNotUnique;
continue;
}
elemIDtoIndex[elemID] = elemIndex;
assert( false ); //TODO mark element as unusable

elemIndex++;
}
else if ( startsWith( line, "ND" ) )
{
chunks = split( line, " ", SplitBehaviour::SkipEmptyParts );
size_t nodeID = toSizeT( chunks[1] );

std::map<size_t, size_t>::iterator search = nodeIDtoIndex.find( nodeID );
if ( search != nodeIDtoIndex.end() )
{
if ( status ) *status = Status::Warn_NodeNotUnique;
continue;
}
nodeIDtoIndex[nodeID] = nodeIndex;
assert( nodeIndex < nodeCount );
Vertex &vertex = vertices[nodeIndex];
vertex.x = toDouble( chunks[2] );
vertex.y = toDouble( chunks[3] );

nodeIndex++;
}
}

for ( std::vector<Face>::iterator it = faces.begin(); it != faces.end(); ++it )
{
Face &face = *it;
for ( Face::size_type nd = 0; nd < face.size(); ++nd )
{
size_t nodeID = face[nd];

std::map<size_t, size_t>::iterator ni2i = nodeIDtoIndex.find( nodeID );
if ( ni2i != nodeIDtoIndex.end() )
{
face[nd] = ni2i->second; // convert from ID to index
}
else
{
assert( false ); //TODO mark element as unusable

if ( status ) *status = Status::Warn_ElementWithInvalidNode;
}
}

//TODO check validity of the face
//check that we have distinct nodes
}

Mesh *mesh = new Mesh;
mesh->faces = faces;
mesh->vertices = vertices;

return mesh;
}
28 changes: 28 additions & 0 deletions external/mdal/frmts/mdal_2dm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
MDAL - Mesh Data Abstraction Library (MIT License)
Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com)
*/

#ifndef MDAL_2DM_HPP
#define MDAL_2DM_HPP

#include <string>

#include "mdal_defines.hpp"
#include "mdal.h"

namespace MDAL
{

class Loader2dm
{
public:
Loader2dm( const std::string &meshFile );
Mesh *load( Status *status );

private:
std::string mMeshFile;
};

} // namespace MDAL
#endif //MDAL_2DM_HPP
Loading

0 comments on commit 50422a1

Please sign in to comment.