QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsmeshadvancedediting.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshadvancedediting.cpp - QgsMeshAdvancedEditing
3
4 ---------------------
5 begin : 9.7.2021
6 copyright : (C) 2021 by Vincent Cloarec
7 email : vcloarec at gmail dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
17
18#include "poly2tri.h"
19#include "qgis.h"
20#include "qgsexpression.h"
22#include "qgsmesheditor.h"
23#include "qgsmeshlayer.h"
24#include "qgsproject.h"
26#include "qgsterrainprovider.h"
27
29
31
32void QgsMeshAdvancedEditing::setInputVertices( const QList<int> verticesIndexes )
33{
34 mInputVertices = verticesIndexes;
35}
36
37void QgsMeshAdvancedEditing::setInputFaces( const QList<int> faceIndexes )
38{
39 mInputFaces = faceIndexes;
40}
41
43{
44 return mMessage;
45}
46
48{
49 mInputVertices.clear();
50 mInputFaces.clear();
51 mMessage.clear();
52 mIsFinished = false;
54}
55
57{
58 return mIsFinished;
59}
60
62{
63 return QString();
64}
65
67
68QgsTopologicalMesh::Changes QgsMeshEditRefineFaces::apply( QgsMeshEditor *meshEditor )
69{
70 QSet<int> facesToRefine = qgis::listToSet( mInputFaces );
71 QHash<int, FaceRefinement> facesRefinement;
72 QHash<int, BorderFace> borderFaces;
73
74 createNewVerticesAndRefinedFaces( meshEditor, facesToRefine, facesRefinement );
75 if ( !createNewBorderFaces( meshEditor, facesToRefine, facesRefinement, borderFaces ) )
77
78 // create new vertices
79 const QgsMesh &nativeMesh = *meshEditor->topologicalMesh().mesh();
80 mAddedFacesFirstIndex = nativeMesh.faceCount();
81
82 mFaceIndexesToRemove = facesRefinement.keys();
83 mFaceIndexesToRemove.append( borderFaces.keys() );
84 mFacesToRemove.resize( mFaceIndexesToRemove.size() );
86 for ( int i = 0; i < mFaceIndexesToRemove.count(); ++i )
87 {
88 int faceIndexToRemove = mFaceIndexesToRemove.at( i );
89 mFacesToRemove[i] = nativeMesh.face( faceIndexToRemove );
90 mFacesNeighborhoodToRemove[i] = meshEditor->topologicalMesh().neighborsOfFace( faceIndexToRemove );
91 }
92
93 meshEditor->topologicalMesh().applyChanges( *this );
94
95 mIsFinished = true;
96
97 return *this;
98}
99
100void QgsMeshEditRefineFaces::createNewVerticesAndRefinedFaces( QgsMeshEditor *meshEditor,
101 QSet<int> &facesToRefine,
102 QHash<int, FaceRefinement> &facesRefinement )
103{
104 const QgsTopologicalMesh &topology = meshEditor->topologicalMesh();
105 const QgsMesh &mesh = *meshEditor->topologicalMesh().mesh();
106
107 int startingVertexIndex = mesh.vertexCount();
108 int startingGlobalFaceIndex = mesh.faceCount();
109
110 auto canBeRefined = [ & ]( int fi )->bool
111 {
112 if ( fi < 0 || fi > mesh.faceCount() )
113 return false;
114 int fs = mesh.face( fi ).size();
115 return fs == 3 || fs == 4;
116
117 };
118
119 for ( const int faceIndex : std::as_const( mInputFaces ) )
120 {
121 FaceRefinement refinement;
122
123 const QgsMeshFace &face = mesh.face( faceIndex );
124 int faceSize = face.size();
125
126 QVector<int> addedVerticesIndex( faceSize, -1 );
127
128 if ( canBeRefined( faceIndex ) )
129 {
130 refinement.newVerticesLocalIndex.reserve( faceSize );
131 refinement.refinedFaceNeighbor.reserve( faceSize );
132 refinement.borderFaceNeighbor.reserve( faceSize );
133 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
134
135 double zValueSum = 0;
136
137 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
138 {
139 refinement.refinedFaceNeighbor.append( false );
140 refinement.borderFaceNeighbor.append( false );
141
142 int neighborFaceIndex = neighbors.at( positionInFace );
143 bool needCreateVertex = true;
144 if ( neighborFaceIndex != -1 && facesToRefine.contains( neighborFaceIndex ) && canBeRefined( neighborFaceIndex ) )
145 {
146 int neighborFaceSize = mesh.face( neighborFaceIndex ).size();
147 int positionVertexInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, face.at( positionInFace ), neighborFaceIndex );
148 positionVertexInNeighbor = ( positionVertexInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
149 refinement.refinedFaceNeighbor[positionInFace] = true;
150 QHash<int, FaceRefinement>::iterator it = facesRefinement.find( neighborFaceIndex );
151 if ( it != facesRefinement.end() )
152 {
153 FaceRefinement &neighborRefinement = it.value();
154 int existingVertexLocalIndex = neighborRefinement.newVerticesLocalIndex.at( positionVertexInNeighbor );
155 refinement.newVerticesLocalIndex.append( existingVertexLocalIndex );
156 needCreateVertex = false;
157 zValueSum += mVerticesToAdd.at( existingVertexLocalIndex ).z();
158 }
159 }
160
161 if ( needCreateVertex )
162 {
163 const QgsMeshVertex &vertex1 = mesh.vertex( face.at( positionInFace ) );
164 const QgsMeshVertex &vertex2 = mesh.vertex( face.at( ( positionInFace + 1 ) % faceSize ) );
165
166 refinement.newVerticesLocalIndex.append( mVerticesToAdd.count() );
167 addedVerticesIndex[positionInFace] = mVerticesToAdd.count();
168
169 mVerticesToAdd.append( QgsMeshVertex( ( vertex1.x() + vertex2.x() ) / 2,
170 ( vertex1.y() + vertex2.y() ) / 2,
171 ( vertex1.z() + vertex2.z() ) / 2 ) );
172
173 zValueSum += mVerticesToAdd.last().z();
174 mVertexToFaceToAdd.append( -1 );
175
176 }
177 }
178
179 int faceStartIndex = startingGlobalFaceIndex + mFacesToAdd.count();
180
181 if ( faceSize == 3 )
182 {
183 for ( int i = 0; i < faceSize; ++i )
184 {
185 QgsMeshFace newFace( {face.at( i ),
186 refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex,
187 refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex} );
188 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
189 mFacesToAdd.append( newFace );
190 mFacesNeighborhoodToAdd.append( {-1, faceStartIndex + 3, -1} );
191
192 }
193 QgsMeshFace newFace( {refinement.newVerticesLocalIndex.at( 0 ) + startingVertexIndex,
194 refinement.newVerticesLocalIndex.at( 1 ) + startingVertexIndex,
195 refinement.newVerticesLocalIndex.at( ( 2 ) % faceSize ) + startingVertexIndex} );
196 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
197 mFacesToAdd.append( newFace );
198 mFacesNeighborhoodToAdd.append( {faceStartIndex + 1, faceStartIndex + 2, faceStartIndex} );
199 }
200
201 if ( faceSize == 4 )
202 {
203 int centerVertexIndex = mVerticesToAdd.count() + startingVertexIndex;
204 refinement.newCenterVertexIndex = mVerticesToAdd.count();
205 QgsMeshVertex centerVertex = QgsMeshUtils::centroid( face, mesh.vertices );
206 mVerticesToAdd.append( QgsMeshVertex( centerVertex.x(), centerVertex.y(), zValueSum / 4 ) );
207
208 for ( int i = 0; i < faceSize; ++i )
209 {
210 QgsMeshFace newFace( {face.at( i ),
211 refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex,
212 centerVertexIndex,
213 refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex} );
214 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
215 mFacesToAdd.append( newFace );
216 mFacesNeighborhoodToAdd.append( {-1, faceStartIndex + ( i + 1 ) % 4, faceStartIndex + ( i + 3 ) % 4, -1} );
217 }
218
219 mVertexToFaceToAdd.append( mFacesToAdd.count() + startingGlobalFaceIndex - 1 );
220 }
221 else
222 refinement.newCenterVertexIndex = -1;
223
224 facesRefinement.insert( faceIndex, refinement );
225
226 //look for vertexToFace
227 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
228 {
229 if ( addedVerticesIndex.at( positionInFace ) != -1 )
230 {
231 mVertexToFaceToAdd[addedVerticesIndex.at( positionInFace )] =
232 refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex;
233 }
234
235 int vertexIndex = face.at( positionInFace );
236 if ( topology.firstFaceLinked( vertexIndex ) == faceIndex )
237 mVerticesToFaceChanges.append( {vertexIndex, faceIndex, refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex} );
238 }
239 }
240 else
241 {
242 //not 3 or 4 vertices, we do not refine this face
243 facesToRefine.remove( faceIndex );
244 }
245 }
246
247 //all new refined faces are in place, we can build their neighborhood with other new refined faces
248 for ( QHash<int, FaceRefinement>::iterator it = facesRefinement.begin(); it != facesRefinement.end(); ++it )
249 {
250 int faceIndex = it.key();
251 FaceRefinement &faceRefinement = it.value();
252 const QgsMeshFace &face = mesh.face( faceIndex );
253 int faceSize = face.size();
254
255 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
256
257 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
258 {
259 if ( faceRefinement.refinedFaceNeighbor.at( positionInFace ) )
260 {
261 int neighborIndex = neighbors.at( positionInFace );
262 int firstVertexIndex = face.at( positionInFace );
263 int secondVertexIndex = faceRefinement.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
264
265 const FaceRefinement &otherRefinement = facesRefinement.value( neighborIndex );
266 const QgsMeshFace &otherFace = mesh.face( neighborIndex );
267 int otherFaceSize = otherFace.size();
268 int otherPositionInface = ( QgsTopologicalMesh::vertexPositionInFace( firstVertexIndex, otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
269
270 int newFace1ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace ) );
271 const QgsMeshFace &newFace1 = mFacesToAdd.at( newFace1ChangesIndex );
272 int positionInNewface1Index = QgsTopologicalMesh::vertexPositionInFace( firstVertexIndex, newFace1 );
273
274 int newFace2ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace + 1 ) % faceSize );
275 const QgsMeshFace &newFace2 = mFacesToAdd.at( newFace2ChangesIndex );
276 int positionInNewface2Index = QgsTopologicalMesh::vertexPositionInFace( secondVertexIndex, newFace2 );
277
278 int otherNewFace1ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface ) % otherFaceSize );
279 int otherNewFace2ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface + 1 ) % otherFaceSize );
280
281 mFacesNeighborhoodToAdd[newFace1ChangesIndex][positionInNewface1Index] = otherNewFace2ChangesIndex + startingGlobalFaceIndex;
282 mFacesNeighborhoodToAdd[newFace2ChangesIndex][positionInNewface2Index] = otherNewFace1ChangesIndex + startingGlobalFaceIndex;
283 }
284 }
285 }
286}
287
288bool QgsMeshEditRefineFaces::createNewBorderFaces( QgsMeshEditor *meshEditor,
289 const QSet<int> &facesToRefine,
290 QHash<int, FaceRefinement> &facesRefinement,
291 QHash<int, BorderFace> &borderFaces )
292{
293 const QgsTopologicalMesh &topology = meshEditor->topologicalMesh();
294 const QgsMesh &mesh = *meshEditor->topologicalMesh().mesh();
295
296 int startingVertexIndex = mesh.vertexCount();
297 int startingFaceChangesGlobalIndex = mesh.faceCount();
298
299 // first create the border faces
300 for ( int faceIndexToRefine : facesToRefine )
301 {
302 const QgsMeshFace &faceToRefine = mesh.face( faceIndexToRefine );
303 int faceToRefineSize = faceToRefine.size();
304
305 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndexToRefine );
306
307 QHash<int, FaceRefinement>::iterator itFace = facesRefinement.find( faceIndexToRefine );
308
309 if ( itFace == facesRefinement.end() )
310 Q_ASSERT( false ); // That could not happen
311
312 FaceRefinement &refinement = itFace.value();
313
314 for ( int posInFaceToRefine = 0; posInFaceToRefine < faceToRefineSize; ++posInFaceToRefine )
315 {
316 int neighborFaceIndex = neighbors.at( posInFaceToRefine );
317 if ( neighborFaceIndex != -1 && !facesToRefine.contains( neighborFaceIndex ) )
318 {
319 const QgsMeshFace &neighborFace = mesh.face( neighborFaceIndex );
320 int neighborFaceSize = neighborFace.size();
321 int positionInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, faceToRefine.at( posInFaceToRefine ), neighborFaceIndex );
322 positionInNeighbor = ( positionInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
323
324 QHash<int, BorderFace>::iterator it = borderFaces.find( neighborFaceIndex );
325 if ( it == borderFaces.end() ) //not present for now--> create a border face
326 {
327 BorderFace borderFace;
328 for ( int i = 0; i < neighborFaceSize; ++i )
329 {
330 borderFace.unchangeFacesNeighbor.append( false );
331 borderFace.borderFacesNeighbor.append( false );
332 if ( i == positionInNeighbor )
333 {
334 borderFace.refinedFacesNeighbor.append( true );
335 borderFace.newVerticesLocalIndex.append( refinement.newVerticesLocalIndex.at( posInFaceToRefine ) );
336 }
337 else
338 {
339 borderFace.refinedFacesNeighbor.append( false );
340 borderFace.newVerticesLocalIndex.append( -1 );
341 }
342 }
343 borderFaces.insert( neighborFaceIndex, borderFace );
344 }
345 else
346 {
347 BorderFace &borderFace = it.value();
348 for ( int i = 0; i < neighborFaceSize; ++i )
349 {
350 if ( i == positionInNeighbor )
351 {
352 borderFace.unchangeFacesNeighbor[i] = false;
353 borderFace.borderFacesNeighbor[i] = false;
354 borderFace.refinedFacesNeighbor[i] = true;
355 borderFace.newVerticesLocalIndex[i] = refinement.newVerticesLocalIndex.at( posInFaceToRefine );
356 }
357 }
358 }
359 }
360 }
361 }
362
363 // now build information about neighbors
364 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
365 {
366 int faceIndex = it.key();
367 BorderFace &borderFace = it.value();
368
369 const QgsMeshFace &face = mesh.face( faceIndex );
370 int faceSize = face.size();
371
372 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
373 for ( int posInFace = 0; posInFace < faceSize; ++posInFace )
374 {
375 int neighborIndex = neighbors.at( posInFace );
376
377 if ( neighborIndex != -1 )
378 {
379 const QgsMeshFace &neighborFace = mesh.face( neighborIndex );
380 int neighborFaceSize = neighborFace.size();
381 int posInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, face.at( posInFace ), neighborIndex );
382 posInNeighbor = ( posInNeighbor - 1 + neighborFaceSize ) % neighborFaceSize;
383
384 QHash<int, FaceRefinement>::iterator itRefinement = facesRefinement.find( neighborIndex );
385 if ( itRefinement != facesRefinement.end() )
386 {
387 FaceRefinement &neighborRefinement = itRefinement.value();
388 neighborRefinement.borderFaceNeighbor[posInNeighbor] = true;
389 borderFace.refinedFacesNeighbor[posInFace] = true;
390 continue;
391 }
392
393 QHash<int, BorderFace>::iterator itNeighborBorder = borderFaces.find( neighborIndex );
394 if ( itNeighborBorder == borderFaces.end() )
395 borderFace.unchangeFacesNeighbor[posInFace] = true;
396 else
397 {
398 BorderFace &neighborBorderFace = itNeighborBorder.value();
399 neighborBorderFace.borderFacesNeighbor[posInNeighbor] = true;
400 borderFace.borderFacesNeighbor[posInFace] = true;
401 continue;
402 }
403 }
404
405 borderFace.unchangeFacesNeighbor[posInFace] = true;
406
407 }
408 }
409
410// create new faces for each border faces
411 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
412 {
413 int faceIndex = it.key();
414 BorderFace &borderFace = it.value();
415
416 const QgsMeshFace &face = mesh.face( faceIndex );
417 int faceSize = face.size();
418
419 QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
420 std::vector<p2t::Point *> points;
421 for ( int i = 0; i < faceSize; ++i )
422 {
423 const QgsMeshVertex &vert = mesh.vertex( face.at( i ) );
424 points.push_back( new p2t::Point( vert.x(), vert.y() ) );
425 mapPoly2TriPointToVertex.insert( points.back(), face.at( i ) );
426 if ( borderFace.refinedFacesNeighbor.at( i ) )
427 {
428 int localVertexIndex = borderFace.newVerticesLocalIndex.at( i );
429 const QgsMeshVertex &newVert = mVerticesToAdd.at( localVertexIndex );
430 points.push_back( new p2t::Point( newVert.x(), newVert.y() ) );
431 mapPoly2TriPointToVertex.insert( points.back(), localVertexIndex + startingVertexIndex );
432 }
433 }
434
435 try
436 {
437 auto cdt = std::make_unique<p2t::CDT>( points );
438 cdt->Triangulate();
439 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
440 QVector<QgsMeshFace> faces( triangles.size() );
441 for ( size_t i = 0; i < triangles.size(); ++i )
442 {
443 QgsMeshFace &triangle = faces[i];
444 triangle.resize( 3 );
445 QVector<QgsMeshVertex> vertices( 3 );
446 for ( int j = 0; j < 3; j++ )
447 {
448 int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
449 if ( vertInd == -1 )
450 throw std::exception();;
451 triangle[j] = vertInd;
452 if ( vertInd >= startingVertexIndex )
453 vertices[j] = mVerticesToAdd.at( vertInd - startingVertexIndex );
454 else
455 vertices[j] = mesh.vertex( vertInd );
456 }
457 QgsMeshUtils::setCounterClockwise( triangle, vertices[0], vertices[1], vertices[2] );
458 }
459
460 int startingFaceIndex = mesh.faceCount() + mFacesToAdd.count();
461
462 QgsMeshEditingError error;
463 QgsTopologicalMesh::TopologicalFaces topologicalFaces = QgsTopologicalMesh::createNewTopologicalFaces( faces, false, error );
464 QVector<QgsTopologicalMesh::FaceNeighbors> neighborhood = topologicalFaces.facesNeighborhood();
465
466 // reindex internal neighborhood
467 for ( int i = 0; i < neighborhood.count(); ++i )
468 {
469 QgsTopologicalMesh::FaceNeighbors &neighbors = neighborhood[i];
470 for ( int j = 0; j < neighbors.count(); ++j )
471 {
472 if ( neighbors[j] != -1 ) //internal neighborhood
473 neighbors[j] = neighbors[j] + startingFaceIndex;
474 }
475 }
476
477 QVector<int> neighborOfFace = topology.neighborsOfFace( faceIndex );
478
479 // connect neighboring with refined faces, other border face and unchanged faces
480 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
481 {
482 if ( borderFace.refinedFacesNeighbor.at( positionInFace ) )
483 {
484 //here we have two edges to treat
485 QVector<int> vertexIndexes( 2 );
486 QVector<int> localFaceIndex( 2 );
487 vertexIndexes[0] = face.at( positionInFace );
488 vertexIndexes[1] = borderFace.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
489
490 int refinedFaceIndex = neighborOfFace.at( positionInFace );
491 const FaceRefinement &faceRefinement = facesRefinement.value( refinedFaceIndex );
492 const QgsMeshFace &refinedFace = mesh.face( refinedFaceIndex );
493 int refinedFaceSize = refinedFace.size();
494 int positionInRefinedFace = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndexes[0], refinedFace ) + refinedFaceSize - 1 ) % refinedFaceSize;
495
496 for ( int i = 0; i < 2; ++i )
497 {
498 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndexes.at( i ) );
499 circulator.goBoundaryClockwise();
500 localFaceIndex[i] = circulator.currentFaceIndex();
501
502 // new refined faces are in place, so we can link neighborhood
503 QgsTopologicalMesh::FaceNeighbors &neighborsNewFace = neighborhood[localFaceIndex.at( i )];
504 const QgsMeshFace newFace = faces.at( localFaceIndex.at( i ) );
505 int positionInNewFace = QgsTopologicalMesh::vertexPositionInFace( vertexIndexes.at( i ), newFace );
506 int newFaceRefinedIndexInChanges = faceRefinement.newFacesChangesIndex.at( ( positionInRefinedFace + ( 1 - i ) ) % refinedFaceSize ) ;
507 neighborsNewFace[positionInNewFace] = newFaceRefinedIndexInChanges + startingFaceChangesGlobalIndex;
508
509 QgsTopologicalMesh::FaceNeighbors &neighborsRefinedFace = mFacesNeighborhoodToAdd[newFaceRefinedIndexInChanges];
510 const QgsMeshFace &newRefinedFace = mFacesToAdd.at( newFaceRefinedIndexInChanges );
511 int newRefinedFaceSize = newRefinedFace.size();
512 int positionInNewRefinedChange = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndexes.at( i ), newRefinedFace ) + newRefinedFaceSize - 1 ) % newRefinedFaceSize;
513 neighborsRefinedFace[positionInNewRefinedChange] = localFaceIndex.at( i ) + startingFaceIndex;
514 }
515
516 borderFace.edgeFace.append( localFaceIndex.at( 0 ) + startingFaceIndex );
517 }
518
519 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
520 {
521 int vertexIndex = face.at( positionInFace );
522 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndex );
523 circulator.goBoundaryClockwise();
524 int localFaceIndex = circulator.currentFaceIndex();
525
526 // all new border faces are not in place, so store information for later
527 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
528 }
529
530 if ( borderFace.unchangeFacesNeighbor.at( positionInFace ) )
531 {
532 int vertexIndex = face.at( positionInFace );
533 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndex );
534 circulator.goBoundaryClockwise();
535 int localFaceIndex = circulator.currentFaceIndex();
536
537 const QgsMeshFace &newFace = faces.at( localFaceIndex );
538 int positionInNewface = QgsTopologicalMesh::vertexPositionInFace( vertexIndex, newFace );
539 QgsTopologicalMesh::FaceNeighbors &neighborsNewFace = neighborhood[localFaceIndex];
540 int unchangedFaceIndex = neighborOfFace.at( positionInFace );
541 neighborsNewFace[positionInNewface] = unchangedFaceIndex;
542
543 if ( unchangedFaceIndex != -1 )
544 {
545 const QgsMeshFace &unchangedFace = mesh.face( unchangedFaceIndex );
546 int unchangedFaceSize = unchangedFace.size();
547 int positionInUnchangedFace = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndex, unchangedFace ) + unchangedFaceSize - 1 ) % unchangedFaceSize;
548 mNeighborhoodChanges.append( {unchangedFaceIndex, positionInUnchangedFace, faceIndex, localFaceIndex + startingFaceIndex} );
549 }
550
551 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
552 }
553 }
554
555 mFacesToAdd.append( faces );
556 mFacesNeighborhoodToAdd.append( neighborhood );
557
558 for ( p2t::Point *pt : points )
559 delete pt;
560
561 }
562 catch ( ... )
563 {
564 return false;
565 }
566 }
567
568 //all border faces are in place, now it is possible to finalize with completing their nieghborhood with other border faces
569 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
570 {
571 int faceIndex = it.key();
572 BorderFace &borderFace = it.value();
573 const QgsMeshFace &face = mesh.face( faceIndex );
574 int faceSize = face.size();
575
576 const QVector<int> neighbors = topology.neighborsOfFace( faceIndex );
577
578 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
579 {
580 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
581 {
582 int otherIndex = neighbors.at( positionInFace );
583 const QgsMeshFace &otherFace = mesh.face( otherIndex );
584 int otherFaceSize = otherFace.size();
585 int otherPositionInface = ( QgsTopologicalMesh::vertexPositionInFace( face.at( positionInFace ), otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
586 const BorderFace &otherBorderFace = borderFaces.value( otherIndex );
587 int otherNewFaceIndex = otherBorderFace.edgeFace.at( otherPositionInface );
588
589 int newFaceChangesIndex = borderFace.edgeFace.at( positionInFace ) - startingFaceChangesGlobalIndex;
590 const QgsMeshFace &newFace = mFacesToAdd.at( newFaceChangesIndex );
591 int newFacePositionInFace = QgsTopologicalMesh::vertexPositionInFace( face.at( positionInFace ), newFace );
592
593 mFacesNeighborhoodToAdd[newFaceChangesIndex][newFacePositionInFace] = otherNewFaceIndex;
594 }
595
596 // ... and look for vertexToFace
597 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
598 {
599 int vertexIndex = face.at( positionInFace );
600 if ( topology.firstFaceLinked( vertexIndex ) == faceIndex )
601 mVerticesToFaceChanges.append( {vertexIndex, faceIndex, borderFace.edgeFace.at( positionInFace ) } );
602 }
603 }
604 }
605
606 return true;
607}
608
610{
611 return QObject::tr( "Refine %n face(s)", nullptr, mInputFaces.count() );
612}
613
615{
616 if ( !layer || !layer->meshEditor() || !layer->nativeMesh() )
617 return false;
618
619 if ( mInputVertices.isEmpty() )
620 return false;
621
622 const QgsMesh mesh = *layer->nativeMesh();
623 QSet<int> concernedFaces;
624 mChangingVertexMap = QHash<int, int>();
625
626 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Vertex ) );
627 QgsExpressionContext context;
628 context.appendScope( expScope.release() );
629 context.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mesh ) );
630
631 QVector<QgsMeshVertex> newVertices;
632 newVertices.reserve( mInputVertices.count() );
633
634 int inputCount = mInputVertices.count();
636
637 bool calcX = !mExpressionX.isEmpty();
638 bool calcY = !mExpressionY.isEmpty();
639 bool calcZ = !mExpressionZ.isEmpty();
640 QgsExpression expressionX;
641 if ( calcX )
642 {
643 expressionX = QgsExpression( mExpressionX );
644 expressionX.prepare( &context );
645 }
646
647 QgsExpression expressionY;
648 if ( calcY )
649 {
650 expressionY = QgsExpression( mExpressionY );
651 expressionY.prepare( &context );
652 }
653
654 if ( calcX || calcY )
655 {
656 mNewXYValues.reserve( inputCount );
657 mOldXYValues.reserve( inputCount );
658 }
659
660 QgsExpression expressionZ;
661 if ( calcZ || mZFromTerrain )
662 {
663 expressionZ = QgsExpression( mExpressionZ );
664 expressionZ.prepare( &context );
665 mNewZValues.reserve( inputCount );
666 mOldZValues.reserve( inputCount );
667 }
668
669 QgsCoordinateTransform transformation;
670 const QgsAbstractTerrainProvider *terrainProvider = nullptr;
671
672 if ( mZFromTerrain && project )
673 {
674 terrainProvider = project->elevationProperties()->terrainProvider();
675 if ( terrainProvider )
676 {
677 transformation = QgsCoordinateTransform( layer->crs(), terrainProvider->crs(), project );
678 }
679 }
680
681
682 for ( int i = 0; i < mInputVertices.count(); ++i )
683 {
684 const int vertexIndex = mInputVertices.at( i );
685 context.lastScope()->setVariable( QStringLiteral( "_mesh_vertex_index" ), vertexIndex, false );
686
687 mChangingVertexMap[vertexIndex] = i;
688 const QVariant xvar = expressionX.evaluate( &context );
689 const QVariant yvar = expressionY.evaluate( &context );
690 const QVariant zvar = expressionZ.evaluate( &context );
691
692 const QgsMeshVertex &vert = mesh.vertex( vertexIndex );
693
694 if ( calcX || calcY )
695 {
696 mOldXYValues.append( QgsPointXY( vert ) );
697 mNewXYValues.append( QgsPointXY( vert ) );
698
699 const QList<int> facesAround = layer->meshEditor()->topologicalMesh().facesAroundVertex( vertexIndex );
700 concernedFaces.unite( qgis::listToSet( facesAround ) );
701 }
702
703 bool ok = false;
704 if ( calcX )
705 {
706 if ( xvar.isValid() )
707 {
708 double x = xvar.toDouble( &ok );
709 if ( ok )
710 {
711 mNewXYValues.last().setX( x );
712 }
713 else
714 return false;
715 }
716 else
717 {
718 return false;
719 }
720 }
721
722 if ( calcY )
723 {
724 if ( yvar.isValid() )
725 {
726 double y = yvar.toDouble( &ok );
727 if ( ok )
728 {
729 mNewXYValues.last().setY( y );
730 }
731 else
732 return false;
733 }
734 else
735 return false;
736 }
737
738 if ( calcZ && !mZFromTerrain )
739 {
740 double z = std::numeric_limits<double>::quiet_NaN();
741 if ( zvar.isValid() )
742 {
743 z = zvar.toDouble( &ok );
744 if ( !ok )
745 z = std::numeric_limits<double>::quiet_NaN();
746 }
747
748 mNewZValues.append( z );
749 mOldZValues.append( vert.z() );
750 }
751
752 if ( mZFromTerrain && terrainProvider )
753 {
754 QgsPointXY point;
755 bool vertexTransformed;
756 double elevation;
757
758 try
759 {
760 if ( calcX || calcY )
761 {
762 point = transformation.transform( mNewXYValues.last().x(), mNewXYValues.last().y() );
763 }
764 else
765 {
766 point = transformation.transform( vert.x(), vert.y() );
767 }
768 vertexTransformed = true;
769 }
770 catch ( const QgsCsException & )
771 {
772 vertexTransformed = false;
773 }
774
775 // default elevation is the previous Z
776 elevation = vert.z();
777
778 if ( vertexTransformed )
779 {
780 double terrainElevation = terrainProvider->heightAt( point.x(), point.y() );
781 // if elevation at terrain provider is NaN, use the original vertex Z value
782 if ( ! std::isnan( terrainElevation ) )
783 {
784 elevation = terrainElevation;
785 }
786 }
787
788 mNewZValues.append( elevation );
789 mOldZValues.append( vert.z() );
790 }
791 }
792
793 auto transformFunction = [this, layer ]( int vi )-> const QgsMeshVertex
794 {
795 return transformedVertex( layer, vi );
796 };
797
798 mNativeFacesIndexesGeometryChanged = qgis::setToList( concernedFaces );
799 return ( !calcX && !calcY ) || layer->meshEditor()->canBeTransformed( mNativeFacesIndexesGeometryChanged, transformFunction );
800}
801
803{
804 return QObject::tr( "Transform %n vertices by expression", nullptr, mInputVertices.count() );
805}
806
807void QgsMeshTransformVerticesByExpression::setExpressions( const QString &expressionX, const QString &expressionY, const QString &expressionZ )
808{
809 mExpressionX = expressionX;
810 mExpressionY = expressionY;
811 mExpressionZ = expressionZ;
812
813 mChangingVertexMap.clear();
814}
815
816QgsTopologicalMesh::Changes QgsMeshTransformVerticesByExpression::apply( QgsMeshEditor *meshEditor )
817{
818 meshEditor->topologicalMesh().applyChanges( *this );
819 mIsFinished = true;
820 return *this;
821}
822
824{
825 int pos = mChangingVertexMap.value( vertexIndex, -1 );
826 if ( pos > -1 )
827 {
828 QgsPointXY pointXY;
829 double z;
830
831 if ( mNewXYValues.isEmpty() )
832 pointXY = layer->nativeMesh()->vertex( vertexIndex );
833 else
834 pointXY = mNewXYValues.at( pos );
835
836 if ( mNewZValues.isEmpty() )
837 z = layer->nativeMesh()->vertex( vertexIndex ).z();
838 else
839 z = mNewZValues.at( pos );
840
841 return QgsMeshVertex( pointXY.x(), pointXY.y(), z );
842 }
843 else
844 return layer->nativeMesh()->vertex( vertexIndex );
845}
846
848{
849 mZFromTerrain = enable;
850}
Abstract base class for terrain providers.
virtual double heightAt(double x, double y) const =0
Returns the height at the point (x,y) in the terrain provider's native crs().
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the native coordinate reference system of the terrain provider.
Handles coordinate transforms between two coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static QgsExpressionContextScope * meshExpressionScope(QgsMesh::ElementType elementType)
Creates a new scope which contains functions relating to mesh layer element elementType.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QVariant evaluate()
Evaluate the feature and return the result.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
QString message() const
Returns a message that can be provided by the advanced editing when applying is done.
void setInputVertices(const QList< int > verticesIndexes)
Sets the input vertices indexes that will be used for the editing.
virtual ~QgsMeshAdvancedEditing()
void clear()
Removes all data provided to the editing or created by the editing.
virtual bool isFinished() const
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
virtual QString text() const
Returns a short text string describing what this advanced edit does. Default implementation return a ...
void setInputFaces(const QList< int > faceIndexes)
Sets the input faces indexes that will be used for the editing.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
Handles edit operations on a mesh layer.
bool canBeTransformed(const QList< int > &facesToCheck, const std::function< const QgsMeshVertex(int)> &transformFunction) const
Returns true if faces with index in transformedFaces can be transformed without obtaining topologic o...
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshEditor * meshEditor()
Returns a pointer to the mesh editor own by the mesh layer.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh).
void setExpressions(const QString &expressionX, const QString &expressionY, const QString &expressionZ)
Sets the expressions for the coordinates transformation.
QgsMeshVertex transformedVertex(QgsMeshLayer *layer, int vertexIndex) const
Returns the transformed vertex from its index vertexIndex for the mesh layer.
bool calculate(QgsMeshLayer *layer, QgsProject *project=nullptr)
Calculates the transformed vertices of the mesh layer, returns false if this leads to topological or ...
void setZFromTerrain(bool enable)
Sets if Z values for vertices should be obtained from project terrain, instead of expression.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
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.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
QgsAbstractTerrainProvider * terrainProvider()
Returns the project's terrain provider.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
Contains topological differences between two states of a topological mesh, only accessible from the Q...
void clearChanges()
Clears all changes.
QVector< FaceNeighbors > mFacesNeighborhoodToRemove
QList< std::array< int, 4 > > mNeighborhoodChanges
QList< int > mNativeFacesIndexesGeometryChanged
QVector< QgsMeshFace > mFacesToAdd
QVector< FaceNeighbors > mFacesNeighborhoodToAdd
QList< std::array< int, 3 > > mVerticesToFaceChanges
QVector< QgsMeshVertex > mVerticesToAdd
QVector< QgsMeshFace > mFacesToRemove
QVector< FaceNeighbors > facesNeighborhood() const
Returns the face neighborhood of the faces, indexing is local.
static int vertexPositionInFace(int vertexIndex, const QgsMeshFace &face)
Returns vertex position in face.
void applyChanges(const Changes &changes)
Applies the changes.
int firstFaceLinked(int vertexIndex) const
Returns the index of the first face linked, returns -1 if it is a free vertex or out of range index.
QVector< int > neighborsOfFace(int faceIndex) const
Returns the indexes of neighbor faces of the face with index faceIndex.
QVector< int > FaceNeighbors
QList< int > facesAroundVertex(int vertexIndex) const
Returns the indexes of faces that are around the vertex with index vertexIndex.
QgsMesh * mesh() const
Returns a pointer to the wrapped mesh.
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
QVector< int > QgsMeshFace
List of vertex indexes.
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.
int faceCount() const
Returns number of faces.
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.