34 #include <unordered_set>    37 static void make_quad( 
float x0, 
float y0, 
float z0, 
float x1, 
float y1, 
float z1, 
float height, QVector<float> &data, 
bool addNormals )
    40   float dy = -( y1 - y0 );
    43   QVector3D vn( -dy, 0, dx );
    48   data << x0 << z0 + height << -y0;
    50     data << vn.x() << vn.y() << vn.z();
    51   data << x1 << z1 + height << -y1;
    53     data << vn.x() << vn.y() << vn.z();
    54   data << x0 << z0 << -y0;
    56     data << vn.x() << vn.y() << vn.z();
    59   data << x0 << z0 << -y0;
    61     data << vn.x() << vn.y() << vn.z();
    62   data << x1 << z1 + height << -y1;
    64     data << vn.x() << vn.y() << vn.z();
    65   data << x1 << z1 << -y1;
    67     data << vn.x() << vn.y() << vn.z();
    74   , mAddNormals( addNormals )
    75   , mInvertNormals( invertNormals )
    76   , mAddBackFaces( addBackFaces )
    83   , mOriginX( mBounds.xMinimum() )
    84   , mOriginY( mBounds.yMinimum() )
    85   , mAddNormals( addNormals )
    86   , mInvertNormals( invertNormals )
    87   , mAddBackFaces( addBackFaces )
    93 void QgsTessellator::init()
    95   mStride = 3 * 
sizeof( float );
    97     mStride += 3 * 
sizeof( float );
   100 static bool _isRingCounterClockWise( 
const QgsCurve &ring )
   107   for ( 
int i = 1; i < count + 1; ++i )
   109     ring.
pointAt( i % count, pt, vt );
   110     a += ptPrev.
x() * pt.
y() - ptPrev.
y() * pt.
x();
   116 static void _makeWalls( 
const QgsLineString &ring, 
bool ccw, 
float extrusionHeight, QVector<float> &data, 
bool addNormals, 
double originX, 
double originY )
   121   bool is_counter_clockwise = _isRingCounterClockWise( ring );
   125   for ( 
int i = 1; i < ring.
numPoints(); ++i )
   127     pt = ring.
pointN( is_counter_clockwise == ccw ? i : ring.
numPoints() - i - 1 );
   128     float x0 = ptPrev.
x() - originX, y0 = ptPrev.
y() - originY;
   129     float x1 = pt.
x() - originX, y1 = pt.
y() - originY;
   130     float z0 = std::isnan( ptPrev.
z() ) ? 0 : ptPrev.
z();
   131     float z1 = std::isnan( pt.
z() ) ? 0 : pt.
z();
   134     make_quad( x0, y0, z0, x1, y1, z1, extrusionHeight, data, addNormals );
   139 static QVector3D _calculateNormal( 
const QgsLineString *curve, 
double originX, 
double originY, 
bool invertNormal )
   144     return QVector3D( 0, 0, 1 );
   152   for ( 
int i = 1; i < curve->
numPoints(); i++ )
   155     if ( pt1.
z() != pt2.
z() )
   162     return QVector3D( 0, 0, 1 );
   169   double nx = 0, ny = 0, nz = 0;
   173   pt1.
setX( pt1.
x() - originX );
   174   pt1.
setY( pt1.
y() - originY );
   175   for ( 
int i = 1; i < curve->
numPoints(); i++ )
   178     pt2.
setX( pt2.
x() - originX );
   179     pt2.
setY( pt2.
y() - originY );
   181     if ( std::isnan( pt1.
z() ) || std::isnan( pt2.
z() ) )
   184     nx += ( pt1.
y() - pt2.
y() ) * ( pt1.
z() + pt2.
z() );
   185     ny += ( pt1.
z() - pt2.
z() ) * ( pt1.
x() + pt2.
x() );
   186     nz += ( pt1.
x() - pt2.
x() ) * ( pt1.
y() + pt2.
y() );
   191   QVector3D normal( nx, ny, nz );
   199 static void _normalVectorToXYVectors( 
const QVector3D &pNormal, QVector3D &pXVector, QVector3D &pYVector )
   204   if ( pNormal.z() > 0.001 || pNormal.z() < -0.001 )
   206     pXVector = QVector3D( 1, 0, -pNormal.x() / pNormal.z() );
   208   else if ( pNormal.y() > 0.001 || pNormal.y() < -0.001 )
   210     pXVector = QVector3D( 1, -pNormal.x() / pNormal.y(), 0 );
   214     pXVector = QVector3D( -pNormal.y() / pNormal.x(), 1, 0 );
   216   pXVector.normalize();
   217   pYVector = QVector3D::normal( pNormal, pXVector );
   222   std::size_t 
operator()( 
const std::pair<float, float> pair )
 const   224     std::size_t h1 = std::hash<float>()( pair.first );
   225     std::size_t h2 = std::hash<float>()( pair.second );
   231 static void _ringToPoly2tri( 
const QgsLineString *ring, std::vector<p2t::Point *> &polyline, QHash<p2t::Point *, float> *zHash )
   235   polyline.reserve( pCount );
   237   const double *srcXData = ring->
xData();
   238   const double *srcYData = ring->
yData();
   239   const double *srcZData = ring->
zData();
   240   std::unordered_set<std::pair<float, float>, 
float_pair_hash> foundPoints;
   242   for ( 
int i = 0; i < pCount - 1; ++i )
   244     const float x = *srcXData++;
   245     const float y = *srcYData++;
   247     auto res = foundPoints.insert( std::make_pair( x, y ) );
   254     p2t::Point *pt2 = 
new p2t::Point( x, y );
   255     polyline.push_back( pt2 );
   258       ( *zHash )[pt2] = *srcZData++;
   266   const double exp = 1e10;   
   267   return round( x * exp ) / exp;
   271 static QgsCurve *_transform_ring_to_new_base( 
const QgsLineString &curve, 
const QgsPoint &pt0, 
const QMatrix4x4 *toNewBase, 
float scaleX, 
float scaleY )
   280   double *xData = x.data();
   281   double *yData = y.data();
   282   double *zData = z.data();
   284   const double *srcXData = curve.
xData();
   285   const double *srcYData = curve.
yData();
   286   const double *srcZData = curve.
is3D() ? curve.
zData() : 
nullptr;
   288   for ( 
int i = 0; i < count; ++i )
   290     QVector4D v( *srcXData++ - pt0.
x(),
   291                  *srcYData++ - pt0.
y(),
   292                  srcZData ? *srcZData++ - pt0.
z() : 0,
   295       v = toNewBase->map( v );
   298     v.setX( v.x() * scaleX );
   299     v.setY( v.y() * scaleY );
   319 static QgsPolygon *_transform_polygon_to_new_base( 
const QgsPolygon &polygon, 
const QgsPoint &pt0, 
const QMatrix4x4 *toNewBase, 
float scaleX, 
float scaleY )
   322   p->
setExteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ), pt0, toNewBase, scaleX, scaleY ) );
   324     p->
addInteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ), pt0, toNewBase, scaleX, scaleY ) );
   328 static bool _check_intersecting_rings( 
const QgsPolygon &polygon )
   330   std::vector< std::unique_ptr< QgsGeometryEngine > > ringEngines;
   340   for ( 
const std::unique_ptr< QgsGeometryEngine > &ring : ringEngines )
   342     if ( !ring->isSimple() )
   363   if ( ringEngines.size() > 1 )
   365     for ( 
size_t i = 0; i < ringEngines.size(); ++i )
   367       std::unique_ptr< QgsGeometryEngine > &first = ringEngines.at( i );
   369         first->prepareGeometry();
   376       for ( 
int interiorRing = static_cast< int >( i ); interiorRing < polygon.
numInteriorRings(); ++interiorRing )
   378         if ( first->intersects( polygon.
interiorRing( interiorRing ) ) )
   391   std::vector< const QgsLineString * > rings;
   393   rings.emplace_back( qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ) );
   395     rings.emplace_back( qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ) );
   400     if ( numPoints <= 1 )
   403     const double *srcXData = ring->
xData();
   404     const double *srcYData = ring->
yData();
   405     double x0 = *srcXData++;
   406     double y0 = *srcYData++;
   407     for ( 
int i = 1; i < numPoints; ++i )
   409       double x1 = *srcXData++;
   410       double y1 = *srcYData++;
   411       double d = ( x0 - x1 ) * ( x0 - x1 ) + ( y0 - y1 ) * ( y0 - y1 );
   419   return min_d != 1e20 ? std::sqrt( min_d ) : 1e20;
   429   const QVector3D pNormal = !mNoZ ? _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ) : QVector3D();
   430   const int pCount = exterior->
numPoints();
   434   float zMin = std::numeric_limits<float>::max();
   435   float zMax = std::numeric_limits<float>::min();
   440     const double *xData = exterior->
xData();
   441     const double *yData = exterior->
yData();
   442     const double *zData = !mNoZ ? exterior->
zData() : 
nullptr;
   443     for ( 
int i = 0; i < 3; i++ )
   445       float z = ( mNoZ ? 0 : *zData++ );
   451       mData << *xData++ - mOriginX << z << - *yData++ + mOriginY;
   453         mData << pNormal.x() << pNormal.z() << - pNormal.y();
   459       for ( 
int i = 2; i >= 0; i-- )
   461         mData << exterior->
xAt( i ) - mOriginX << ( mNoZ ? 0 : exterior->
zAt( i ) ) << - exterior->
yAt( i ) + mOriginY;
   463           mData << -pNormal.x() << -pNormal.z() << pNormal.y();
   472     std::unique_ptr<QMatrix4x4> toNewBase, toOldBase;
   473     if ( !mNoZ && pNormal != QVector3D( 0, 0, 1 ) )
   478       QVector3D pXVector, pYVector;
   479       _normalVectorToXYVectors( pNormal, pXVector, pYVector );
   484       toNewBase.reset( 
new QMatrix4x4(
   485                          pXVector.x(), pXVector.y(), pXVector.z(), 0,
   486                          pYVector.x(), pYVector.y(), pYVector.z(), 0,
   487                          pNormal.x(), pNormal.y(), pNormal.z(), 0,
   491       toOldBase.reset( 
new QMatrix4x4( toNewBase->transposed() ) );
   497     const float scaleX = !mBounds.isNull() ? 10000.0 / mBounds.width() : 1.0;
   498     const float scaleY = !mBounds.isNull() ? 10000.0 / mBounds.height() : 1.0;
   503     std::unique_ptr<QgsPolygon> polygonNew( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get(), scaleX, scaleY ) );
   512       if ( polygonSimplified.
isNull() )
   522         QgsMessageLog::logMessage( QObject::tr( 
"geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr( 
"3D" ) );
   527         polygonNew.reset( polygonSimplifiedData->
clone() );
   531     if ( !_check_intersecting_rings( *polygonNew ) )
   534       QgsMessageLog::logMessage( QObject::tr( 
"polygon rings self-intersect or intersect each other - skipping" ), QObject::tr( 
"3D" ) );
   538     QList< std::vector<p2t::Point *> > polylinesToDelete;
   539     QHash<p2t::Point *, float> z;
   542     std::vector<p2t::Point *> polyline;
   543     _ringToPoly2tri( qgsgeometry_cast< const QgsLineString * >( polygonNew->exteriorRing() ), polyline, mNoZ ? 
nullptr : &z );
   544     polylinesToDelete << polyline;
   546     std::unique_ptr<p2t::CDT> cdt( 
new p2t::CDT( polyline ) );
   549     for ( 
int i = 0; i < polygonNew->numInteriorRings(); ++i )
   551       std::vector<p2t::Point *> holePolyline;
   554       _ringToPoly2tri( hole, holePolyline, mNoZ ? 
nullptr : &z );
   556       cdt->AddHole( holePolyline );
   557       polylinesToDelete << holePolyline;
   565       std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
   567       mData.reserve( mData.size() + triangles.size() * ( ( mAddNormals ? 6 : 3 ) * ( mAddBackFaces ? 2 : 1 ) ) );
   568       for ( 
size_t i = 0; i < triangles.size(); ++i )
   570         p2t::Triangle *t = triangles[i];
   571         for ( 
int j = 0; j < 3; ++j )
   573           p2t::Point *p = t->GetPoint( j );
   574           QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
   576             pt = *toOldBase * pt;
   577           const double fx = ( pt.x() / scaleX ) - mOriginX + pt0.
x();
   578           const double fy = ( pt.y() / scaleY ) - mOriginY + pt0.
y();
   579           const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
   585           mData << fx << fz << -fy;
   587             mData << pNormal.x() << pNormal.z() << - pNormal.y();
   593           for ( 
int j = 2; j >= 0; --j )
   595             p2t::Point *p = t->GetPoint( j );
   596             QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
   598               pt = *toOldBase * pt;
   599             const double fx = ( pt.x() / scaleX ) - mOriginX + pt0.
x();
   600             const double fy = ( pt.y() / scaleY ) - mOriginY + pt0.
y();
   601             const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
   602             mData << fx << fz << -fy;
   604               mData << -pNormal.x() << -pNormal.z() << pNormal.y();
   614     for ( 
int i = 0; i < polylinesToDelete.count(); ++i )
   615       qDeleteAll( polylinesToDelete[i] );
   619   if ( extrusionHeight != 0 )
   621     _makeWalls( *exterior, 
false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
   624       _makeWalls( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ), 
true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
   626     zMax += extrusionHeight;
   649   return mData.size() / ( mAddNormals ? 6 : 3 );
   654   std::unique_ptr< QgsMultiPolygon > mp = qgis::make_unique< QgsMultiPolygon >();
   655   const QVector<float> data = mData;
   656   mp->reserve( mData.size() );
   657   for ( 
auto it = data.constBegin(); it != data.constEnd(); )
 A rectangle specified with double values. 
 
bool isEmpty() const override
Returns true if the geometry is empty. 
 
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string. 
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference) 
 
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon. 
 
A geometry is the spatial representation of a feature. 
 
int dataVerticesCount() const
Returns the number of vertices stored in the output data array. 
 
virtual bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const =0
Returns the point and vertex id of a point within the curve. 
 
std::unique_ptr< QgsMultiPolygon > asMultiPolygon() const
Returns the triangulation as a multipolygon geometry. 
 
QgsPoint getPointFromData(QVector< float >::const_iterator &it)
 
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension. 
 
int numPoints() const override
Returns the number of points in the curve. 
 
const double * xData() const
Returns a const pointer to the x vertex data. 
 
void addInteriorRing(QgsCurve *ring) override
Adds an interior ring to the geometry (takes ownership) 
 
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon. 
 
double _round_coord(double x)
 
void addPolygon(const QgsPolygon &polygon, float extrusionHeight)
Tessellates a triangle and adds its vertex entries to the output data array. 
 
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary). 
 
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string. 
 
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
 
std::size_t operator()(const std::pair< float, float > pair) const
 
QgsTessellator(double originX, double originY, bool addNormals, bool invertNormals=false, bool addBackFaces=false)
Creates tessellator with a specified origin point of the world (in map coordinates) ...
 
Abstract base class for curved geometry type. 
 
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry. 
 
Point geometry type, with support for z-dimension and m-values. 
 
double _minimum_distance_between_coordinates(const QgsPolygon &polygon)
 
const double * yData() const
Returns a const pointer to the y vertex data. 
 
QgsPoint startPoint() const override
Returns the starting point of the curve. 
 
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values...
 
void setX(double x)
Sets the point's x-coordinate. 
 
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine. 
 
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive. 
 
void setY(double y)
Sets the point's y-coordinate. 
 
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon. 
 
Line string geometry type, with support for z-dimension and m-values. 
 
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy. 
 
QgsPoint pointN(int i) const
Returns the specified point from inside the line string. 
 
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string. 
 
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring. 
 
virtual int numPoints() const =0
Returns the number of points in the curve. 
 
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.