QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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 "qgstriangularmesh.h"
19
20#include "meshoptimizer.h"
22#include "qgsgeometry.h"
23#include "qgslogger.h"
24#include "qgsmeshlayerutils.h"
25#include "qgsmeshspatialindex.h"
26#include "qgsmeshutils.h"
27#include "qgsrectangle.h"
28
29#include <QList>
30#include <QString>
31
32using namespace Qt::StringLiterals;
33
34static void triangulateFaces( const QgsMeshFace &face,
35 int nativeIndex,
36 QVector<QgsMeshFace> &destinationFaces,
37 QVector<int> &triangularToNative,
38 const QgsMesh &verticesMeshSource )
39{
40 int vertexCount = face.size();
41 if ( vertexCount < 3 )
42 return;
43
44 while ( vertexCount > 3 )
45 {
46 // clip one ear from last 2 and first vertex
47 const QgsMeshFace ear = { face[vertexCount - 2], face[vertexCount - 1], face[0] };
48 if ( !( std::isnan( verticesMeshSource.vertex( ear[0] ).x() ) ||
49 std::isnan( verticesMeshSource.vertex( ear[1] ).x() ) ||
50 std::isnan( verticesMeshSource.vertex( ear[2] ).x() ) ) )
51 {
52 destinationFaces.push_back( ear );
53 triangularToNative.push_back( nativeIndex );
54 }
55 --vertexCount;
56 }
57
58 const QgsMeshFace triangle = { face[1], face[2], face[0] };
59 if ( !( std::isnan( verticesMeshSource.vertex( triangle[0] ).x() ) ||
60 std::isnan( verticesMeshSource.vertex( triangle[1] ).x() ) ||
61 std::isnan( verticesMeshSource.vertex( triangle[2] ).x() ) ) )
62 {
63 destinationFaces.push_back( triangle );
64 triangularToNative.push_back( nativeIndex );
65 }
66}
67
68void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
69{
70 triangulateFaces( face, nativeIndex, mTriangularMesh.faces, mTrianglesToNativeFaces, mTriangularMesh );
71}
72
73QgsMeshVertex QgsTriangularMesh::transformVertex( const QgsMeshVertex &vertex, Qgis::TransformDirection direction ) const
74{
75 QgsMeshVertex transformedVertex = vertex;
76
77 if ( mCoordinateTransform.isValid() )
78 {
79 try
80 {
81 transformedVertex.transform( mCoordinateTransform, direction );
82 }
83 catch ( QgsCsException &cse )
84 {
85 Q_UNUSED( cse )
86 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
87 transformedVertex = QgsMeshVertex();
88 }
89 }
90
91 return transformedVertex;
92}
93
94QgsMeshVertex QgsTriangularMesh::calculateCentroid( const QgsMeshFace &nativeFace ) const
95{
96 return QgsMeshUtils::centroid( nativeFace, mTriangularMesh.vertices );
97}
98
100{
101 return mAverageTriangleSize;
102}
103
106
107bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform )
108{
109 Q_ASSERT( nativeMesh );
110
111 bool needUpdateVerticesCoordinates = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
112 ( ( mCoordinateTransform.isValid() || transform.isValid() ) &&
113 ( mCoordinateTransform.sourceCrs() != transform.sourceCrs() ||
114 mCoordinateTransform.destinationCrs() != transform.destinationCrs() ||
115 mCoordinateTransform.isValid() != transform.isValid() ) ) ;
116
117 bool needUpdateFrame = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
118 mNativeMeshFaceCentroids.size() != nativeMesh->faces.size() ||
119 mTriangularMesh.faces.size() < nativeMesh->faces.size() ||
120 mTriangularMesh.edges.size() != nativeMesh->edges.size();
121
122
123 // FIND OUT IF UPDATE IS NEEDED
124 if ( ! needUpdateVerticesCoordinates && !needUpdateFrame )
125 return false;
126
127 // CLEAN-UP
128 mTriangularMesh.vertices.clear();
129 if ( needUpdateFrame )
130 {
131 mTriangularMesh.faces.clear();
132 mTriangularMesh.edges.clear();
133 mEdgesToNativeEdges.clear();
134 mTrianglesToNativeFaces.clear();
135 }
136
137 // TRANSFORM VERTICES
138 mCoordinateTransform = transform;
139 mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
140 mExtent.setNull();
141 for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
142 {
143 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh->vertices.at( i ) );
144 mExtent.include( mTriangularMesh.vertices.at( i ) );
145 }
146
147 if ( needUpdateFrame )
148 {
149 // CREATE TRIANGULAR MESH
150 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
151 {
152 const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
153 triangulate( face, i );
154 }
155 }
156
157 // CALCULATE CENTROIDS
158 mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
159 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
160 {
161 const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
162 mNativeMeshFaceCentroids[i] = calculateCentroid( face );
163 }
164
165 // CALCULATE SPATIAL INDEX
166 mSpatialFaceIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Face );
167
168 if ( needUpdateFrame )
169 {
170 // SET ALL TRIANGLE CCW AND COMPUTE AVERAGE SIZE
171 finalizeTriangles();
172 }
173
174 // CREATE EDGES
175 // remove all edges with invalid vertices
176 if ( needUpdateFrame )
177 {
178 const QVector<QgsMeshEdge> edges = nativeMesh->edges;
179 for ( int nativeIndex = 0; nativeIndex < edges.size(); ++nativeIndex )
180 {
181 const QgsMeshEdge &edge = edges.at( nativeIndex );
182 if ( !( std::isnan( mTriangularMesh.vertex( edge.first ).x() ) ||
183 std::isnan( mTriangularMesh.vertex( edge.second ).x() ) ) )
184 {
185 mTriangularMesh.edges.push_back( edge );
186 mEdgesToNativeEdges.push_back( nativeIndex );
187 }
188 }
189 }
190
191 // CALCULATE CENTROIDS
192 mNativeMeshEdgeCentroids.resize( nativeMesh->edgeCount() );
193 for ( int i = 0; i < nativeMesh->edgeCount(); ++i )
194 {
195 const QgsMeshEdge &edge = nativeMesh->edges.at( i ) ;
196 const QgsPoint &a = mTriangularMesh.vertices[edge.first];
197 const QgsPoint &b = mTriangularMesh.vertices[edge.second];
198 mNativeMeshEdgeCentroids[i] = QgsMeshVertex( ( a.x() + b.x() ) / 2.0, ( a.y() + b.y() ) / 2.0, ( a.z() + b.z() ) / 2.0 );
199 }
200
201 // CALCULATE SPATIAL INDEX
202 mSpatialEdgeIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Edge );
203
204 return true;
205}
206
208{
209 return update( nativeMesh, mCoordinateTransform );
210}
211
212void QgsTriangularMesh::finalizeTriangles()
213{
214 mAverageTriangleSize = 0;
215 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
216 {
217 QgsMeshFace &face = mTriangularMesh.faces[i];
218
219 const QgsMeshVertex &v0 = mTriangularMesh.vertex( face[0] );
220 const QgsMeshVertex &v1 = mTriangularMesh.vertex( face[1] );
221 const QgsMeshVertex &v2 = mTriangularMesh.vertex( face[2] );
222
223 QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( v0, v1, v2 );
224
225 mAverageTriangleSize += std::fmax( bbox.width(), bbox.height() );
226
227 QgsMeshUtils::setCounterClockwise( face, v0, v1, v2 );
228 }
229 mAverageTriangleSize /= mTriangularMesh.faceCount();
230}
231
233{
234 return transformVertex( vertex, Qgis::TransformDirection::Forward );
235}
236
238{
239 return transformVertex( vertex, Qgis::TransformDirection::Reverse );
240}
241
243{
245 if ( !mCoordinateTransform.isShortCircuited() )
246 {
247 try
248 {
249 QgsCoordinateTransform extentTransform = mCoordinateTransform;
250 extentTransform.setBallparkTransformsAreAppropriate( true );
252 }
253 catch ( QgsCsException &cse )
254 {
255 Q_UNUSED( cse )
256 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
257 }
258 }
259 else
261
262 return nativeExtent;
263}
264
266{
267 if ( !mIsExtentValid )
268 {
269 mExtent.setNull();
270 for ( int i = 0; i < mTriangularMesh.vertices.size(); ++i )
271 if ( !mTriangularMesh.vertices.at( i ).isEmpty() )
272 mExtent.include( mTriangularMesh.vertices.at( i ) );
273
274 mIsExtentValid = true;
275 }
276 return mExtent;
277}
278
280{
281 return mLod;
282}
283
285{
286 switch ( type )
287 {
289 return mTriangularMesh.vertexCount() != 0;
291 return mTriangularMesh.edgeCount() != 0;
293 return mTriangularMesh.faceCount() != 0;
294 }
295
296 return false;
297}
298
299void QgsTriangularMesh::addVertex( const QgsMeshVertex &vertex )
300{
301 QgsMeshVertex vertexInTriangularCoordinates = nativeToTriangularCoordinates( vertex );
302 mTriangularMesh.vertices.append( vertexInTriangularCoordinates );
303 if ( !vertexInTriangularCoordinates.isEmpty() )
304 mExtent.include( vertexInTriangularCoordinates );
305}
306
307const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
308{
309 return mTriangularMesh.vertices;
310}
311
312const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
313{
314 return mTriangularMesh.faces;
315}
316
317const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
318{
319 return mTriangularMesh.edges;
320}
321
322const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
323{
324 return faceCentroids();
325}
326
327const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
328{
329 return mNativeMeshFaceCentroids;
330}
331
332const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
333{
334 return mNativeMeshEdgeCentroids;
335}
336
338{
339 return mTrianglesToNativeFaces;
340}
341
342const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
343{
344 return mEdgesToNativeEdges;
345}
346
348{
349 QgsPointXY mapPoint;
350 if ( mCoordinateTransform.isValid() )
351 {
352 try
353 {
354 mapPoint = mCoordinateTransform.transform( point );
355 }
356 catch ( QgsCsException &cse )
357 {
358 Q_UNUSED( cse )
359 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
360 mapPoint = point;
361 }
362 }
363 else
364 mapPoint = point;
365
366 return point;
367}
368
370{
371 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
372 for ( const int faceIndex : faceIndexes )
373 {
374 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
375 const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
376 if ( geom.contains( &point ) )
377 return faceIndex;
378 }
379 return -1;
380}
381
383{
384 int triangleIndex = faceIndexForPoint_v2( point );
385 if ( triangleIndex == -1 )
386 return -1;
387
388 if ( triangleIndex < mTrianglesToNativeFaces.count() )
389 return mTrianglesToNativeFaces.at( triangleIndex );
390
391 return -1;
392}
393
395{
396 QSet<int> concernedFaceIndex = QgsMeshUtils::nativeFacesFromTriangles(
397 faceIndexesForRectangle( rectangle ),
399 return concernedFaceIndex.values();
400}
401
403{
404 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
405
406 for ( const int faceIndex : faceIndexes )
407 {
408 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
409 if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
410 return faceIndex;
411 }
412 return -1;
413}
414
416{
417 return mSpatialFaceIndex.intersects( rectangle );
418}
419
421{
422 return mSpatialEdgeIndex.intersects( rectangle );
423}
424
425QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
426{
427 QVector<QVector3D> normals( vertices().count(), QVector3D( 0, 0, 0 ) );
428
429 for ( const auto &face : triangles() )
430 {
431 if ( face.isEmpty() )
432 continue;
433
434 for ( int i = 0; i < 3; i++ )
435 {
436 int index1( face.at( i ) );
437 int index2( face.at( ( i + 1 ) % 3 ) );
438 int index3( face.at( ( i + 2 ) % 3 ) );
439
440 const QgsMeshVertex &vert( vertices().at( index1 ) );
441 const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
442 const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
443
444 QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
445 QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
446
447 normals[index1] += QVector3D::crossProduct( v1, v2 );
448 }
449 }
450 return normals;
451}
452
453QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
454{
455 QVector<QgsTriangularMesh *> simplifiedMeshes;
456
457 if ( mTriangularMesh.edgeCount() != 0 )
458 return simplifiedMeshes;
459
460 if ( !( reductionFactor > 1 ) )
461 return simplifiedMeshes;
462
463 size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
464
465 unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
466
467 QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
468 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
469 {
470 const QgsMeshFace &f = mTriangularMesh.face( i );
471 for ( int j = 0; j < 3; ++j )
472 indexes[i * 3 + j] = f.at( j );
473 }
474
475 QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
476 for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
477 {
478 const QgsMeshVertex &v = mTriangularMesh.vertex( i );
479 vertices[i * 3] = v.x() ;
480 vertices[i * 3 + 1] = v.y() ;
481 vertices[i * 3 + 2] = v.z() ;
482 }
483
484 int path = 0;
485 while ( true )
486 {
487 QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
488 size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
489
490 if ( indexes.size() <= int( maxNumberOfIndexes ) )
491 {
492 delete simplifiedMesh;
493 break;
494 }
495
496 QVector<unsigned int> returnIndexes( indexes.size() );
497 //returned size could be different than goal size but not than the input indexes count
498 size_t size = meshopt_simplifySloppy(
499 returnIndexes.data(),
500 indexes.data(),
501 indexes.size(),
502 vertices.data(),
503 verticesCount,
504 sizeof( float ) * 3,
505 maxNumberOfIndexes
506#if MESHOPTIMIZER_VERSION > 150
507 , 0
508 , nullptr
509#endif
510 );
511
512
513 returnIndexes.resize( size );
514
515 if ( size == 0 || int( size ) >= indexes.size() )
516 {
517 QgsDebugError( u"Mesh simplification failed after %1 path"_s.arg( path + 1 ) );
518 delete simplifiedMesh;
519 break;
520 }
521
522 QgsMesh newMesh;
523 newMesh.vertices = mTriangularMesh.vertices;
524
525 newMesh.faces.resize( returnIndexes.size() / 3 );
526 for ( int i = 0; i < newMesh.faces.size(); ++i )
527 {
528 QgsMeshFace f( 3 );
529 for ( size_t j = 0; j < 3 ; ++j )
530 f[j] = returnIndexes.at( i * 3 + j ) ;
531 newMesh.faces[i ] = f;
532 }
533
534 simplifiedMesh->mTriangularMesh = newMesh;
535 simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
536 simplifiedMesh->finalizeTriangles();
537 simplifiedMeshes.push_back( simplifiedMesh );
538
539 QgsDebugMsgLevel( u"Simplified mesh created with %1 triangles"_s.arg( newMesh.faceCount() ), 2 );
540
541 simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
542 for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
543 {
544 QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
545 double x = 0;
546 double y = 0;
547 for ( size_t j = 0; j < 3 ; ++j )
548 {
549 x += mTriangularMesh.vertex( triangle[j] ).x();
550 y += mTriangularMesh.vertex( triangle[j] ).y();
551 }
552 x /= 3;
553 y /= 3;
554 QgsPoint centroid( x, y );
555 int indexInBaseMesh = faceIndexForPoint_v2( centroid );
556
557 if ( indexInBaseMesh == -1 )
558 {
559 // sometime the centroid of simplified mesh could be outside the base mesh,
560 // so try with vertices of the simplified triangle
561 int j = 0;
562 while ( indexInBaseMesh == -1 && j < 3 )
563 indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
564 }
565
566 if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
567 simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
568 }
569
570 simplifiedMesh->mLod = path + 1;
571 simplifiedMesh->mBaseTriangularMesh = this;
572
573 if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
574 break;
575
576 indexes = returnIndexes;
577 ++path;
578 }
579
580 return simplifiedMeshes;
581}
582
584{
585 //if necessary defined removes triangles index
586 if ( changes.mRemovedTriangleIndexes.isEmpty() && !changes.mNativeFaceIndexesToRemove.isEmpty() )
587 {
588 for ( int nf = 0; nf < changes.mNativeFaceIndexesToRemove.count(); ++nf )
589 {
590 int nativeIndex = changes.mNativeFaceIndexesToRemove.at( nf );
591 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( nf );
592 Q_ASSERT( !nativeFace.isEmpty() );
593
594 QgsRectangle nativeFaceExtent( mTriangularMesh.vertex( nativeFace.at( 0 ) ), mTriangularMesh.vertex( nativeFace.at( 0 ) ) );
595 for ( int i = 1; i < nativeFace.count(); ++i )
596 {
597 const QgsMeshVertex &triangularVertex = mTriangularMesh.vertex( nativeFace.at( i ) );
598 nativeFaceExtent.include( triangularVertex );
599 }
600
601 QList<int> concernedTriangle = faceIndexesForRectangle( nativeFaceExtent );
602 //Remove only those corresponding to the native face
603 for ( int i = 0; i < concernedTriangle.count(); ++i )
604 {
605 int triangleIndex = concernedTriangle.at( i );
606 if ( mTrianglesToNativeFaces.at( triangleIndex ) == nativeIndex )
607 changes.mRemovedTriangleIndexes.append( triangleIndex );
608 }
609 }
610 }
611
612 if ( changes.mOldZValue.isEmpty() && !changes.mNewZValue.isEmpty() )
613 {
614 changes.mOldZValue.reserve( changes.mNewZValue.count() );
615 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
616 changes.mOldZValue.append( mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
617 }
618
619 if ( changes.mTriangleIndexesGeometryChanged.isEmpty() && !changes.mNativeFaceIndexesGeometryChanged.isEmpty() )
620 {
621 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
622 {
623 const QgsMeshFace &nativeFace = changes.mNativeFacesGeometryChanged.at( i );
624 if ( nativeFace.count() < 2 )
625 continue;
626 QgsRectangle bbox( mTriangularMesh.vertices.at( nativeFace.at( 0 ) ), mTriangularMesh.vertices.at( nativeFace.at( 1 ) ) );
627
628 for ( int i = 2; i < nativeFace.count(); ++i )
629 bbox.include( mTriangularMesh.vertices.at( nativeFace.at( i ) ) );
630
631 QList<int> triangleIndexes = faceIndexesForRectangle( bbox );
632 int pos = 0;
633 while ( pos < triangleIndexes.count() )
634 {
635 if ( trianglesToNativeFaces().at( triangleIndexes.at( pos ) ) !=
636 changes.mNativeFaceIndexesGeometryChanged.at( i ) )
637 triangleIndexes.removeAt( pos );
638 else
639 ++pos;
640 }
641 changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
642 }
643 }
644
645 // add vertices
646 for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
647 addVertex( vertex );
648
649 // add faces
650 if ( !changes.mNativeFacesToAdd.isEmpty() )
651 {
652 changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
653 int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
654 for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
655 {
656 const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
657 triangulate( nativeFace, firstNewNativeFacesIndex + i );
658 mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
659 }
660
661 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
662 mSpatialFaceIndex.addFace( i, mTriangularMesh );
663 }
664
665 // remove faces
666 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
667 {
668 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
669 mTrianglesToNativeFaces[triangleIndex] = -1;
670 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
671 mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
672 }
673
674 for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
675 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
676
677 // remove vertices
678 for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
679 mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
680
681 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
682 mIsExtentValid = false;
683
684 // change Z value
685 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
686 {
687 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
688 mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
689 }
690
691 //remove outdated spatial index
692 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
693 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
694
695 // change (X,Y) of vertices
696 for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
697 {
698 const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
699 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
700 nativeCoordinates.y(),
701 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
702
703 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
704 }
705
706 //restore spatial undex
707 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
708 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
709
710 //update native faces
711 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
712 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
713}
714
716{
717 //reverse added faces and added vertices
718 if ( !changes.mNativeFacesToAdd.isEmpty() )
719 {
720 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
721 mSpatialFaceIndex.removeFace( i, mTriangularMesh );
722
723 int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
724
725 mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
726 mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
727 mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
728 }
729
730 int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
731 mTriangularMesh.vertices.resize( initialVerticesCount );
732
733 if ( !changes.mAddedVertices.isEmpty() )
734 mIsExtentValid = false;
735
736 // for each vertex to remove we need to update the vertices with the native vertex
737 for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
738 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
739
740 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
741 mIsExtentValid = false;
742
743 // reverse removed faces
744 QVector<QgsMeshFace> restoredTriangles;
745 QVector<int> restoredTriangularToNative;
746 for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
747 {
748 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
749 triangulateFaces( nativeFace,
750 changes.mNativeFaceIndexesToRemove.at( i ),
751 restoredTriangles,
752 restoredTriangularToNative,
753 mTriangularMesh );
754 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
755 }
756 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
757 {
758 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
759 mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
760 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
761 mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
762 }
763
764 // reverse Z value
765 for ( int i = 0; i < changes.mOldZValue.count(); ++i )
766 {
767 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
768 mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
769 }
770
771 //remove outdated spatial index
772 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
773 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
774
775 // reverse (X,Y) of vertices
776 for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
777 {
778 const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
779 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
780 nativeCoordinates.y(),
781 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
782
783 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
784 }
785
786 //restore spatial undex
787 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
788 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
789
790 //update native faces
791 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
792 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
793}
794
796 const QgsMesh &nativeMesh )
797{
798 mAddedVertices = topologicalChanges.addedVertices();
799 mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
800 mNativeFacesToAdd = topologicalChanges.addedFaces();
801 mNativeFacesToRemove = topologicalChanges.removedFaces();
802 mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
803 mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
804 mNewZValue = topologicalChanges.newVerticesZValues();
805 mNewXYValue = topologicalChanges.newVerticesXYValues();
806 mOldXYValue = topologicalChanges.oldVerticesXYValues();
807
808 mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
809 mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
810 for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
811 mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
812}
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2729
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2730
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
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.
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.
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:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:742
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:390
double y
Definition qgspoint.h:57
A rectangle specified with double values.
void include(const QgsPointXY &p)
Updates the rectangle to include the specified point.
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.
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:63
#define QgsDebugError(str)
Definition qgslogger.h:59
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.
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