QGIS API Documentation 3.43.0-Master (b60ef06885e)
qgstriangularmesh.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstriangularmesh.cpp
3 ---------------------
4 begin : April 2018
5 copyright : (C) 2018 by Peter Petrik
6 email : zilolv at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include <QList>
19#include "qgstriangularmesh.h"
21#include "qgsgeometry.h"
22#include "qgsrectangle.h"
23#include "qgslogger.h"
24#include "qgsmeshspatialindex.h"
25#include "qgsmeshlayerutils.h"
26#include "qgsmeshutils.h"
27#include "meshOptimizer/meshoptimizer.h"
28
29static void triangulateFaces( const QgsMeshFace &face,
30 int nativeIndex,
31 QVector<QgsMeshFace> &destinationFaces,
32 QVector<int> &triangularToNative,
33 const QgsMesh &verticesMeshSource )
34{
35 int vertexCount = face.size();
36 if ( vertexCount < 3 )
37 return;
38
39 while ( vertexCount > 3 )
40 {
41 // clip one ear from last 2 and first vertex
42 const QgsMeshFace ear = { face[vertexCount - 2], face[vertexCount - 1], face[0] };
43 if ( !( std::isnan( verticesMeshSource.vertex( ear[0] ).x() ) ||
44 std::isnan( verticesMeshSource.vertex( ear[1] ).x() ) ||
45 std::isnan( verticesMeshSource.vertex( ear[2] ).x() ) ) )
46 {
47 destinationFaces.push_back( ear );
48 triangularToNative.push_back( nativeIndex );
49 }
50 --vertexCount;
51 }
52
53 const QgsMeshFace triangle = { face[1], face[2], face[0] };
54 if ( !( std::isnan( verticesMeshSource.vertex( triangle[0] ).x() ) ||
55 std::isnan( verticesMeshSource.vertex( triangle[1] ).x() ) ||
56 std::isnan( verticesMeshSource.vertex( triangle[2] ).x() ) ) )
57 {
58 destinationFaces.push_back( triangle );
59 triangularToNative.push_back( nativeIndex );
60 }
61}
62
63void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
64{
65 triangulateFaces( face, nativeIndex, mTriangularMesh.faces, mTrianglesToNativeFaces, mTriangularMesh );
66}
67
68QgsMeshVertex QgsTriangularMesh::transformVertex( const QgsMeshVertex &vertex, Qgis::TransformDirection direction ) const
69{
70 QgsMeshVertex transformedVertex = vertex;
71
72 if ( mCoordinateTransform.isValid() )
73 {
74 try
75 {
76 transformedVertex.transform( mCoordinateTransform, direction );
77 }
78 catch ( QgsCsException &cse )
79 {
80 Q_UNUSED( cse )
81 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
82 transformedVertex = QgsMeshVertex();
83 }
84 }
85
86 return transformedVertex;
87}
88
89QgsMeshVertex QgsTriangularMesh::calculateCentroid( const QgsMeshFace &nativeFace ) const
90{
91 return QgsMeshUtils::centroid( nativeFace, mTriangularMesh.vertices );
92}
93
95{
96 return mAverageTriangleSize;
97}
98
101
102bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform )
103{
104 Q_ASSERT( nativeMesh );
105
106 bool needUpdateVerticesCoordinates = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
107 ( ( mCoordinateTransform.isValid() || transform.isValid() ) &&
108 ( mCoordinateTransform.sourceCrs() != transform.sourceCrs() ||
109 mCoordinateTransform.destinationCrs() != transform.destinationCrs() ||
110 mCoordinateTransform.isValid() != transform.isValid() ) ) ;
111
112 bool needUpdateFrame = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
113 mNativeMeshFaceCentroids.size() != nativeMesh->faces.size() ||
114 mTriangularMesh.faces.size() < nativeMesh->faces.size() ||
115 mTriangularMesh.edges.size() != nativeMesh->edges.size();
116
117
118 // FIND OUT IF UPDATE IS NEEDED
119 if ( ! needUpdateVerticesCoordinates && !needUpdateFrame )
120 return false;
121
122 // CLEAN-UP
123 mTriangularMesh.vertices.clear();
124 if ( needUpdateFrame )
125 {
126 mTriangularMesh.faces.clear();
127 mTriangularMesh.edges.clear();
128 mEdgesToNativeEdges.clear();
129 mTrianglesToNativeFaces.clear();
130 }
131
132 // TRANSFORM VERTICES
133 mCoordinateTransform = transform;
134 mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
135 mExtent.setNull();
136 for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
137 {
138 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh->vertices.at( i ) );
139 mExtent.include( mTriangularMesh.vertices.at( i ) );
140 }
141
142 if ( needUpdateFrame )
143 {
144 // CREATE TRIANGULAR MESH
145 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
146 {
147 const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
148 triangulate( face, i );
149 }
150 }
151
152 // CALCULATE CENTROIDS
153 mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
154 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
155 {
156 const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
157 mNativeMeshFaceCentroids[i] = calculateCentroid( face );
158 }
159
160 // CALCULATE SPATIAL INDEX
161 mSpatialFaceIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Face );
162
163 if ( needUpdateFrame )
164 {
165 // SET ALL TRIANGLE CCW AND COMPUTE AVERAGE SIZE
166 finalizeTriangles();
167 }
168
169 // CREATE EDGES
170 // remove all edges with invalid vertices
171 if ( needUpdateFrame )
172 {
173 const QVector<QgsMeshEdge> edges = nativeMesh->edges;
174 for ( int nativeIndex = 0; nativeIndex < edges.size(); ++nativeIndex )
175 {
176 const QgsMeshEdge &edge = edges.at( nativeIndex );
177 if ( !( std::isnan( mTriangularMesh.vertex( edge.first ).x() ) ||
178 std::isnan( mTriangularMesh.vertex( edge.second ).x() ) ) )
179 {
180 mTriangularMesh.edges.push_back( edge );
181 mEdgesToNativeEdges.push_back( nativeIndex );
182 }
183 }
184 }
185
186 // CALCULATE CENTROIDS
187 mNativeMeshEdgeCentroids.resize( nativeMesh->edgeCount() );
188 for ( int i = 0; i < nativeMesh->edgeCount(); ++i )
189 {
190 const QgsMeshEdge &edge = nativeMesh->edges.at( i ) ;
191 const QgsPoint &a = mTriangularMesh.vertices[edge.first];
192 const QgsPoint &b = mTriangularMesh.vertices[edge.second];
193 mNativeMeshEdgeCentroids[i] = QgsMeshVertex( ( a.x() + b.x() ) / 2.0, ( a.y() + b.y() ) / 2.0, ( a.z() + b.z() ) / 2.0 );
194 }
195
196 // CALCULATE SPATIAL INDEX
197 mSpatialEdgeIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Edge );
198
199 return true;
200}
201
203{
204 return update( nativeMesh, mCoordinateTransform );
205}
206
207void QgsTriangularMesh::finalizeTriangles()
208{
209 mAverageTriangleSize = 0;
210 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
211 {
212 QgsMeshFace &face = mTriangularMesh.faces[i];
213
214 const QgsMeshVertex &v0 = mTriangularMesh.vertex( face[0] );
215 const QgsMeshVertex &v1 = mTriangularMesh.vertex( face[1] );
216 const QgsMeshVertex &v2 = mTriangularMesh.vertex( face[2] );
217
218 QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( v0, v1, v2 );
219
220 mAverageTriangleSize += std::fmax( bbox.width(), bbox.height() );
221
222 QgsMeshUtils::setCounterClockwise( face, v0, v1, v2 );
223 }
224 mAverageTriangleSize /= mTriangularMesh.faceCount();
225}
226
228{
229 return transformVertex( vertex, Qgis::TransformDirection::Forward );
230}
231
233{
234 return transformVertex( vertex, Qgis::TransformDirection::Reverse );
235}
236
238{
240 if ( !mCoordinateTransform.isShortCircuited() )
241 {
242 try
243 {
244 QgsCoordinateTransform extentTransform = mCoordinateTransform;
245 extentTransform.setBallparkTransformsAreAppropriate( true );
247 }
248 catch ( QgsCsException &cse )
249 {
250 Q_UNUSED( cse )
251 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
252 }
253 }
254 else
256
257 return nativeExtent;
258}
259
261{
262 if ( !mIsExtentValid )
263 {
264 mExtent.setNull();
265 for ( int i = 0; i < mTriangularMesh.vertices.size(); ++i )
266 if ( !mTriangularMesh.vertices.at( i ).isEmpty() )
267 mExtent.include( mTriangularMesh.vertices.at( i ) );
268
269 mIsExtentValid = true;
270 }
271 return mExtent;
272}
273
275{
276 return mLod;
277}
278
280{
281 switch ( type )
282 {
284 return mTriangularMesh.vertexCount() != 0;
286 return mTriangularMesh.edgeCount() != 0;
288 return mTriangularMesh.faceCount() != 0;
289 }
290
291 return false;
292}
293
294void QgsTriangularMesh::addVertex( const QgsMeshVertex &vertex )
295{
296 QgsMeshVertex vertexInTriangularCoordinates = nativeToTriangularCoordinates( vertex );
297 mTriangularMesh.vertices.append( vertexInTriangularCoordinates );
298 if ( !vertexInTriangularCoordinates.isEmpty() )
299 mExtent.include( vertexInTriangularCoordinates );
300}
301
302const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
303{
304 return mTriangularMesh.vertices;
305}
306
307const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
308{
309 return mTriangularMesh.faces;
310}
311
312const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
313{
314 return mTriangularMesh.edges;
315}
316
317const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
318{
319 return faceCentroids();
320}
321
322const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
323{
324 return mNativeMeshFaceCentroids;
325}
326
327const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
328{
329 return mNativeMeshEdgeCentroids;
330}
331
333{
334 return mTrianglesToNativeFaces;
335}
336
337const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
338{
339 return mEdgesToNativeEdges;
340}
341
343{
344 QgsPointXY mapPoint;
345 if ( mCoordinateTransform.isValid() )
346 {
347 try
348 {
349 mapPoint = mCoordinateTransform.transform( point );
350 }
351 catch ( QgsCsException &cse )
352 {
353 Q_UNUSED( cse )
354 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
355 mapPoint = point;
356 }
357 }
358 else
359 mapPoint = point;
360
361 return point;
362}
363
365{
366 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
367 for ( const int faceIndex : faceIndexes )
368 {
369 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
370 const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
371 if ( geom.contains( &point ) )
372 return faceIndex;
373 }
374 return -1;
375}
376
378{
379 int triangleIndex = faceIndexForPoint_v2( point );
380 if ( triangleIndex == -1 )
381 return -1;
382
383 if ( triangleIndex < mTrianglesToNativeFaces.count() )
384 return mTrianglesToNativeFaces.at( triangleIndex );
385
386 return -1;
387}
388
390{
391 QSet<int> concernedFaceIndex = QgsMeshUtils::nativeFacesFromTriangles(
392 faceIndexesForRectangle( rectangle ),
394 return concernedFaceIndex.values();
395}
396
398{
399 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
400
401 for ( const int faceIndex : faceIndexes )
402 {
403 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
404 if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
405 return faceIndex;
406 }
407 return -1;
408}
409
411{
412 return mSpatialFaceIndex.intersects( rectangle );
413}
414
416{
417 return mSpatialEdgeIndex.intersects( rectangle );
418}
419
420QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
421{
422 QVector<QVector3D> normals( vertices().count(), QVector3D( 0, 0, 0 ) );
423
424 for ( const auto &face : triangles() )
425 {
426 if ( face.isEmpty() )
427 continue;
428
429 for ( int i = 0; i < 3; i++ )
430 {
431 int index1( face.at( i ) );
432 int index2( face.at( ( i + 1 ) % 3 ) );
433 int index3( face.at( ( i + 2 ) % 3 ) );
434
435 const QgsMeshVertex &vert( vertices().at( index1 ) );
436 const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
437 const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
438
439 QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
440 QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
441
442 normals[index1] += QVector3D::crossProduct( v1, v2 );
443 }
444 }
445 return normals;
446}
447
448QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
449{
450 QVector<QgsTriangularMesh *> simplifiedMeshes;
451
452 if ( mTriangularMesh.edgeCount() != 0 )
453 return simplifiedMeshes;
454
455 if ( !( reductionFactor > 1 ) )
456 return simplifiedMeshes;
457
458 size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
459
460 unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
461
462 QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
463 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
464 {
465 const QgsMeshFace &f = mTriangularMesh.face( i );
466 for ( int j = 0; j < 3; ++j )
467 indexes[i * 3 + j] = f.at( j );
468 }
469
470 QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
471 for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
472 {
473 const QgsMeshVertex &v = mTriangularMesh.vertex( i );
474 vertices[i * 3] = v.x() ;
475 vertices[i * 3 + 1] = v.y() ;
476 vertices[i * 3 + 2] = v.z() ;
477 }
478
479 int path = 0;
480 while ( true )
481 {
482 QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
483 size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
484
485 if ( indexes.size() <= int( maxNumberOfIndexes ) )
486 {
487 delete simplifiedMesh;
488 break;
489 }
490
491 QVector<unsigned int> returnIndexes( indexes.size() );
492 //returned size could be different than goal size but not than the input indexes count
493 size_t size = meshopt_simplifySloppy(
494 returnIndexes.data(),
495 indexes.data(),
496 indexes.size(),
497 vertices.data(),
498 verticesCount,
499 sizeof( float ) * 3,
500 maxNumberOfIndexes );
501
502
503 returnIndexes.resize( size );
504
505 if ( size == 0 || int( size ) >= indexes.size() )
506 {
507 QgsDebugError( QStringLiteral( "Mesh simplification failed after %1 path" ).arg( path + 1 ) );
508 delete simplifiedMesh;
509 break;
510 }
511
512 QgsMesh newMesh;
513 newMesh.vertices = mTriangularMesh.vertices;
514
515 newMesh.faces.resize( returnIndexes.size() / 3 );
516 for ( int i = 0; i < newMesh.faces.size(); ++i )
517 {
518 QgsMeshFace f( 3 );
519 for ( size_t j = 0; j < 3 ; ++j )
520 f[j] = returnIndexes.at( i * 3 + j ) ;
521 newMesh.faces[i ] = f;
522 }
523
524 simplifiedMesh->mTriangularMesh = newMesh;
525 simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
526 simplifiedMesh->finalizeTriangles();
527 simplifiedMeshes.push_back( simplifiedMesh );
528
529 QgsDebugMsgLevel( QStringLiteral( "Simplified mesh created with %1 triangles" ).arg( newMesh.faceCount() ), 2 );
530
531 simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
532 for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
533 {
534 QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
535 double x = 0;
536 double y = 0;
537 for ( size_t j = 0; j < 3 ; ++j )
538 {
539 x += mTriangularMesh.vertex( triangle[j] ).x();
540 y += mTriangularMesh.vertex( triangle[j] ).y();
541 }
542 x /= 3;
543 y /= 3;
544 QgsPoint centroid( x, y );
545 int indexInBaseMesh = faceIndexForPoint_v2( centroid );
546
547 if ( indexInBaseMesh == -1 )
548 {
549 // sometime the centroid of simplified mesh could be outside the base mesh,
550 // so try with vertices of the simplified triangle
551 int j = 0;
552 while ( indexInBaseMesh == -1 && j < 3 )
553 indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
554 }
555
556 if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
557 simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
558 }
559
560 simplifiedMesh->mLod = path + 1;
561 simplifiedMesh->mBaseTriangularMesh = this;
562
563 if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
564 break;
565
566 indexes = returnIndexes;
567 ++path;
568 }
569
570 return simplifiedMeshes;
571}
572
574{
575 //if necessary defined removes triangles index
576 if ( changes.mRemovedTriangleIndexes.isEmpty() && !changes.mNativeFaceIndexesToRemove.isEmpty() )
577 {
578 for ( int nf = 0; nf < changes.mNativeFaceIndexesToRemove.count(); ++nf )
579 {
580 int nativeIndex = changes.mNativeFaceIndexesToRemove.at( nf );
581 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( nf );
582 Q_ASSERT( !nativeFace.isEmpty() );
583
584 QgsRectangle nativeFaceExtent( mTriangularMesh.vertex( nativeFace.at( 0 ) ), mTriangularMesh.vertex( nativeFace.at( 0 ) ) );
585 for ( int i = 1; i < nativeFace.count(); ++i )
586 {
587 const QgsMeshVertex &triangularVertex = mTriangularMesh.vertex( nativeFace.at( i ) );
588 nativeFaceExtent.include( triangularVertex );
589 }
590
591 QList<int> concernedTriangle = faceIndexesForRectangle( nativeFaceExtent );
592 //Remove only those corresponding to the native face
593 for ( int i = 0; i < concernedTriangle.count(); ++i )
594 {
595 int triangleIndex = concernedTriangle.at( i );
596 if ( mTrianglesToNativeFaces.at( triangleIndex ) == nativeIndex )
597 changes.mRemovedTriangleIndexes.append( triangleIndex );
598 }
599 }
600 }
601
602 if ( changes.mOldZValue.isEmpty() && !changes.mNewZValue.isEmpty() )
603 {
604 changes.mOldZValue.reserve( changes.mNewZValue.count() );
605 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
606 changes.mOldZValue.append( mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
607 }
608
609 if ( changes.mTriangleIndexesGeometryChanged.isEmpty() && !changes.mNativeFaceIndexesGeometryChanged.isEmpty() )
610 {
611 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
612 {
613 const QgsMeshFace &nativeFace = changes.mNativeFacesGeometryChanged.at( i );
614 if ( nativeFace.count() < 2 )
615 continue;
616 QgsRectangle bbox( mTriangularMesh.vertices.at( nativeFace.at( 0 ) ), mTriangularMesh.vertices.at( nativeFace.at( 1 ) ) );
617
618 for ( int i = 2; i < nativeFace.count(); ++i )
619 bbox.include( mTriangularMesh.vertices.at( nativeFace.at( i ) ) );
620
621 QList<int> triangleIndexes = faceIndexesForRectangle( bbox );
622 int pos = 0;
623 while ( pos < triangleIndexes.count() )
624 {
625 if ( trianglesToNativeFaces().at( triangleIndexes.at( pos ) ) !=
626 changes.mNativeFaceIndexesGeometryChanged.at( i ) )
627 triangleIndexes.removeAt( pos );
628 else
629 ++pos;
630 }
631 changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
632 }
633 }
634
635 // add vertices
636 for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
637 addVertex( vertex );
638
639 // add faces
640 if ( !changes.mNativeFacesToAdd.isEmpty() )
641 {
642 changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
643 int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
644 for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
645 {
646 const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
647 triangulate( nativeFace, firstNewNativeFacesIndex + i );
648 mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
649 }
650
651 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
652 mSpatialFaceIndex.addFace( i, mTriangularMesh );
653 }
654
655 // remove faces
656 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
657 {
658 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
659 mTrianglesToNativeFaces[triangleIndex] = -1;
660 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
661 mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
662 }
663
664 for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
665 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
666
667 // remove vertices
668 for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
669 mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
670
671 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
672 mIsExtentValid = false;
673
674 // change Z value
675 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
676 {
677 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
678 mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
679 }
680
681 //remove outdated spatial index
682 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
683 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
684
685 // change (X,Y) of vertices
686 for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
687 {
688 const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
689 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
690 nativeCoordinates.y(),
691 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
692
693 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
694 }
695
696 //restore spatial undex
697 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
698 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
699
700 //update native faces
701 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
702 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
703}
704
706{
707 //reverse added faces and added vertices
708 if ( !changes.mNativeFacesToAdd.isEmpty() )
709 {
710 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
711 mSpatialFaceIndex.removeFace( i, mTriangularMesh );
712
713 int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
714
715 mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
716 mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
717 mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
718 }
719
720 int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
721 mTriangularMesh.vertices.resize( initialVerticesCount );
722
723 if ( !changes.mAddedVertices.isEmpty() )
724 mIsExtentValid = false;
725
726 // for each vertex to remove we need to update the vertices with the native vertex
727 for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
728 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
729
730 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
731 mIsExtentValid = false;
732
733 // reverse removed faces
734 QVector<QgsMeshFace> restoredTriangles;
735 QVector<int> restoredTriangularToNative;
736 for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
737 {
738 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
739 triangulateFaces( nativeFace,
740 changes.mNativeFaceIndexesToRemove.at( i ),
741 restoredTriangles,
742 restoredTriangularToNative,
743 mTriangularMesh );
744 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
745 }
746 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
747 {
748 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
749 mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
750 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
751 mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
752 }
753
754 // reverse Z value
755 for ( int i = 0; i < changes.mOldZValue.count(); ++i )
756 {
757 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
758 mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
759 }
760
761 //remove outdated spatial index
762 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
763 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
764
765 // reverse (X,Y) of vertices
766 for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
767 {
768 const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
769 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
770 nativeCoordinates.y(),
771 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
772
773 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
774 }
775
776 //restore spatial undex
777 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
778 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
779
780 //update native faces
781 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
782 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
783}
784
786 const QgsMesh &nativeMesh )
787{
788 mAddedVertices = topologicalChanges.addedVertices();
789 mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
790 mNativeFacesToAdd = topologicalChanges.addedFaces();
791 mNativeFacesToRemove = topologicalChanges.removedFaces();
792 mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
793 mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
794 mNewZValue = topologicalChanges.newVerticesZValues();
795 mNewXYValue = topologicalChanges.newVerticesXYValues();
796 mOldXYValue = topologicalChanges.oldVerticesXYValues();
797
798 mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
799 mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
800 for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
801 mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
802}
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2619
@ Forward
Forward transform (from source to destination)
@ Reverse
Reverse/inverse transform (from destination to source)
Handles coordinate transforms between two coordinate systems.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Custom exception class for Coordinate Reference System related exceptions.
QString what() const
A geometry is the spatial representation of a feature.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
A spatial index for QgsMeshFace or QgsMeshEdge objects.
QList< int > intersects(const QgsRectangle &rectangle) const
Returns a list of face ids with a bounding box which intersects the specified rectangle.
void addFace(int faceIndex, const QgsMesh &mesh)
Adds a face with faceIndex from the mesh in the spatial index.
void removeFace(int faceIndex, const QgsMesh &mesh)
Removes a face with faceIndex from the mesh in the spatial index.
static bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
static QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
static void setCounterClockwise(QgsMeshFace &triangle, const QgsMeshVertex &v0, const QgsMeshVertex &v1, const QgsMeshVertex &v2)
Checks if the triangle is counter clockwise, if not sets it counter clockwise.
static QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
static QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:736
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
Definition qgspoint.cpp:384
double y
Definition qgspoint.h:53
A rectangle specified with double values.
void include(const QgsPointXY &p)
Updates the rectangle to include the specified point.
void setNull()
Mark a rectangle as being null (holding no spatial information).
Contains topological differences between two states of a topological mesh, only accessible from the Q...
QVector< QgsMeshFace > removedFaces() const
Returns the faces that are removed with this changes.
QVector< QgsMeshVertex > addedVertices() const
Returns the added vertices with this changes.
QList< int > changedCoordinatesVerticesIndexes() const
Returns the indexes of vertices that have changed coordinates.
QList< int > removedFaceIndexes() const
Returns the indexes of the faces that are removed with this changes.
QList< double > newVerticesZValues() const
Returns the new Z values of vertices that have changed their coordinates.
QVector< QgsMeshFace > addedFaces() const
Returns the face that are added with this changes.
QList< QgsPointXY > oldVerticesXYValues() const
Returns the old (X,Y) values of vertices that have changed their coordinates.
QList< QgsPointXY > newVerticesXYValues() const
Returns the new (X,Y) values of vertices that have changed their coordinates.
QList< int > nativeFacesIndexesGeometryChanged() const
Returns a list of the native face indexes that have a geometry changed.
QList< int > verticesToRemoveIndexes() const
Returns the indexes of vertices to remove.
Makes changes to a triangular mesh and keeps track of these changes.
Changes()=default
Default constructor, no changes.
A triangular/derived mesh with vertices in map coordinates.
const QVector< QgsMeshVertex > & edgeCentroids() const
Returns centroids of the native edges in map CRS.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
QVector< QgsTriangularMesh * > simplifyMesh(double reductionFactor, int minimumTrianglesCount=10) const
Returns simplified meshes.
QgsRectangle nativeExtent()
Returns the extent of the mesh in the native mesh coordinates system, returns empty extent if the tra...
QgsPointXY transformFromLayerToTrianglesCoordinates(const QgsPointXY &point) const
Transforms a point from layer coordinates system to triangular Mesh coordinates system.
int levelOfDetail() const
Returns the corresponding index of level of detail on which this mesh is associated.
QgsRectangle extent() const
Returns the extent of the triangular mesh in map coordinates.
int faceIndexForPoint(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing.
int nativeFaceIndexForPoint(const QgsPointXY &point) const
Finds index of native face at given point It uses spatial indexing.
double averageTriangleSize() const
Returns the average size of triangles in map unit.
void reverseChanges(const Changes &changes, const QgsMesh &nativeMesh)
Reverses the changes on the triangular mesh (see Changes)
void applyChanges(const Changes &changes)
Applies the changes on the triangular mesh (see Changes)
QList< int > edgeIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of edges intersecting given bounding box It uses spatial indexing.
Q_DECL_DEPRECATED const QVector< QgsMeshVertex > & centroids() const
Returns centroids of the native faces in map CRS.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
const QVector< QgsMeshEdge > & edges() const
Returns edges.
bool contains(const QgsMesh::ElementType &type) const
Returns whether the mesh contains mesh elements of given type.
QgsMeshVertex triangularToNativeCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from triangular mesh coordinates system to native coordinates system.
QVector< QVector3D > vertexNormals(float vertScale) const
Calculates and returns normal vector on each vertex that is part of any face.
QgsMeshVertex nativeToTriangularCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from native coordinates system to triangular mesh coordinates system.
bool update(QgsMesh *nativeMesh, const QgsCoordinateTransform &transform)
Constructs triangular mesh from layer's native mesh and transform to destination CRS.
const QVector< QgsMeshVertex > & faceCentroids() const
Returns centroids of the native faces in map CRS.
QList< int > nativeFaceIndexForRectangle(const QgsRectangle &rectangle) const
Finds indexes of native faces which bounding boxes intersect given bounding box It uses spatial index...
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
const QVector< int > & edgesToNativeEdges() const
Returns mapping between edges and original edges.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
QgsPoint QgsMeshVertex
xyz coords of vertex
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
QVector< QgsMeshFace > faces
int faceCount() const
Returns number of faces.
ElementType
Defines type of mesh elements.
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.
int edgeCount() const
Returns number of edge.
QVector< QgsMeshEdge > edges