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,
const float scale )
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() * scale );
299 v.setY( v.y() * scale );
319 static QgsPolygon *_transform_polygon_to_new_base(
const QgsPolygon &polygon,
const QgsPoint &pt0,
const QMatrix4x4 *toNewBase,
const float scale )
322 p->
setExteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ), pt0, toNewBase, scale ) );
324 p->
addInteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ), pt0, toNewBase, scale ) );
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 = ( !zData ? 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 scale = mBounds.
isNull() ? 1.0 : std::max( 10000.0 / mBounds.
width(), 10000.0 / mBounds.
height() );
502 std::unique_ptr<QgsPolygon> polygonNew( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get(), scale ) );
511 if ( polygonSimplified.
isNull() )
516 const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.
constGet() );
521 QgsMessageLog::logMessage( QObject::tr(
"geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr(
"3D" ) );
526 polygonNew.reset( polygonSimplifiedData->
clone() );
530 if ( !_check_intersecting_rings( *polygonNew ) )
533 QgsMessageLog::logMessage( QObject::tr(
"polygon rings self-intersect or intersect each other - skipping" ), QObject::tr(
"3D" ) );
537 QList< std::vector<p2t::Point *> > polylinesToDelete;
538 QHash<p2t::Point *, float> z;
541 std::vector<p2t::Point *> polyline;
542 _ringToPoly2tri( qgsgeometry_cast< const QgsLineString * >( polygonNew->exteriorRing() ), polyline, mNoZ ?
nullptr : &z );
543 polylinesToDelete << polyline;
545 std::unique_ptr<p2t::CDT> cdt(
new p2t::CDT( polyline ) );
548 for (
int i = 0; i < polygonNew->numInteriorRings(); ++i )
550 std::vector<p2t::Point *> holePolyline;
551 const QgsLineString *hole = qgsgeometry_cast< const QgsLineString *>( polygonNew->interiorRing( i ) );
553 _ringToPoly2tri( hole, holePolyline, mNoZ ?
nullptr : &z );
555 cdt->AddHole( holePolyline );
556 polylinesToDelete << holePolyline;
564 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
566 mData.reserve( mData.size() + triangles.size() * ( ( mAddNormals ? 6 : 3 ) * ( mAddBackFaces ? 2 : 1 ) ) );
567 for (
size_t i = 0; i < triangles.size(); ++i )
569 p2t::Triangle *t = triangles[i];
570 for (
int j = 0; j < 3; ++j )
572 p2t::Point *p = t->GetPoint( j );
573 QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
575 pt = *toOldBase * pt;
576 const double fx = ( pt.x() / scale ) - mOriginX + pt0.
x();
577 const double fy = ( pt.y() / scale ) - mOriginY + pt0.
y();
578 const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
584 mData << fx << fz << -fy;
586 mData << pNormal.x() << pNormal.z() << - pNormal.y();
592 for (
int j = 2; j >= 0; --j )
594 p2t::Point *p = t->GetPoint( j );
595 QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
597 pt = *toOldBase * pt;
598 const double fx = ( pt.x() / scale ) - mOriginX + pt0.
x();
599 const double fy = ( pt.y() / scale ) - mOriginY + pt0.
y();
600 const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
601 mData << fx << fz << -fy;
603 mData << -pNormal.x() << -pNormal.z() << pNormal.y();
613 for (
int i = 0; i < polylinesToDelete.count(); ++i )
614 qDeleteAll( polylinesToDelete[i] );
618 if ( extrusionHeight != 0 )
620 _makeWalls( *exterior,
false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
623 _makeWalls( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ),
true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY );
625 zMax += extrusionHeight;
648 return mData.size() / ( mAddNormals ? 6 : 3 );
653 std::unique_ptr< QgsMultiPolygon > mp = qgis::make_unique< QgsMultiPolygon >();
654 const QVector<float>
data = mData;
655 mp->reserve( mData.size() );
656 for (
auto it =
data.constBegin(); it !=
data.constEnd(); )