QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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, int nativeIndex, QVector<QgsMeshFace> &destinationFaces, QVector<int> &triangularToNative, const QgsMesh &verticesMeshSource )
35{
36 int vertexCount = face.size();
37 if ( vertexCount < 3 )
38 return;
39
40 while ( vertexCount > 3 )
41 {
42 // clip one ear from last 2 and first vertex
43 const QgsMeshFace ear = { face[vertexCount - 2], face[vertexCount - 1], face[0] };
44 if ( !( std::isnan( verticesMeshSource.vertex( ear[0] ).x() ) || std::isnan( verticesMeshSource.vertex( ear[1] ).x() ) || std::isnan( verticesMeshSource.vertex( ear[2] ).x() ) ) )
45 {
46 destinationFaces.push_back( ear );
47 triangularToNative.push_back( nativeIndex );
48 }
49 --vertexCount;
50 }
51
52 const QgsMeshFace triangle = { face[1], face[2], face[0] };
53 if ( !( std::isnan( verticesMeshSource.vertex( triangle[0] ).x() ) || std::isnan( verticesMeshSource.vertex( triangle[1] ).x() ) || std::isnan( verticesMeshSource.vertex( triangle[2] ).x() ) ) )
54 {
55 destinationFaces.push_back( triangle );
56 triangularToNative.push_back( nativeIndex );
57 }
58}
59
60void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
61{
62 triangulateFaces( face, nativeIndex, mTriangularMesh.faces, mTrianglesToNativeFaces, mTriangularMesh );
63}
64
65QgsMeshVertex QgsTriangularMesh::transformVertex( const QgsMeshVertex &vertex, Qgis::TransformDirection direction ) const
66{
67 QgsMeshVertex transformedVertex = vertex;
68
69 if ( mCoordinateTransform.isValid() )
70 {
71 try
72 {
73 transformedVertex.transform( mCoordinateTransform, direction );
74 }
75 catch ( QgsCsException &cse )
76 {
77 Q_UNUSED( cse )
78 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
79 transformedVertex = QgsMeshVertex();
80 }
81 }
82
83 return transformedVertex;
84}
85
86QgsMeshVertex QgsTriangularMesh::calculateCentroid( const QgsMeshFace &nativeFace ) const
87{
88 return QgsMeshUtils::centroid( nativeFace, mTriangularMesh.vertices );
89}
90
92{
93 return mAverageTriangleSize;
94}
95
98
99bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform )
100{
101 Q_ASSERT( nativeMesh );
102
103 bool needUpdateVerticesCoordinates = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
104 ( ( mCoordinateTransform.isValid() || transform.isValid() ) &&
105 ( mCoordinateTransform.sourceCrs() != transform.sourceCrs() ||
106 mCoordinateTransform.destinationCrs() != transform.destinationCrs() ||
107 mCoordinateTransform.isValid() != transform.isValid() ) ) ;
108
109 bool needUpdateFrame = mTriangularMesh.vertices.size() != nativeMesh->vertices.size()
110 || mNativeMeshFaceCentroids.size() != nativeMesh->faces.size()
111 || mTriangularMesh.faces.size() < nativeMesh->faces.size()
112 || mTriangularMesh.edges.size() != nativeMesh->edges.size();
113
114
115 // FIND OUT IF UPDATE IS NEEDED
116 if ( !needUpdateVerticesCoordinates && !needUpdateFrame )
117 return false;
118
119 // CLEAN-UP
120 mTriangularMesh.vertices.clear();
121 if ( needUpdateFrame )
122 {
123 mTriangularMesh.faces.clear();
124 mTriangularMesh.edges.clear();
125 mEdgesToNativeEdges.clear();
126 mTrianglesToNativeFaces.clear();
127 }
128
129 // TRANSFORM VERTICES
130 mCoordinateTransform = transform;
131 mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
132 mExtent.setNull();
133 for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
134 {
135 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh->vertices.at( i ) );
136 mExtent.include( mTriangularMesh.vertices.at( i ) );
137 }
138
139 if ( needUpdateFrame )
140 {
141 // CREATE TRIANGULAR MESH
142 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
143 {
144 const QgsMeshFace &face = nativeMesh->faces.at( i );
145 triangulate( face, i );
146 }
147 }
148
149 // CALCULATE CENTROIDS
150 mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
151 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
152 {
153 const QgsMeshFace &face = nativeMesh->faces.at( i );
154 mNativeMeshFaceCentroids[i] = calculateCentroid( face );
155 }
156
157 // CALCULATE SPATIAL INDEX
158 mSpatialFaceIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Face );
159
160 if ( needUpdateFrame )
161 {
162 // SET ALL TRIANGLE CCW AND COMPUTE AVERAGE SIZE
163 finalizeTriangles();
164 }
165
166 // CREATE EDGES
167 // remove all edges with invalid vertices
168 if ( needUpdateFrame )
169 {
170 const QVector<QgsMeshEdge> edges = nativeMesh->edges;
171 for ( int nativeIndex = 0; nativeIndex < edges.size(); ++nativeIndex )
172 {
173 const QgsMeshEdge &edge = edges.at( nativeIndex );
174 if ( !( std::isnan( mTriangularMesh.vertex( edge.first ).x() ) || std::isnan( mTriangularMesh.vertex( edge.second ).x() ) ) )
175 {
176 mTriangularMesh.edges.push_back( edge );
177 mEdgesToNativeEdges.push_back( nativeIndex );
178 }
179 }
180 }
181
182 // CALCULATE CENTROIDS
183 mNativeMeshEdgeCentroids.resize( nativeMesh->edgeCount() );
184 for ( int i = 0; i < nativeMesh->edgeCount(); ++i )
185 {
186 const QgsMeshEdge &edge = nativeMesh->edges.at( i );
187 const QgsPoint &a = mTriangularMesh.vertices[edge.first];
188 const QgsPoint &b = mTriangularMesh.vertices[edge.second];
189 mNativeMeshEdgeCentroids[i] = QgsMeshVertex( ( a.x() + b.x() ) / 2.0, ( a.y() + b.y() ) / 2.0, ( a.z() + b.z() ) / 2.0 );
190 }
191
192 // CALCULATE SPATIAL INDEX
193 mSpatialEdgeIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Edge );
194
195 return true;
196}
197
199{
200 return update( nativeMesh, mCoordinateTransform );
201}
202
203void QgsTriangularMesh::finalizeTriangles()
204{
205 mAverageTriangleSize = 0;
206 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
207 {
208 QgsMeshFace &face = mTriangularMesh.faces[i];
209
210 const QgsMeshVertex &v0 = mTriangularMesh.vertex( face[0] );
211 const QgsMeshVertex &v1 = mTriangularMesh.vertex( face[1] );
212 const QgsMeshVertex &v2 = mTriangularMesh.vertex( face[2] );
213
214 QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( v0, v1, v2 );
215
216 mAverageTriangleSize += std::fmax( bbox.width(), bbox.height() );
217
218 QgsMeshUtils::setCounterClockwise( face, v0, v1, v2 );
219 }
220 mAverageTriangleSize /= mTriangularMesh.faceCount();
221}
222
224{
225 return transformVertex( vertex, Qgis::TransformDirection::Forward );
226}
227
229{
230 return transformVertex( vertex, Qgis::TransformDirection::Reverse );
231}
232
234{
236 if ( !mCoordinateTransform.isShortCircuited() )
237 {
238 try
239 {
240 QgsCoordinateTransform extentTransform = mCoordinateTransform;
241 extentTransform.setBallparkTransformsAreAppropriate( true );
243 }
244 catch ( QgsCsException &cse )
245 {
246 Q_UNUSED( cse )
247 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
248 }
249 }
250 else
252
253 return nativeExtent;
254}
255
257{
258 if ( !mIsExtentValid )
259 {
260 mExtent.setNull();
261 for ( int i = 0; i < mTriangularMesh.vertices.size(); ++i )
262 if ( !mTriangularMesh.vertices.at( i ).isEmpty() )
263 mExtent.include( mTriangularMesh.vertices.at( i ) );
264
265 mIsExtentValid = true;
266 }
267 return mExtent;
268}
269
271{
272 return mLod;
273}
274
276{
277 switch ( type )
278 {
280 return mTriangularMesh.vertexCount() != 0;
282 return mTriangularMesh.edgeCount() != 0;
284 return mTriangularMesh.faceCount() != 0;
285 }
286
287 return false;
288}
289
290void QgsTriangularMesh::addVertex( const QgsMeshVertex &vertex )
291{
292 QgsMeshVertex vertexInTriangularCoordinates = nativeToTriangularCoordinates( vertex );
293 mTriangularMesh.vertices.append( vertexInTriangularCoordinates );
294 if ( !vertexInTriangularCoordinates.isEmpty() )
295 mExtent.include( vertexInTriangularCoordinates );
296}
297
298const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
299{
300 return mTriangularMesh.vertices;
301}
302
303const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
304{
305 return mTriangularMesh.faces;
306}
307
308const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
309{
310 return mTriangularMesh.edges;
311}
312
313const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
314{
315 return faceCentroids();
316}
317
318const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
319{
320 return mNativeMeshFaceCentroids;
321}
322
323const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
324{
325 return mNativeMeshEdgeCentroids;
326}
327
329{
330 return mTrianglesToNativeFaces;
331}
332
333const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
334{
335 return mEdgesToNativeEdges;
336}
337
339{
340 QgsPointXY mapPoint;
341 if ( mCoordinateTransform.isValid() )
342 {
343 try
344 {
345 mapPoint = mCoordinateTransform.transform( point );
346 }
347 catch ( QgsCsException &cse )
348 {
349 Q_UNUSED( cse )
350 QgsDebugError( u"Caught CRS exception %1"_s.arg( cse.what() ) );
351 mapPoint = point;
352 }
353 }
354 else
355 mapPoint = point;
356
357 return point;
358}
359
361{
362 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
363 for ( const int faceIndex : faceIndexes )
364 {
365 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
366 const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
367 if ( geom.contains( &point ) )
368 return faceIndex;
369 }
370 return -1;
371}
372
374{
375 int triangleIndex = faceIndexForPoint_v2( point );
376 if ( triangleIndex == -1 )
377 return -1;
378
379 if ( triangleIndex < mTrianglesToNativeFaces.count() )
380 return mTrianglesToNativeFaces.at( triangleIndex );
381
382 return -1;
383}
384
386{
387 QSet<int> concernedFaceIndex = QgsMeshUtils::nativeFacesFromTriangles( faceIndexesForRectangle( rectangle ), trianglesToNativeFaces() );
388 return concernedFaceIndex.values();
389}
390
392{
393 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
394
395 for ( const int faceIndex : faceIndexes )
396 {
397 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
398 if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
399 return faceIndex;
400 }
401 return -1;
402}
403
405{
406 return mSpatialFaceIndex.intersects( rectangle );
407}
408
410{
411 return mSpatialEdgeIndex.intersects( rectangle );
412}
413
414QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
415{
416 QVector<QVector3D> normals( vertices().count(), QVector3D( 0, 0, 0 ) );
417
418 for ( const auto &face : triangles() )
419 {
420 if ( face.isEmpty() )
421 continue;
422
423 for ( int i = 0; i < 3; i++ )
424 {
425 int index1( face.at( i ) );
426 int index2( face.at( ( i + 1 ) % 3 ) );
427 int index3( face.at( ( i + 2 ) % 3 ) );
428
429 const QgsMeshVertex &vert( vertices().at( index1 ) );
430 const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
431 const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
432
433 QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
434 QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
435
436 normals[index1] += QVector3D::crossProduct( v1, v2 );
437 }
438 }
439 return normals;
440}
441
442QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
443{
444 QVector<QgsTriangularMesh *> simplifiedMeshes;
445
446 if ( mTriangularMesh.edgeCount() != 0 )
447 return simplifiedMeshes;
448
449 if ( !( reductionFactor > 1 ) )
450 return simplifiedMeshes;
451
452 size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
453
454 unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
455
456 QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
457 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
458 {
459 const QgsMeshFace &f = mTriangularMesh.face( i );
460 for ( int j = 0; j < 3; ++j )
461 indexes[i * 3 + j] = f.at( j );
462 }
463
464 QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
465 for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
466 {
467 const QgsMeshVertex &v = mTriangularMesh.vertex( i );
468 vertices[i * 3] = v.x();
469 vertices[i * 3 + 1] = v.y();
470 vertices[i * 3 + 2] = v.z();
471 }
472
473 int path = 0;
474 while ( true )
475 {
476 QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
477 size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
478
479 if ( indexes.size() <= int( maxNumberOfIndexes ) )
480 {
481 delete simplifiedMesh;
482 break;
483 }
484
485 QVector<unsigned int> returnIndexes( indexes.size() );
486 //returned size could be different than goal size but not than the input indexes count
487 size_t size = meshopt_simplifySloppy(
488 returnIndexes.data(),
489 indexes.data(),
490 indexes.size(),
491 vertices.data(),
492 verticesCount,
493 sizeof( float ) * 3,
494 maxNumberOfIndexes
495#if MESHOPTIMIZER_VERSION > 150
496 ,
497 0,
498 nullptr
499#endif
500 );
501
502
503 returnIndexes.resize( size );
504
505 if ( size == 0 || int( size ) >= indexes.size() )
506 {
507 QgsDebugError( u"Mesh simplification failed after %1 path"_s.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( u"Simplified mesh created with %1 triangles"_s.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 ) ) != changes.mNativeFaceIndexesGeometryChanged.at( i ) )
626 triangleIndexes.removeAt( pos );
627 else
628 ++pos;
629 }
630 changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
631 }
632 }
633
634 // add vertices
635 for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
636 addVertex( vertex );
637
638 // add faces
639 if ( !changes.mNativeFacesToAdd.isEmpty() )
640 {
641 changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
642 int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
643 for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
644 {
645 const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
646 triangulate( nativeFace, firstNewNativeFacesIndex + i );
647 mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
648 }
649
650 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
651 mSpatialFaceIndex.addFace( i, mTriangularMesh );
652 }
653
654 // remove faces
655 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
656 {
657 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
658 mTrianglesToNativeFaces[triangleIndex] = -1;
659 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
660 mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
661 }
662
663 for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
664 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
665
666 // remove vertices
667 for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
668 mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
669
670 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
671 mIsExtentValid = false;
672
673 // change Z value
674 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
675 {
676 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
677 mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
678 }
679
680 //remove outdated spatial index
681 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
682 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
683
684 // change (X,Y) of vertices
685 for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
686 {
687 const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
688 const QgsMeshVertex nativeVertex( nativeCoordinates.x(), nativeCoordinates.y(), mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
689
690 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
691 }
692
693 //restore spatial undex
694 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
695 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
696
697 //update native faces
698 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
699 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
700}
701
703{
704 //reverse added faces and added vertices
705 if ( !changes.mNativeFacesToAdd.isEmpty() )
706 {
707 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
708 mSpatialFaceIndex.removeFace( i, mTriangularMesh );
709
710 int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
711
712 mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
713 mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
714 mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
715 }
716
717 int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
718 mTriangularMesh.vertices.resize( initialVerticesCount );
719
720 if ( !changes.mAddedVertices.isEmpty() )
721 mIsExtentValid = false;
722
723 // for each vertex to remove we need to update the vertices with the native vertex
724 for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
725 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
726
727 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
728 mIsExtentValid = false;
729
730 // reverse removed faces
731 QVector<QgsMeshFace> restoredTriangles;
732 QVector<int> restoredTriangularToNative;
733 for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
734 {
735 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
736 triangulateFaces( nativeFace, changes.mNativeFaceIndexesToRemove.at( i ), restoredTriangles, restoredTriangularToNative, mTriangularMesh );
737 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
738 }
739 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
740 {
741 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
742 mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
743 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
744 mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
745 }
746
747 // reverse Z value
748 for ( int i = 0; i < changes.mOldZValue.count(); ++i )
749 {
750 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
751 mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
752 }
753
754 //remove outdated spatial index
755 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
756 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
757
758 // reverse (X,Y) of vertices
759 for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
760 {
761 const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
762 const QgsMeshVertex nativeVertex( nativeCoordinates.x(), nativeCoordinates.y(), mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
763
764 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
765 }
766
767 //restore spatial undex
768 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
769 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
770
771 //update native faces
772 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
773 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
774}
775
776QgsTriangularMesh::Changes::Changes( const QgsTopologicalMesh::Changes &topologicalChanges, const QgsMesh &nativeMesh )
777{
778 mAddedVertices = topologicalChanges.addedVertices();
779 mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
780 mNativeFacesToAdd = topologicalChanges.addedFaces();
781 mNativeFacesToRemove = topologicalChanges.removedFaces();
782 mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
783 mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
784 mNewZValue = topologicalChanges.newVerticesZValues();
785 mNewXYValue = topologicalChanges.newVerticesXYValues();
786 mOldXYValue = topologicalChanges.oldVerticesXYValues();
787
788 mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
789 mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
790 for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
791 mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
792}
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2764
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2765
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2766
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:766
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:414
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