Skip to content

Commit

Permalink
Fixes to make tessellation of polygons without Z work again
Browse files Browse the repository at this point in the history
+ initial work on unit testing of tessellator
  • Loading branch information
wonder-sk committed Oct 10, 2017
1 parent 59fa541 commit 2c24690
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 4 deletions.
19 changes: 16 additions & 3 deletions src/3d/qgstessellator.cpp
Expand Up @@ -111,24 +111,35 @@ static void _makeWalls( const QgsCurve &ring, bool ccw, float extrusionHeight, Q
}
}

static QVector3D _calculateNormal( const QgsCurve *curve )
static QVector3D _calculateNormal( const QgsCurve *curve, bool &hasValidZ )
{
// Calculate the polygon's normal vector, based on Newell's method
// https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
QgsVertexId::VertexType vt;
QgsPoint pt1, pt2;
QVector3D normal( 0, 0, 0 );

// assume that Z coordinates are not present
hasValidZ = false;

for ( int i = 0; i < curve->numPoints() - 1; i++ )
{
curve->pointAt( i, pt1, vt );
curve->pointAt( i + 1, pt2, vt );

if ( qIsNaN( pt1.z() ) || qIsNaN( pt2.z() ) )
continue;

hasValidZ = true;

normal.setX( normal.x() + ( pt1.y() - pt2.y() ) * ( pt1.z() + pt2.z() ) );
normal.setY( normal.y() + ( pt1.z() - pt2.z() ) * ( pt1.x() + pt2.x() ) );
normal.setZ( normal.z() + ( pt1.x() - pt2.x() ) * ( pt1.y() + pt2.y() ) );
}

if ( !hasValidZ )
return QVector3D( 0, 0, 1 );

normal.normalize();

return normal;
Expand Down Expand Up @@ -183,7 +194,8 @@ void QgsTessellator::addPolygon( const QgsPolygonV2 &polygon, float extrusionHei
QgsVertexId::VertexType vt;
QgsPoint pt;

const QVector3D pNormal = _calculateNormal( exterior );
bool hasValidZ;
const QVector3D pNormal = _calculateNormal( exterior, hasValidZ );
const int pCount = exterior->numPoints();

// Polygon is a triangle
Expand All @@ -207,7 +219,8 @@ void QgsTessellator::addPolygon( const QgsPolygonV2 &polygon, float extrusionHei
}

const QgsPoint ptFirst( exterior->startPoint() );
QVector3D pOrigin( ptFirst.x(), ptFirst.y(), ptFirst.z() ), pXVector;
QVector3D pOrigin( ptFirst.x(), ptFirst.y(), qIsNaN( ptFirst.z() ) ? 0 : ptFirst.z() );
QVector3D pXVector;
// Here we define the two perpendicular vectors that define the local
// 2D space on the plane. They will act as axis for which we will
// calculate the projection coordinates of a 3D point to the plane.
Expand Down
4 changes: 3 additions & 1 deletion src/3d/qgstessellator.h
Expand Up @@ -16,6 +16,8 @@
#ifndef QGSTESSELLATOR_H
#define QGSTESSELLATOR_H

#include "qgis_3d.h"

class QgsPolygonV2;

#include <QVector>
Expand All @@ -32,7 +34,7 @@ class QgsPolygonV2;
*
* \since QGIS 3.0
*/
class QgsTessellator
class _3D_EXPORT QgsTessellator
{
public:
//! Creates tessellator with a specified origin point of the world (in map coordinates)
Expand Down
71 changes: 71 additions & 0 deletions tests/src/3d/CMakeLists.txt
@@ -0,0 +1,71 @@
# Standard includes and utils to compile into all tests.
SET (util_SRCS)


#####################################################
# Don't forget to include output directory, otherwise
# the UI file won't be wrapped!
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/tests/core #for render checker class
${CMAKE_SOURCE_DIR}/src/3d
${CMAKE_SOURCE_DIR}/src/core
${CMAKE_SOURCE_DIR}/src/core/expression
${CMAKE_SOURCE_DIR}/src/core/auth
${CMAKE_SOURCE_DIR}/src/core/composer
${CMAKE_SOURCE_DIR}/src/core/geometry
${CMAKE_SOURCE_DIR}/src/core/layout
${CMAKE_SOURCE_DIR}/src/core/metadata
${CMAKE_SOURCE_DIR}/src/core/raster
${CMAKE_SOURCE_DIR}/src/core/symbology
${CMAKE_SOURCE_DIR}/src/core/fieldformatter
${CMAKE_SOURCE_DIR}/src/test
${CMAKE_SOURCE_DIR}/src/native


${CMAKE_BINARY_DIR}/src/core
${CMAKE_BINARY_DIR}/src/3d
${CMAKE_BINARY_DIR}/src/ui
${CMAKE_BINARY_DIR}/src/native
${CMAKE_CURRENT_BINARY_DIR}
)
INCLUDE_DIRECTORIES(SYSTEM
${QT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${PROJ_INCLUDE_DIR}
${GEOS_INCLUDE_DIR}
)

#note for tests we should not include the moc of our
#qtests in the executable file list as the moc is
#directly included in the sources
#and should not be compiled twice. Trying to include
#them in will cause an error at build time
#############################################################
# Tests:

MACRO (ADD_QGIS_TEST testname testsrc)
SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS})
SET(qgis_${testname}_MOC_CPPS ${testsrc})
ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS})
SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES AUTOMOC TRUE)
SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES AUTORCC TRUE)
TARGET_LINK_LIBRARIES(qgis_${testname}
${QT_QTXML_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTSVG_LIBRARY}
${QT_QTTEST_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${PROJ_LIBRARY}
${GEOS_LIBRARY}
${GDAL_LIBRARY}
qgis_core
qgis_3d)
ADD_TEST(qgis_${testname} ${CMAKE_CURRENT_BINARY_DIR}/../../../output/bin/qgis_${testname} -maxwarnings 10000)
TARGET_LINK_LIBRARIES(qgis_${testname} qgis_native)
ADD_TEST(qgis_${testname} ${CMAKE_CURRENT_BINARY_DIR}/../../../output/bin/qgis_${testname})
#SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES
# INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${QGIS_LIB_DIR}
# INSTALL_RPATH_USE_LINK_PATH true )
ENDMACRO (ADD_QGIS_TEST)

ADD_QGIS_TEST(tessellatortest testqgstessellator.cpp)
69 changes: 69 additions & 0 deletions tests/src/3d/testqgstessellator.cpp
@@ -0,0 +1,69 @@
/***************************************************************************
testqgstessellator.cpp
----------------------
Date : October 2017
Copyright : (C) 2017 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgstest.h"

#include "qgspoint.h"
#include "qgspolygon.h"
#include "qgstessellator.h"


/**
* \ingroup UnitTests
* This is a unit test for the node tool
*/
class TestQgsTessellator : public QObject
{
Q_OBJECT
public:
TestQgsTessellator();

private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.

void testBasic();

private:
};

TestQgsTessellator::TestQgsTessellator() = default;


//runs before all tests
void TestQgsTessellator::initTestCase()
{
}

//runs after all tests
void TestQgsTessellator::cleanupTestCase()
{
}

void TestQgsTessellator::testBasic()
{
QgsPolygonV2 polygon;
polygon.fromWkt( "POLYGON((1 1, 2 1, 3 2, 1 2, 1 1))" );

QgsTessellator t( 0, 0, false );
t.addPolygon( polygon, 0 );

QVector<float> polygonData = t.data();
QCOMPARE( polygonData.count(), 2 * 3 * 3 ); // two triangles (3 points with x/y/z coords)
}


QGSTEST_MAIN( TestQgsTessellator )
#include "testqgstessellator.moc"
3 changes: 3 additions & 0 deletions tests/src/CMakeLists.txt
Expand Up @@ -33,6 +33,9 @@ IF (ENABLE_TESTS)
IF (WITH_GUI)
ADD_SUBDIRECTORY(gui)
ENDIF (WITH_GUI)
IF (WITH_3D)
ADD_SUBDIRECTORY(3d)
ENDIF (WITH_3D)
ADD_SUBDIRECTORY(analysis)
ADD_SUBDIRECTORY(providers)
IF (WITH_DESKTOP)
Expand Down
2 changes: 2 additions & 0 deletions tests/src/app/CMakeLists.txt
Expand Up @@ -88,7 +88,9 @@ ENDMACRO (ADD_QGIS_TEST)
#############################################################
# Tests:

IF (WITH_BINDINGS)
ADD_QGIS_TEST(apppythontest testqgisapppython.cpp)
ENDIF ()
ADD_QGIS_TEST(qgisappclipboard testqgisappclipboard.cpp)
ADD_QGIS_TEST(attributetabletest testqgsattributetable.cpp)
ADD_QGIS_TEST(fieldcalculatortest testqgsfieldcalculator.cpp)
Expand Down

0 comments on commit 2c24690

Please sign in to comment.