35 #include <unordered_set> 
   37 static std::pair<float, float> rotateCoords( 
float x, 
float y, 
float origin_x, 
float origin_y, 
float r )
 
   39   r = qDegreesToRadians( r );
 
   40   float x0 = x - origin_x, y0 = y - origin_y;
 
   44   float x1 = origin_x + x0 * qCos( r ) - y0 * qSin( r );
 
   45   float y1 = origin_y + x0 * qSin( r ) + y0 * qCos( r );
 
   46   return std::make_pair( x1, y1 );
 
   49 static void make_quad( 
float x0, 
float y0, 
float z0, 
float x1, 
float y1, 
float z1, 
float height, QVector<float> &data, 
bool addNormals, 
bool addTextureCoords, 
float textureRotation )
 
   52   float dy = -( y1 - y0 );
 
   55   QVector3D vn( -dy, 0, dx );
 
   64   QVector<double> textureCoordinates;
 
   65   textureCoordinates.reserve( 12 );
 
   67   if ( fabsf( dy ) <= fabsf( dx ) )
 
   98   textureCoordinates.push_back( u0 );
 
   99   textureCoordinates.push_back( v0 );
 
  101   textureCoordinates.push_back( u1 );
 
  102   textureCoordinates.push_back( v1 );
 
  104   textureCoordinates.push_back( u2 );
 
  105   textureCoordinates.push_back( v2 );
 
  107   textureCoordinates.push_back( u2 );
 
  108   textureCoordinates.push_back( v2 );
 
  110   textureCoordinates.push_back( u1 );
 
  111   textureCoordinates.push_back( v1 );
 
  113   textureCoordinates.push_back( u3 );
 
  114   textureCoordinates.push_back( v3 );
 
  116   for ( 
int i = 0; i < textureCoordinates.size(); i += 2 )
 
  118     std::pair<float, float> rotated = rotateCoords( textureCoordinates[i], textureCoordinates[i + 1], 0, 0, textureRotation );
 
  119     textureCoordinates[i] = rotated.first;
 
  120     textureCoordinates[i + 1] = rotated.second;
 
  125   data << x0 << z0 + height << -y0;
 
  127     data << vn.x() << vn.y() << vn.z();
 
  128   if ( addTextureCoords )
 
  129     data << textureCoordinates[0] << textureCoordinates[1];
 
  131   data << x1 << z1 + height << -y1;
 
  133     data << vn.x() << vn.y() << vn.z();
 
  134   if ( addTextureCoords )
 
  135     data << textureCoordinates[2] << textureCoordinates[3];
 
  137   data << x0 << z0 << -y0;
 
  139     data << vn.x() << vn.y() << vn.z();
 
  140   if ( addTextureCoords )
 
  141     data << textureCoordinates[4] << textureCoordinates[5];
 
  145   data << x0 << z0 << -y0;
 
  147     data << vn.x() << vn.y() << vn.z();
 
  148   if ( addTextureCoords )
 
  149     data << textureCoordinates[6] << textureCoordinates[7];
 
  151   data << x1 << z1 + height << -y1;
 
  153     data << vn.x() << vn.y() << vn.z();
 
  154   if ( addTextureCoords )
 
  155     data << textureCoordinates[8] << textureCoordinates[9];
 
  157   data << x1 << z1 << -y1;
 
  159     data << vn.x() << vn.y() << vn.z();
 
  160   if ( addTextureCoords )
 
  161     data << textureCoordinates[10] << textureCoordinates[11];
 
  166                                 bool addTextureCoords, 
int facade, 
float textureRotation )
 
  167   : mOriginX( originX )
 
  168   , mOriginY( originY )
 
  169   , mAddNormals( addNormals )
 
  170   , mInvertNormals( invertNormals )
 
  171   , mAddBackFaces( addBackFaces )
 
  172   , mAddTextureCoords( addTextureCoords )
 
  174   , mTessellatedFacade( facade )
 
  175   , mTextureRotation( textureRotation )
 
  181                                 bool addTextureCoords, 
int facade, 
float textureRotation )
 
  183   , mOriginX( mBounds.xMinimum() )
 
  184   , mOriginY( mBounds.yMinimum() )
 
  185   , mAddNormals( addNormals )
 
  186   , mInvertNormals( invertNormals )
 
  187   , mAddBackFaces( addBackFaces )
 
  188   , mAddTextureCoords( addTextureCoords )
 
  190   , mTessellatedFacade( facade )
 
  191   , mTextureRotation( textureRotation )
 
  196 void QgsTessellator::init()
 
  198   mStride = 3 * 
sizeof( float );
 
  200     mStride += 3 * 
sizeof( float );
 
  201   if ( mAddTextureCoords )
 
  202     mStride += 2 * 
sizeof( float );
 
  205 static bool _isRingCounterClockWise( 
const QgsCurve &ring )
 
  212   for ( 
int i = 1; i < count + 1; ++i )
 
  214     ring.
pointAt( i % count, pt, vt );
 
  215     a += ptPrev.
x() * pt.
y() - ptPrev.
y() * pt.
x();
 
  221 static void _makeWalls( 
const QgsLineString &ring, 
bool ccw, 
float extrusionHeight, QVector<float> &data,
 
  222                         bool addNormals, 
bool addTextureCoords, 
double originX, 
double originY, 
float textureRotation )
 
  227   bool is_counter_clockwise = _isRingCounterClockWise( ring );
 
  231   for ( 
int i = 1; i < ring.
numPoints(); ++i )
 
  233     pt = ring.
pointN( is_counter_clockwise == ccw ? i : ring.
numPoints() - i - 1 );
 
  234     float x0 = ptPrev.
x() - originX, y0 = ptPrev.
y() - originY;
 
  235     float x1 = pt.
x() - originX, y1 = pt.
y() - originY;
 
  236     float z0 = std::isnan( ptPrev.
z() ) ? 0 : ptPrev.
z();
 
  237     float z1 = std::isnan( pt.
z() ) ? 0 : pt.
z();
 
  240     make_quad( x0, y0, z0, x1, y1, z1, extrusionHeight, data, addNormals, addTextureCoords, textureRotation );
 
  245 static QVector3D _calculateNormal( 
const QgsLineString *curve, 
double originX, 
double originY, 
bool invertNormal )
 
  250     return QVector3D( 0, 0, 1 );
 
  258   for ( 
int i = 1; i < curve->
numPoints(); i++ )
 
  261     if ( pt1.
z() != pt2.
z() )
 
  268     return QVector3D( 0, 0, 1 );
 
  275   double nx = 0, ny = 0, nz = 0;
 
  279   pt1.
setX( pt1.
x() - originX );
 
  280   pt1.
setY( pt1.
y() - originY );
 
  281   for ( 
int i = 1; i < curve->
numPoints(); i++ )
 
  284     pt2.
setX( pt2.
x() - originX );
 
  285     pt2.
setY( pt2.
y() - originY );
 
  287     if ( std::isnan( pt1.
z() ) || std::isnan( pt2.
z() ) )
 
  290     nx += ( pt1.
y() - pt2.
y() ) * ( pt1.
z() + pt2.
z() );
 
  291     ny += ( pt1.
z() - pt2.
z() ) * ( pt1.
x() + pt2.
x() );
 
  292     nz += ( pt1.
x() - pt2.
x() ) * ( pt1.
y() + pt2.
y() );
 
  297   QVector3D normal( nx, ny, nz );
 
  305 static void _normalVectorToXYVectors( 
const QVector3D &pNormal, QVector3D &pXVector, QVector3D &pYVector )
 
  310   if ( pNormal.z() > 0.001 || pNormal.z() < -0.001 )
 
  312     pXVector = QVector3D( 1, 0, -pNormal.x() / pNormal.z() );
 
  314   else if ( pNormal.y() > 0.001 || pNormal.y() < -0.001 )
 
  316     pXVector = QVector3D( 1, -pNormal.x() / pNormal.y(), 0 );
 
  320     pXVector = QVector3D( -pNormal.y() / pNormal.x(), 1, 0 );
 
  322   pXVector.normalize();
 
  323   pYVector = QVector3D::normal( pNormal, pXVector );
 
  328   std::size_t 
operator()( 
const std::pair<float, float> pair )
 const 
  330     std::size_t h1 = std::hash<float>()( pair.first );
 
  331     std::size_t h2 = std::hash<float>()( pair.second );
 
  337 static void _ringToPoly2tri( 
const QgsLineString *ring, std::vector<p2t::Point *> &polyline, QHash<p2t::Point *, float> *zHash )
 
  341   polyline.reserve( pCount );
 
  343   const double *srcXData = ring->
xData();
 
  344   const double *srcYData = ring->
yData();
 
  345   const double *srcZData = ring->
zData();
 
  346   std::unordered_set<std::pair<float, float>, 
float_pair_hash> foundPoints;
 
  348   for ( 
int i = 0; i < pCount - 1; ++i )
 
  350     const float x = *srcXData++;
 
  351     const float y = *srcYData++;
 
  353     auto res = foundPoints.insert( std::make_pair( x, y ) );
 
  360     p2t::Point *pt2 = 
new p2t::Point( x, y );
 
  361     polyline.push_back( pt2 );
 
  364       ( *zHash )[pt2] = *srcZData++;
 
  372   const double exp = 1e10;   
 
  373   return round( x * exp ) / exp;
 
  377 static QgsCurve *_transform_ring_to_new_base( 
const QgsLineString &curve, 
const QgsPoint &pt0, 
const QMatrix4x4 *toNewBase, 
const float scale )
 
  386   double *xData = x.data();
 
  387   double *yData = y.data();
 
  388   double *zData = z.data();
 
  390   const double *srcXData = curve.
xData();
 
  391   const double *srcYData = curve.
yData();
 
  392   const double *srcZData = curve.
is3D() ? curve.
zData() : 
nullptr;
 
  394   for ( 
int i = 0; i < count; ++i )
 
  396     QVector4D v( *srcXData++ - pt0.
x(),
 
  397                  *srcYData++ - pt0.
y(),
 
  398                  srcZData ? *srcZData++ - pt0.
z() : 0,
 
  401       v = toNewBase->map( v );
 
  404     v.setX( v.x() * scale );
 
  405     v.setY( v.y() * scale );
 
  425 static QgsPolygon *_transform_polygon_to_new_base( 
const QgsPolygon &polygon, 
const QgsPoint &pt0, 
const QMatrix4x4 *toNewBase, 
const float scale )
 
  428   p->
setExteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ), pt0, toNewBase, scale ) );
 
  430     p->
addInteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ), pt0, toNewBase, scale ) );
 
  434 static bool _check_intersecting_rings( 
const QgsPolygon &polygon )
 
  436   std::vector< std::unique_ptr< QgsGeometryEngine > > ringEngines;
 
  446   for ( 
const std::unique_ptr< QgsGeometryEngine > &ring : ringEngines )
 
  448     if ( !ring->isSimple() )
 
  469   if ( ringEngines.size() > 1 )
 
  471     for ( 
size_t i = 0; i < ringEngines.size(); ++i )
 
  473       std::unique_ptr< QgsGeometryEngine > &first = ringEngines.at( i );
 
  475         first->prepareGeometry();
 
  482       for ( 
int interiorRing = 
static_cast< int >( i ); interiorRing < polygon.
numInteriorRings(); ++interiorRing )
 
  484         if ( first->intersects( polygon.
interiorRing( interiorRing ) ) )
 
  497   std::vector< const QgsLineString * > rings;
 
  499   rings.emplace_back( qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ) );
 
  501     rings.emplace_back( qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ) );
 
  506     if ( numPoints <= 1 )
 
  509     const double *srcXData = ring->
xData();
 
  510     const double *srcYData = ring->
yData();
 
  511     double x0 = *srcXData++;
 
  512     double y0 = *srcYData++;
 
  513     for ( 
int i = 1; i < numPoints; ++i )
 
  515       double x1 = *srcXData++;
 
  516       double y1 = *srcYData++;
 
  517       double d = ( x0 - x1 ) * ( x0 - x1 ) + ( y0 - y1 ) * ( y0 - y1 );
 
  525   return min_d != 1e20 ? std::sqrt( min_d ) : 1e20;
 
  534   const QVector3D pNormal = !mNoZ ? _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ) : QVector3D();
 
  535   const int pCount = exterior->
numPoints();
 
  539   float zMin = std::numeric_limits<float>::max();
 
  540   float zMax = std::numeric_limits<float>::min();
 
  542   const float scale = mBounds.
isNull() ? 1.0 : std::max( 10000.0 / mBounds.
width(), 10000.0 / mBounds.
height() );
 
  544   std::unique_ptr<QMatrix4x4> toNewBase, toOldBase;
 
  546   std::unique_ptr<QgsPolygon> polygonNew;
 
  547   auto rotatePolygonToXYPlane = [&]()
 
  549     if ( !mNoZ && pNormal != QVector3D( 0, 0, 1 ) )
 
  553       QVector3D pXVector, pYVector;
 
  554       _normalVectorToXYVectors( pNormal, pXVector, pYVector );
 
  559       toNewBase.reset( 
new QMatrix4x4(
 
  560                          pXVector.x(), pXVector.y(), pXVector.z(), 0,
 
  561                          pYVector.x(), pYVector.y(), pYVector.z(), 0,
 
  562                          pNormal.x(), pNormal.y(), pNormal.z(), 0,
 
  566       toOldBase.reset( 
new QMatrix4x4( toNewBase->transposed() ) );
 
  575     polygonNew.reset( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get(), scale ) );
 
  581   QVector3D upVector( 0, 0, 1 );
 
  582   float pNormalUpVectorDotProduct = QVector3D::dotProduct( upVector, pNormal );
 
  583   float radsBetwwenUpNormal = qAcos( pNormalUpVectorDotProduct );
 
  585   float detectionDelta = qDegreesToRadians( 10.0f );
 
  587   if ( radsBetwwenUpNormal > M_PI_2 - detectionDelta && radsBetwwenUpNormal < M_PI_2 + detectionDelta ) facade = 1;
 
  588   else if ( radsBetwwenUpNormal > - M_PI_2 - detectionDelta && radsBetwwenUpNormal < -M_PI_2 + detectionDelta ) facade = 1;
 
  591   if ( pCount == 4 && polygon.
numInteriorRings() == 0 && ( mTessellatedFacade & facade ) )
 
  594     if ( mAddTextureCoords )
 
  596       rotatePolygonToXYPlane();
 
  597       triangle = qgsgeometry_cast< QgsLineString * >( polygonNew->exteriorRing() );
 
  598       Q_ASSERT( polygonNew->exteriorRing()->numPoints() >= 3 );
 
  602     const double *xData = exterior->
xData();
 
  603     const double *yData = exterior->
yData();
 
  604     const double *zData = !mNoZ ? exterior->
zData() : 
nullptr;
 
  605     for ( 
int i = 0; i < 3; i++ )
 
  607       float z = !zData ? 0 : *zData;
 
  613       mData << *xData - mOriginX << z << - *yData + mOriginY;
 
  615         mData << pNormal.x() << pNormal.z() << - pNormal.y();
 
  616       if ( mAddTextureCoords )
 
  618         std::pair<float, float> p( triangle->
xAt( i ), triangle->
yAt( i ) );
 
  621           p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
 
  623         else if ( facade & 2 )
 
  625           p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
 
  627         mData << p.first << p.second;
 
  638       for ( 
int i = 2; i >= 0; i-- )
 
  640         mData << exterior->
xAt( i ) - mOriginX << ( mNoZ ? 0 : exterior->
zAt( i ) ) << - exterior->
yAt( i ) + mOriginY;
 
  642           mData << -pNormal.x() << -pNormal.z() << pNormal.y();
 
  643         if ( mAddTextureCoords )
 
  645           std::pair<float, float> p( triangle->
xAt( i ), triangle->
yAt( i ) );
 
  648             p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
 
  650           else if ( facade & 2 )
 
  652             p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
 
  654           mData << p.first << p.second;
 
  659   else if ( mTessellatedFacade & facade )
 
  662     rotatePolygonToXYPlane();
 
  671       if ( polygonSimplified.
isNull() )
 
  676       const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.
constGet() );
 
  681         QgsMessageLog::logMessage( QObject::tr( 
"geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr( 
"3D" ) );
 
  686         polygonNew.reset( polygonSimplifiedData->
clone() );
 
  690     if ( !_check_intersecting_rings( *polygonNew ) )
 
  693       QgsMessageLog::logMessage( QObject::tr( 
"polygon rings self-intersect or intersect each other - skipping" ), QObject::tr( 
"3D" ) );
 
  697     QList< std::vector<p2t::Point *> > polylinesToDelete;
 
  698     QHash<p2t::Point *, float> z;
 
  701     std::vector<p2t::Point *> polyline;
 
  702     _ringToPoly2tri( qgsgeometry_cast< const QgsLineString * >( polygonNew->exteriorRing() ), polyline, mNoZ ? 
nullptr : &z );
 
  703     polylinesToDelete << polyline;
 
  705     std::unique_ptr<p2t::CDT> cdt( 
new p2t::CDT( polyline ) );
 
  708     for ( 
int i = 0; i < polygonNew->numInteriorRings(); ++i )
 
  710       std::vector<p2t::Point *> holePolyline;
 
  711       const QgsLineString *hole = qgsgeometry_cast< const QgsLineString *>( polygonNew->interiorRing( i ) );
 
  713       _ringToPoly2tri( hole, holePolyline, mNoZ ? 
nullptr : &z );
 
  715       cdt->AddHole( holePolyline );
 
  716       polylinesToDelete << holePolyline;
 
  724       std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
 
  726       mData.reserve( mData.size() + 3 * triangles.size() * ( 
stride() / 
sizeof( 
float ) ) );
 
  727       for ( 
size_t i = 0; i < triangles.size(); ++i )
 
  729         p2t::Triangle *t = triangles[i];
 
  730         for ( 
int j = 0; j < 3; ++j )
 
  732           p2t::Point *p = t->GetPoint( j );
 
  733           QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
 
  735             pt = *toOldBase * pt;
 
  736           const double fx = ( pt.x() / scale ) - mOriginX + pt0.
x();
 
  737           const double fy = ( pt.y() / scale ) - mOriginY + pt0.
y();
 
  738           const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
 
  744           mData << fx << fz << -fy;
 
  746             mData << pNormal.x() << pNormal.z() << - pNormal.y();
 
  747           if ( mAddTextureCoords )
 
  749             std::pair<float, float> pr = rotateCoords( p->x, p->y, 0.0f, 0.0f, mTextureRotation );
 
  750             mData << pr.first << pr.second;
 
  757           for ( 
int j = 2; j >= 0; --j )
 
  759             p2t::Point *p = t->GetPoint( j );
 
  760             QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
 
  762               pt = *toOldBase * pt;
 
  763             const double fx = ( pt.x() / scale ) - mOriginX + pt0.
x();
 
  764             const double fy = ( pt.y() / scale ) - mOriginY + pt0.
y();
 
  765             const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
 
  766             mData << fx << fz << -fy;
 
  768               mData << -pNormal.x() << -pNormal.z() << pNormal.y();
 
  769             if ( mAddTextureCoords )
 
  771               std::pair<float, float> pr = rotateCoords( p->x, p->y, 0.0f, 0.0f, mTextureRotation );
 
  772               mData << pr.first << pr.second;
 
  783     for ( 
int i = 0; i < polylinesToDelete.count(); ++i )
 
  784       qDeleteAll( polylinesToDelete[i] );
 
  788   if ( extrusionHeight != 0 && ( mTessellatedFacade & 1 ) )
 
  790     _makeWalls( *exterior, 
false, extrusionHeight, mData, mAddNormals, mAddTextureCoords, mOriginX, mOriginY, mTextureRotation );
 
  793       _makeWalls( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ), 
true, extrusionHeight, mData, mAddNormals, mAddTextureCoords, mOriginX, mOriginY, mTextureRotation );
 
  795     zMax += extrusionHeight;
 
  818   return mData.size() / ( 
stride() / 
sizeof( float ) );
 
  823   std::unique_ptr< QgsMultiPolygon > mp = std::make_unique< QgsMultiPolygon >();
 
  824   const QVector<float> 
data = mData;
 
  825   mp->reserve( mData.size() );
 
  826   for ( 
auto it = 
data.constBegin(); it != 
data.constEnd(); )
 
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const =0
Returns the point and vertex id of a point within the curve.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
Line string geometry type, with support for z-dimension and m-values.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
const double * yData() const
Returns a const pointer to the y vertex data.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string.
const double * xData() const
Returns a const pointer to the x vertex data.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Point geometry type, with support for z-dimension and m-values.
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
void addInteriorRing(QgsCurve *ring) override
Adds an interior ring to the geometry (takes ownership)
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
A rectangle specified with double values.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
QVector< float > data() const
Returns array of triangle vertex data.
std::unique_ptr< QgsMultiPolygon > asMultiPolygon() const
Returns the triangulation as a multipolygon geometry.
QgsTessellator(double originX, double originY, bool addNormals, bool invertNormals=false, bool addBackFaces=false, bool noZ=false, bool addTextureCoords=false, int facade=3, float textureRotation=0.0f)
Creates tessellator with a specified origin point of the world (in map coordinates)
int stride() const
Returns size of one vertex entry in bytes.
void addPolygon(const QgsPolygon &polygon, float extrusionHeight)
Tessellates a triangle and adds its vertex entries to the output data array.
int dataVerticesCount() const
Returns the number of vertices stored in the output data array.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QgsPoint getPointFromData(QVector< float >::const_iterator &it)
double _round_coord(double x)
double _minimum_distance_between_coordinates(const QgsPolygon &polygon)
VertexType
Type of vertex.
std::size_t operator()(const std::pair< float, float > pair) const