35 static void make_quad(
float x0,
float y0,
float z0,
float x1,
float y1,
float z1,
float height, QVector<float> &data,
bool addNormals )
38 float dy = -( y1 - y0 );
41 QVector3D vn( -dy, 0, dx );
46 data << x0 << z0 + height << -y0;
48 data << vn.x() << vn.y() << vn.z();
49 data << x1 << z1 + height << -y1;
51 data << vn.x() << vn.y() << vn.z();
52 data << x0 << z0 << -y0;
54 data << vn.x() << vn.y() << vn.z();
57 data << x0 << z0 << -y0;
59 data << vn.x() << vn.y() << vn.z();
60 data << x1 << z1 + height << -y1;
62 data << vn.x() << vn.y() << vn.z();
63 data << x1 << z1 << -y1;
65 data << vn.x() << vn.y() << vn.z();
72 , mAddNormals( addNormals )
73 , mInvertNormals( invertNormals )
74 , mAddBackFaces( addBackFaces )
76 mStride = 3 *
sizeof( float );
78 mStride += 3 *
sizeof( float );
82 static bool _isRingCounterClockWise(
const QgsCurve &ring )
89 for (
int i = 1; i < count + 1; ++i )
91 ring.
pointAt( i % count, pt, vt );
92 a += ptPrev.
x() * pt.
y() - ptPrev.
y() * pt.
x();
98 static void _makeWalls(
const QgsCurve &ring,
bool ccw,
float extrusionHeight, QVector<float> &data,
bool addNormals,
double originX,
double originY )
103 bool is_counter_clockwise = _isRingCounterClockWise( ring );
109 ring.
pointAt( is_counter_clockwise == ccw ? 0 : ring.
numPoints() - 1, ptPrev, vt );
110 for (
int i = 1; i < ring.
numPoints(); ++i )
112 ring.
pointAt( is_counter_clockwise == ccw ? i : ring.
numPoints() - i - 1, pt, vt );
113 float x0 = ptPrev.
x() - originX, y0 = ptPrev.
y() - originY;
114 float x1 = pt.x() - originX, y1 = pt.y() - originY;
115 float z0 = std::isnan( ptPrev.
z() ) ? 0 : ptPrev.
z();
116 float z1 = std::isnan( pt.z() ) ? 0 : pt.z();
119 make_quad( x0, y0, z0, x1, y1, z1, extrusionHeight, data, addNormals );
124 static QVector3D _calculateNormal(
const QgsCurve *curve,
double originX,
double originY,
bool invertNormal )
132 return QVector3D( 0, 0, 1 );
139 for (
int i = 1; i < curve->
numPoints(); i++ )
142 if ( pt1.
z() != pt2.
z() )
149 return QVector3D( 0, 0, 1 );
156 double nx = 0, ny = 0, nz = 0;
157 for (
int i = 0; i < curve->
numPoints() - 1; i++ )
160 curve->
pointAt( i + 1, pt2, vt );
163 pt1.
setX( pt1.
x() - originX );
164 pt1.
setY( pt1.
y() - originY );
165 pt2.
setX( pt2.
x() - originX );
166 pt2.
setY( pt2.
y() - originY );
168 if ( std::isnan( pt1.
z() ) || std::isnan( pt2.
z() ) )
171 nx += ( pt1.
y() - pt2.
y() ) * ( pt1.
z() + pt2.
z() );
172 ny += ( pt1.
z() - pt2.
z() ) * ( pt1.
x() + pt2.
x() );
173 nz += ( pt1.
x() - pt2.
x() ) * ( pt1.
y() + pt2.
y() );
176 QVector3D normal( nx, ny, nz );
184 static void _normalVectorToXYVectors(
const QVector3D &pNormal, QVector3D &pXVector, QVector3D &pYVector )
189 if ( pNormal.z() > 0.001 || pNormal.z() < -0.001 )
191 pXVector = QVector3D( 1, 0, -pNormal.x() / pNormal.z() );
193 else if ( pNormal.y() > 0.001 || pNormal.y() < -0.001 )
195 pXVector = QVector3D( 1, -pNormal.x() / pNormal.y(), 0 );
199 pXVector = QVector3D( -pNormal.y() / pNormal.x(), 1, 0 );
201 pXVector.normalize();
202 pYVector = QVector3D::normal( pNormal, pXVector );
206 static void _ringToPoly2tri(
const QgsCurve *ring, std::vector<p2t::Point *> &polyline, QHash<p2t::Point *, float> &zHash )
213 polyline.reserve( pCount );
215 for (
int i = 0; i < pCount - 1; ++i )
218 const float x = pt.
x();
219 const float y = pt.
y();
220 const float z = pt.
z();
222 const bool found = std::find_if( polyline.begin(), polyline.end(), [x, y]( p2t::Point *&p ) {
return *p == p2t::Point( x, y ); } ) != polyline.end();
229 p2t::Point *pt2 =
new p2t::Point( x, y );
230 polyline.push_back( pt2 );
238 const double exp = 1e10;
239 return round( x * exp ) / exp;
243 static QgsCurve *_transform_ring_to_new_base(
const QgsCurve &curve,
const QgsPoint &pt0,
const QMatrix4x4 *toNewBase )
246 QVector<QgsPoint> pts;
247 pts.reserve( count );
249 for (
int i = 0; i < count; ++i )
254 QVector4D v( pt2.x(), pt2.y(), pt2.z(), 0 );
256 v = toNewBase->map( v );
284 static bool _check_intersecting_rings(
const QgsPolygon &polygon )
286 QList<QgsGeometry> geomRings;
295 for (
int i = 0; i < geomRings.count(); ++i )
297 if ( !geomRings[i].isSimple() )
320 for (
int i = 0; i < geomRings.count(); ++i )
321 for (
int j = i + 1; j < geomRings.count(); ++j )
323 if ( geomRings[i].intersects( geomRings[j] ) )
357 const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals );
358 const int pCount = exterior->
numPoints();
365 for (
int i = 0; i < 3; i++ )
367 exterior->
pointAt( i, pt, vt );
368 mData << pt.
x() - mOriginX << pt.
z() << - pt.
y() + mOriginY;
370 mData << pNormal.x() << pNormal.z() << - pNormal.y();
376 for (
int i = 2; i >= 0; i-- )
378 exterior->
pointAt( i, pt, vt );
379 mData << pt.
x() - mOriginX << pt.
z() << - pt.
y() + mOriginY;
381 mData << -pNormal.x() << -pNormal.z() << pNormal.y();
390 std::unique_ptr<QMatrix4x4> toNewBase, toOldBase;
391 if ( pNormal != QVector3D( 0, 0, 1 ) )
396 QVector3D pXVector, pYVector;
397 _normalVectorToXYVectors( pNormal, pXVector, pYVector );
402 toNewBase.reset(
new QMatrix4x4(
403 pXVector.x(), pXVector.y(), pXVector.z(), 0,
404 pYVector.x(), pYVector.y(), pYVector.z(), 0,
405 pNormal.x(), pNormal.y(), pNormal.z(), 0,
409 toOldBase.reset(
new QMatrix4x4( toNewBase->transposed() ) );
417 std::unique_ptr<QgsPolygon> polygonNew( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get() ) );
426 if ( polygonSimplified.
isNull() )
436 QgsMessageLog::logMessage( QObject::tr(
"geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr(
"3D" ) );
441 polygonNew.reset( polygonSimplifiedData->
clone() );
445 if ( !_check_intersecting_rings( *polygonNew ) )
448 QgsMessageLog::logMessage( QObject::tr(
"polygon rings self-intersect or intersect each other - skipping" ), QObject::tr(
"3D" ) );
452 QList< std::vector<p2t::Point *> > polylinesToDelete;
453 QHash<p2t::Point *, float> z;
456 std::vector<p2t::Point *> polyline;
457 _ringToPoly2tri( polygonNew->exteriorRing(), polyline, z );
458 polylinesToDelete << polyline;
460 std::unique_ptr<p2t::CDT> cdt(
new p2t::CDT( polyline ) );
463 for (
int i = 0; i < polygonNew->numInteriorRings(); ++i )
465 std::vector<p2t::Point *> holePolyline;
466 const QgsCurve *hole = polygonNew->interiorRing( i );
468 _ringToPoly2tri( hole, holePolyline, z );
470 cdt->AddHole( holePolyline );
471 polylinesToDelete << holePolyline;
479 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
481 for (
size_t i = 0; i < triangles.size(); ++i )
483 p2t::Triangle *t = triangles[i];
484 for (
int j = 0; j < 3; ++j )
486 p2t::Point *p = t->GetPoint( j );
487 QVector4D pt( p->x, p->y, z[p], 0 );
489 pt = *toOldBase * pt;
490 const double fx = pt.x() - mOriginX + pt0.
x();
491 const double fy = pt.y() - mOriginY + pt0.
y();
492 const double fz = pt.z() + extrusionHeight + pt0.
z();
493 mData << fx << fz << -fy;
495 mData << pNormal.x() << pNormal.z() << - pNormal.y();
501 for (
int j = 2; j >= 0; --j )
503 p2t::Point *p = t->GetPoint( j );
504 QVector4D pt( p->x, p->y, z[p], 0 );
506 pt = *toOldBase * pt;
507 const double fx = pt.x() - mOriginX + pt0.
x();
508 const double fy = pt.y() - mOriginY + pt0.
y();
509 const double fz = pt.z() + extrusionHeight + pt0.
z();
510 mData << fx << fz << -fy;
512 mData << -pNormal.x() << -pNormal.z() << pNormal.y();
522 for (
int i = 0; i < polylinesToDelete.count(); ++i )
523 qDeleteAll( polylinesToDelete[i] );
527 if ( extrusionHeight != 0 )
529 _makeWalls( *exterior,
false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
532 _makeWalls( *polygon.
interiorRing( i ),
true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
550 return mData.size() / ( mAddNormals ? 6 : 3 );
555 std::unique_ptr< QgsMultiPolygon > mp = qgis::make_unique< QgsMultiPolygon >();
556 const QVector<float> data = mData;
557 for (
auto it = data.constBegin(); it != data.constEnd(); )
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
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.
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).
vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry...
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
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)
void setX(double x)
Sets the point's x-coordinate.
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.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
Line string geometry type, with support for z-dimension and m-values.
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
virtual QgsPoint startPoint() const =0
Returns the starting point of the curve.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
virtual int numPoints() const =0
Returns the number of points in the curve.