mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Add a switch to QgsTessellator to avoid z handling and fallback to a
purely 2d tesselation Optimises tessellation when only a 2d tessellation is required by skipping unnecessary/unwanted calculations
This commit is contained in:
parent
8adc498421
commit
9b07075fae
@ -32,12 +32,14 @@ Optionally provides extrusion by adding triangles that serve as walls when extru
|
||||
Creates tessellator with a specified origin point of the world (in map coordinates)
|
||||
%End
|
||||
|
||||
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false );
|
||||
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false );
|
||||
%Docstring
|
||||
Creates tessellator with a specified ``bounds`` of input geometry coordinates.
|
||||
This constructor allows the tessellator to map input coordinates to a desirable range for numerically
|
||||
stability during calculations.
|
||||
|
||||
If ``noZ`` is ``True``, then a 2-dimensional tesselation only will be performed and all z coordinates will be ignored.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
|
@ -78,13 +78,14 @@ QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals,
|
||||
init();
|
||||
}
|
||||
|
||||
QgsTessellator::QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals, bool addBackFaces )
|
||||
QgsTessellator::QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals, bool addBackFaces, bool noZ )
|
||||
: mBounds( bounds )
|
||||
, mOriginX( mBounds.xMinimum() )
|
||||
, mOriginY( mBounds.yMinimum() )
|
||||
, mAddNormals( addNormals )
|
||||
, mInvertNormals( invertNormals )
|
||||
, mAddBackFaces( addBackFaces )
|
||||
, mNoZ( noZ )
|
||||
{
|
||||
init();
|
||||
}
|
||||
@ -227,7 +228,7 @@ struct float_pair_hash
|
||||
}
|
||||
};
|
||||
|
||||
static void _ringToPoly2tri( const QgsLineString *ring, std::vector<p2t::Point *> &polyline, QHash<p2t::Point *, float> &zHash )
|
||||
static void _ringToPoly2tri( const QgsLineString *ring, std::vector<p2t::Point *> &polyline, QHash<p2t::Point *, float> *zHash )
|
||||
{
|
||||
const int pCount = ring->numPoints();
|
||||
|
||||
@ -242,7 +243,6 @@ static void _ringToPoly2tri( const QgsLineString *ring, std::vector<p2t::Point *
|
||||
{
|
||||
const float x = *srcXData++;
|
||||
const float y = *srcYData++;
|
||||
const float z = *srcZData++;
|
||||
|
||||
auto res = foundPoints.insert( std::make_pair( x, y ) );
|
||||
if ( !res.second )
|
||||
@ -253,7 +253,10 @@ static void _ringToPoly2tri( const QgsLineString *ring, std::vector<p2t::Point *
|
||||
|
||||
p2t::Point *pt2 = new p2t::Point( x, y );
|
||||
polyline.push_back( pt2 );
|
||||
zHash[pt2] = z;
|
||||
if ( zHash )
|
||||
{
|
||||
( *zHash )[pt2] = *srcZData++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,7 +424,7 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
{
|
||||
const QgsLineString *exterior = qgsgeometry_cast< const QgsLineString * >( polygon.exteriorRing() );
|
||||
|
||||
const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals );
|
||||
const QVector3D pNormal = !mNoZ ? _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ) : QVector3D();
|
||||
const int pCount = exterior->numPoints();
|
||||
|
||||
if ( pCount == 4 && polygon.numInteriorRings() == 0 )
|
||||
@ -429,10 +432,10 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
// polygon is a triangle - write vertices to the output data array without triangulation
|
||||
const double *xData = exterior->xData();
|
||||
const double *yData = exterior->yData();
|
||||
const double *zData = exterior->zData();
|
||||
const double *zData = !mNoZ ? exterior->zData() : nullptr;
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
mData << *xData++ - mOriginX << *zData++ << - *yData++ + mOriginY;
|
||||
mData << *xData++ - mOriginX << ( mNoZ ? 0 : *zData++ ) << - *yData++ + mOriginY;
|
||||
if ( mAddNormals )
|
||||
mData << pNormal.x() << pNormal.z() << - pNormal.y();
|
||||
}
|
||||
@ -442,7 +445,7 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
// the same triangle with reversed order of coordinates and inverted normal
|
||||
for ( int i = 2; i >= 0; i-- )
|
||||
{
|
||||
mData << exterior->xAt( i ) - mOriginX << exterior->zAt( i ) << - exterior->yAt( i ) + mOriginY;
|
||||
mData << exterior->xAt( i ) - mOriginX << ( mNoZ ? 0 : exterior->zAt( i ) ) << - exterior->yAt( i ) + mOriginY;
|
||||
if ( mAddNormals )
|
||||
mData << -pNormal.x() << -pNormal.z() << pNormal.y();
|
||||
}
|
||||
@ -450,11 +453,11 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !qgsDoubleNear( pNormal.length(), 1, 0.001 ) )
|
||||
if ( !mNoZ && !qgsDoubleNear( pNormal.length(), 1, 0.001 ) )
|
||||
return; // this should not happen - pNormal should be normalized to unit length
|
||||
|
||||
std::unique_ptr<QMatrix4x4> toNewBase, toOldBase;
|
||||
if ( pNormal != QVector3D( 0, 0, 1 ) )
|
||||
if ( !mNoZ && pNormal != QVector3D( 0, 0, 1 ) )
|
||||
{
|
||||
// this is not a horizontal plane - need to reproject the polygon to a new base so that
|
||||
// we can do the triangulation in a plane
|
||||
@ -524,7 +527,7 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
|
||||
// polygon exterior
|
||||
std::vector<p2t::Point *> polyline;
|
||||
_ringToPoly2tri( qgsgeometry_cast< const QgsLineString * >( polygonNew->exteriorRing() ), polyline, z );
|
||||
_ringToPoly2tri( qgsgeometry_cast< const QgsLineString * >( polygonNew->exteriorRing() ), polyline, mNoZ ? nullptr : &z );
|
||||
polylinesToDelete << polyline;
|
||||
|
||||
std::unique_ptr<p2t::CDT> cdt( new p2t::CDT( polyline ) );
|
||||
@ -535,7 +538,7 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
std::vector<p2t::Point *> holePolyline;
|
||||
const QgsLineString *hole = qgsgeometry_cast< const QgsLineString *>( polygonNew->interiorRing( i ) );
|
||||
|
||||
_ringToPoly2tri( hole, holePolyline, z );
|
||||
_ringToPoly2tri( hole, holePolyline, mNoZ ? nullptr : &z );
|
||||
|
||||
cdt->AddHole( holePolyline );
|
||||
polylinesToDelete << holePolyline;
|
||||
@ -555,12 +558,12 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
for ( int j = 0; j < 3; ++j )
|
||||
{
|
||||
p2t::Point *p = t->GetPoint( j );
|
||||
QVector4D pt( p->x, p->y, z[p], 0 );
|
||||
QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
|
||||
if ( toOldBase )
|
||||
pt = *toOldBase * pt;
|
||||
const double fx = ( pt.x() / scaleX ) - mOriginX + pt0.x();
|
||||
const double fy = ( pt.y() / scaleY ) - mOriginY + pt0.y();
|
||||
const double fz = pt.z() + extrusionHeight + pt0.z();
|
||||
const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.z() );
|
||||
mData << fx << fz << -fy;
|
||||
if ( mAddNormals )
|
||||
mData << pNormal.x() << pNormal.z() << - pNormal.y();
|
||||
@ -572,12 +575,12 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
|
||||
for ( int j = 2; j >= 0; --j )
|
||||
{
|
||||
p2t::Point *p = t->GetPoint( j );
|
||||
QVector4D pt( p->x, p->y, z[p], 0 );
|
||||
QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
|
||||
if ( toOldBase )
|
||||
pt = *toOldBase * pt;
|
||||
const double fx = ( pt.x() / scaleX ) - mOriginX + pt0.x();
|
||||
const double fy = ( pt.y() / scaleY ) - mOriginY + pt0.y();
|
||||
const double fz = pt.z() + extrusionHeight + pt0.z();
|
||||
const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.z() );
|
||||
mData << fx << fz << -fy;
|
||||
if ( mAddNormals )
|
||||
mData << -pNormal.x() << -pNormal.z() << pNormal.y();
|
||||
|
@ -47,9 +47,12 @@ class CORE_EXPORT QgsTessellator
|
||||
* Creates tessellator with a specified \a bounds of input geometry coordinates.
|
||||
* This constructor allows the tessellator to map input coordinates to a desirable range for numerically
|
||||
* stability during calculations.
|
||||
*
|
||||
* If \a noZ is TRUE, then a 2-dimensional tesselation only will be performed and all z coordinates will be ignored.
|
||||
*
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false );
|
||||
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false );
|
||||
|
||||
//! Tessellates a triangle and adds its vertex entries to the output data array
|
||||
void addPolygon( const QgsPolygon &polygon, float extrusionHeight );
|
||||
@ -82,6 +85,7 @@ class CORE_EXPORT QgsTessellator
|
||||
bool mAddBackFaces = false;
|
||||
QVector<float> mData;
|
||||
int mStride;
|
||||
bool mNoZ = false;
|
||||
};
|
||||
|
||||
#endif // QGSTESSELLATOR_H
|
||||
|
@ -101,6 +101,7 @@ bool checkTriangleOutput( const QVector<float> &data, bool withNormals, const QL
|
||||
TriangleCoords out( dataRaw, withNormals );
|
||||
if ( exp != out )
|
||||
{
|
||||
qDebug() << i;
|
||||
qDebug() << "expected:";
|
||||
exp.dump();
|
||||
qDebug() << "got:";
|
||||
@ -136,6 +137,7 @@ class TestQgsTessellator : public QObject
|
||||
void testCrashSelfIntersection();
|
||||
void testCrashEmptyPolygon();
|
||||
void testBoundsScaling();
|
||||
void testNoZ();
|
||||
|
||||
private:
|
||||
};
|
||||
@ -369,6 +371,21 @@ void TestQgsTessellator::testBoundsScaling()
|
||||
QVERIFY( checkTriangleOutput( t2.data(), true, tc ) );
|
||||
}
|
||||
|
||||
void TestQgsTessellator::testNoZ()
|
||||
{
|
||||
// test tessellation with no z support
|
||||
QgsPolygon polygonZ;
|
||||
polygonZ.fromWkt( "POLYGONZ((1 1 1, 2 1 1, 3 2 1, 1 2 1, 1 1 1))" );
|
||||
|
||||
QList<TriangleCoords> tc;
|
||||
tc << TriangleCoords( QVector3D( 0, 1, 0 ), QVector3D( 1, 0, 0 ), QVector3D( 2, 1, 0 ) );
|
||||
tc << TriangleCoords( QVector3D( 0, 1, 0 ), QVector3D( 0, 0, 0 ), QVector3D( 1, 0, 0 ) );
|
||||
|
||||
QgsTessellator t( polygonZ.boundingBox(), false, false, false, true );
|
||||
t.addPolygon( polygonZ, 0 );
|
||||
QVERIFY( checkTriangleOutput( t.data(), false, tc ) );
|
||||
}
|
||||
|
||||
|
||||
QGSTEST_MAIN( TestQgsTessellator )
|
||||
#include "testqgstessellator.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user