QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
28#include <QString>
29
30using namespace Qt::StringLiterals;
31
33
35
36void QgsMeshAdvancedEditing::setInputVertices( const QList<int> verticesIndexes )
37{
38 mInputVertices = verticesIndexes;
39}
40
41void QgsMeshAdvancedEditing::setInputFaces( const QList<int> faceIndexes )
42{
43 mInputFaces = faceIndexes;
44}
45
47{
48 return mMessage;
49}
50
52{
53 mInputVertices.clear();
54 mInputFaces.clear();
55 mMessage.clear();
56 mIsFinished = false;
58}
59
61{
62 return mIsFinished;
63}
64
66{
67 return QString();
68}
69
71
72QgsTopologicalMesh::Changes QgsMeshEditRefineFaces::apply( QgsMeshEditor *meshEditor )
73{
74 QSet<int> facesToRefine = qgis::listToSet( mInputFaces );
75 QHash<int, FaceRefinement> facesRefinement;
76 QHash<int, BorderFace> borderFaces;
77
78 createNewVerticesAndRefinedFaces( meshEditor, facesToRefine, facesRefinement );
79 if ( !createNewBorderFaces( meshEditor, facesToRefine, facesRefinement, borderFaces ) )
81
82 // create new vertices
83 const QgsMesh &nativeMesh = *meshEditor->topologicalMesh().mesh();
84 mAddedFacesFirstIndex = nativeMesh.faceCount();
85
86 mFaceIndexesToRemove = facesRefinement.keys();
87 mFaceIndexesToRemove.append( borderFaces.keys() );
88 mFacesToRemove.resize( mFaceIndexesToRemove.size() );
90 for ( int i = 0; i < mFaceIndexesToRemove.count(); ++i )
91 {
92 int faceIndexToRemove = mFaceIndexesToRemove.at( i );
93 mFacesToRemove[i] = nativeMesh.face( faceIndexToRemove );
94 mFacesNeighborhoodToRemove[i] = meshEditor->topologicalMesh().neighborsOfFace( faceIndexToRemove );
95 }
96
97 meshEditor->topologicalMesh().applyChanges( *this );
98
99 mIsFinished = true;
100
101 return *this;
102}
103
104void QgsMeshEditRefineFaces::createNewVerticesAndRefinedFaces( QgsMeshEditor *meshEditor, QSet<int> &facesToRefine, QHash<int, FaceRefinement> &facesRefinement )
105{
106 const QgsTopologicalMesh &topology = meshEditor->topologicalMesh();
107 const QgsMesh &mesh = *meshEditor->topologicalMesh().mesh();
108
109 int startingVertexIndex = mesh.vertexCount();
110 int startingGlobalFaceIndex = mesh.faceCount();
111
112 auto canBeRefined = [&]( int fi ) -> bool {
113 if ( fi < 0 || fi > mesh.faceCount() )
114 return false;
115 int fs = mesh.face( fi ).size();
116 return fs == 3 || fs == 4;
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, ( vertex1.y() + vertex2.y() ) / 2, ( vertex1.z() + vertex2.z() ) / 2 ) );
170
171 zValueSum += mVerticesToAdd.last().z();
172 mVertexToFaceToAdd.append( -1 );
173 }
174 }
175
176 int faceStartIndex = startingGlobalFaceIndex + mFacesToAdd.count();
177
178 if ( faceSize == 3 )
179 {
180 for ( int i = 0; i < faceSize; ++i )
181 {
182 QgsMeshFace newFace(
183 { face.at( i ), refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex, refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex }
184 );
185 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
186 mFacesToAdd.append( newFace );
187 mFacesNeighborhoodToAdd.append( { -1, faceStartIndex + 3, -1 } );
188 }
189 QgsMeshFace newFace(
190 { refinement.newVerticesLocalIndex.at( 0 ) + startingVertexIndex,
191 refinement.newVerticesLocalIndex.at( 1 ) + startingVertexIndex,
192 refinement.newVerticesLocalIndex.at( ( 2 ) % faceSize ) + startingVertexIndex }
193 );
194 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
195 mFacesToAdd.append( newFace );
196 mFacesNeighborhoodToAdd.append( { faceStartIndex + 1, faceStartIndex + 2, faceStartIndex } );
197 }
198
199 if ( faceSize == 4 )
200 {
201 int centerVertexIndex = mVerticesToAdd.count() + startingVertexIndex;
202 refinement.newCenterVertexIndex = mVerticesToAdd.count();
203 QgsMeshVertex centerVertex = QgsMeshUtils::centroid( face, mesh.vertices );
204 mVerticesToAdd.append( QgsMeshVertex( centerVertex.x(), centerVertex.y(), zValueSum / 4 ) );
205
206 for ( int i = 0; i < faceSize; ++i )
207 {
208 QgsMeshFace newFace(
209 { face.at( i ), refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex, centerVertexIndex, refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex }
210 );
211 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
212 mFacesToAdd.append( newFace );
213 mFacesNeighborhoodToAdd.append( { -1, faceStartIndex + ( i + 1 ) % 4, faceStartIndex + ( i + 3 ) % 4, -1 } );
214 }
215
216 mVertexToFaceToAdd.append( mFacesToAdd.count() + startingGlobalFaceIndex - 1 );
217 }
218 else
219 refinement.newCenterVertexIndex = -1;
220
221 facesRefinement.insert( faceIndex, refinement );
222
223 //look for vertexToFace
224 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
225 {
226 if ( addedVerticesIndex.at( positionInFace ) != -1 )
227 {
228 mVertexToFaceToAdd[addedVerticesIndex.at( positionInFace )] = refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex;
229 }
230
231 int vertexIndex = face.at( positionInFace );
232 if ( topology.firstFaceLinked( vertexIndex ) == faceIndex )
233 mVerticesToFaceChanges.append( { vertexIndex, faceIndex, refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex } );
234 }
235 }
236 else
237 {
238 //not 3 or 4 vertices, we do not refine this face
239 facesToRefine.remove( faceIndex );
240 }
241 }
242
243 //all new refined faces are in place, we can build their neighborhood with other new refined faces
244 for ( QHash<int, FaceRefinement>::iterator it = facesRefinement.begin(); it != facesRefinement.end(); ++it )
245 {
246 int faceIndex = it.key();
247 FaceRefinement &faceRefinement = it.value();
248 const QgsMeshFace &face = mesh.face( faceIndex );
249 int faceSize = face.size();
250
251 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
252
253 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
254 {
255 if ( faceRefinement.refinedFaceNeighbor.at( positionInFace ) )
256 {
257 int neighborIndex = neighbors.at( positionInFace );
258 int firstVertexIndex = face.at( positionInFace );
259 int secondVertexIndex = faceRefinement.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
260
261 const FaceRefinement &otherRefinement = facesRefinement.value( neighborIndex );
262 const QgsMeshFace &otherFace = mesh.face( neighborIndex );
263 int otherFaceSize = otherFace.size();
264 int otherPositionInface = ( QgsTopologicalMesh::vertexPositionInFace( firstVertexIndex, otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
265
266 int newFace1ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace ) );
267 const QgsMeshFace &newFace1 = mFacesToAdd.at( newFace1ChangesIndex );
268 int positionInNewface1Index = QgsTopologicalMesh::vertexPositionInFace( firstVertexIndex, newFace1 );
269
270 int newFace2ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace + 1 ) % faceSize );
271 const QgsMeshFace &newFace2 = mFacesToAdd.at( newFace2ChangesIndex );
272 int positionInNewface2Index = QgsTopologicalMesh::vertexPositionInFace( secondVertexIndex, newFace2 );
273
274 int otherNewFace1ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface ) % otherFaceSize );
275 int otherNewFace2ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface + 1 ) % otherFaceSize );
276
277 mFacesNeighborhoodToAdd[newFace1ChangesIndex][positionInNewface1Index] = otherNewFace2ChangesIndex + startingGlobalFaceIndex;
278 mFacesNeighborhoodToAdd[newFace2ChangesIndex][positionInNewface2Index] = otherNewFace1ChangesIndex + startingGlobalFaceIndex;
279 }
280 }
281 }
282}
283
284bool QgsMeshEditRefineFaces::createNewBorderFaces( QgsMeshEditor *meshEditor, const QSet<int> &facesToRefine, QHash<int, FaceRefinement> &facesRefinement, QHash<int, BorderFace> &borderFaces )
285{
286 const QgsTopologicalMesh &topology = meshEditor->topologicalMesh();
287 const QgsMesh &mesh = *meshEditor->topologicalMesh().mesh();
288
289 int startingVertexIndex = mesh.vertexCount();
290 int startingFaceChangesGlobalIndex = mesh.faceCount();
291
292 // first create the border faces
293 for ( int faceIndexToRefine : facesToRefine )
294 {
295 const QgsMeshFace &faceToRefine = mesh.face( faceIndexToRefine );
296 int faceToRefineSize = faceToRefine.size();
297
298 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndexToRefine );
299
300 QHash<int, FaceRefinement>::iterator itFace = facesRefinement.find( faceIndexToRefine );
301
302 if ( itFace == facesRefinement.end() )
303 Q_ASSERT( false ); // That could not happen
304
305 FaceRefinement &refinement = itFace.value();
306
307 for ( int posInFaceToRefine = 0; posInFaceToRefine < faceToRefineSize; ++posInFaceToRefine )
308 {
309 int neighborFaceIndex = neighbors.at( posInFaceToRefine );
310 if ( neighborFaceIndex != -1 && !facesToRefine.contains( neighborFaceIndex ) )
311 {
312 const QgsMeshFace &neighborFace = mesh.face( neighborFaceIndex );
313 int neighborFaceSize = neighborFace.size();
314 int positionInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, faceToRefine.at( posInFaceToRefine ), neighborFaceIndex );
315 positionInNeighbor = ( positionInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
316
317 QHash<int, BorderFace>::iterator it = borderFaces.find( neighborFaceIndex );
318 if ( it == borderFaces.end() ) //not present for now--> create a border face
319 {
320 BorderFace borderFace;
321 for ( int i = 0; i < neighborFaceSize; ++i )
322 {
323 borderFace.unchangeFacesNeighbor.append( false );
324 borderFace.borderFacesNeighbor.append( false );
325 if ( i == positionInNeighbor )
326 {
327 borderFace.refinedFacesNeighbor.append( true );
328 borderFace.newVerticesLocalIndex.append( refinement.newVerticesLocalIndex.at( posInFaceToRefine ) );
329 }
330 else
331 {
332 borderFace.refinedFacesNeighbor.append( false );
333 borderFace.newVerticesLocalIndex.append( -1 );
334 }
335 }
336 borderFaces.insert( neighborFaceIndex, borderFace );
337 }
338 else
339 {
340 BorderFace &borderFace = it.value();
341 for ( int i = 0; i < neighborFaceSize; ++i )
342 {
343 if ( i == positionInNeighbor )
344 {
345 borderFace.unchangeFacesNeighbor[i] = false;
346 borderFace.borderFacesNeighbor[i] = false;
347 borderFace.refinedFacesNeighbor[i] = true;
348 borderFace.newVerticesLocalIndex[i] = refinement.newVerticesLocalIndex.at( posInFaceToRefine );
349 }
350 }
351 }
352 }
353 }
354 }
355
356 // now build information about neighbors
357 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
358 {
359 int faceIndex = it.key();
360 BorderFace &borderFace = it.value();
361
362 const QgsMeshFace &face = mesh.face( faceIndex );
363 int faceSize = face.size();
364
365 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
366 for ( int posInFace = 0; posInFace < faceSize; ++posInFace )
367 {
368 int neighborIndex = neighbors.at( posInFace );
369
370 if ( neighborIndex != -1 )
371 {
372 const QgsMeshFace &neighborFace = mesh.face( neighborIndex );
373 int neighborFaceSize = neighborFace.size();
374 int posInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, face.at( posInFace ), neighborIndex );
375 posInNeighbor = ( posInNeighbor - 1 + neighborFaceSize ) % neighborFaceSize;
376
377 QHash<int, FaceRefinement>::iterator itRefinement = facesRefinement.find( neighborIndex );
378 if ( itRefinement != facesRefinement.end() )
379 {
380 FaceRefinement &neighborRefinement = itRefinement.value();
381 neighborRefinement.borderFaceNeighbor[posInNeighbor] = true;
382 borderFace.refinedFacesNeighbor[posInFace] = true;
383 continue;
384 }
385
386 QHash<int, BorderFace>::iterator itNeighborBorder = borderFaces.find( neighborIndex );
387 if ( itNeighborBorder == borderFaces.end() )
388 borderFace.unchangeFacesNeighbor[posInFace] = true;
389 else
390 {
391 BorderFace &neighborBorderFace = itNeighborBorder.value();
392 neighborBorderFace.borderFacesNeighbor[posInNeighbor] = true;
393 borderFace.borderFacesNeighbor[posInFace] = true;
394 continue;
395 }
396 }
397
398 borderFace.unchangeFacesNeighbor[posInFace] = true;
399 }
400 }
401
402 // create new faces for each border faces
403 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
404 {
405 int faceIndex = it.key();
406 BorderFace &borderFace = it.value();
407
408 const QgsMeshFace &face = mesh.face( faceIndex );
409 int faceSize = face.size();
410
411 QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
412 std::vector<p2t::Point *> points;
413 for ( int i = 0; i < faceSize; ++i )
414 {
415 const QgsMeshVertex &vert = mesh.vertex( face.at( i ) );
416 points.push_back( new p2t::Point( vert.x(), vert.y() ) );
417 mapPoly2TriPointToVertex.insert( points.back(), face.at( i ) );
418 if ( borderFace.refinedFacesNeighbor.at( i ) )
419 {
420 int localVertexIndex = borderFace.newVerticesLocalIndex.at( i );
421 const QgsMeshVertex &newVert = mVerticesToAdd.at( localVertexIndex );
422 points.push_back( new p2t::Point( newVert.x(), newVert.y() ) );
423 mapPoly2TriPointToVertex.insert( points.back(), localVertexIndex + startingVertexIndex );
424 }
425 }
426
427 try
428 {
429 auto cdt = std::make_unique<p2t::CDT>( points );
430 cdt->Triangulate();
431 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
432 QVector<QgsMeshFace> faces( triangles.size() );
433 for ( size_t i = 0; i < triangles.size(); ++i )
434 {
435 QgsMeshFace &triangle = faces[i];
436 triangle.resize( 3 );
437 QVector<QgsMeshVertex> vertices( 3 );
438 for ( int j = 0; j < 3; j++ )
439 {
440 int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
441 if ( vertInd == -1 )
442 throw std::exception();
443 ;
444 triangle[j] = vertInd;
445 if ( vertInd >= startingVertexIndex )
446 vertices[j] = mVerticesToAdd.at( vertInd - startingVertexIndex );
447 else
448 vertices[j] = mesh.vertex( vertInd );
449 }
450 QgsMeshUtils::setCounterClockwise( triangle, vertices[0], vertices[1], vertices[2] );
451 }
452
453 int startingFaceIndex = mesh.faceCount() + mFacesToAdd.count();
454
455 QgsMeshEditingError error;
456 QgsTopologicalMesh::TopologicalFaces topologicalFaces = QgsTopologicalMesh::createNewTopologicalFaces( faces, false, error );
457 QVector<QgsTopologicalMesh::FaceNeighbors> neighborhood = topologicalFaces.facesNeighborhood();
458
459 // reindex internal neighborhood
460 for ( int i = 0; i < neighborhood.count(); ++i )
461 {
462 QgsTopologicalMesh::FaceNeighbors &neighbors = neighborhood[i];
463 for ( int j = 0; j < neighbors.count(); ++j )
464 {
465 if ( neighbors[j] != -1 ) //internal neighborhood
466 neighbors[j] = neighbors[j] + startingFaceIndex;
467 }
468 }
469
470 QVector<int> neighborOfFace = topology.neighborsOfFace( faceIndex );
471
472 // connect neighboring with refined faces, other border face and unchanged faces
473 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
474 {
475 if ( borderFace.refinedFacesNeighbor.at( positionInFace ) )
476 {
477 //here we have two edges to treat
478 QVector<int> vertexIndexes( 2 );
479 QVector<int> localFaceIndex( 2 );
480 vertexIndexes[0] = face.at( positionInFace );
481 vertexIndexes[1] = borderFace.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
482
483 int refinedFaceIndex = neighborOfFace.at( positionInFace );
484 const FaceRefinement &faceRefinement = facesRefinement.value( refinedFaceIndex );
485 const QgsMeshFace &refinedFace = mesh.face( refinedFaceIndex );
486 int refinedFaceSize = refinedFace.size();
487 int positionInRefinedFace = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndexes[0], refinedFace ) + refinedFaceSize - 1 ) % refinedFaceSize;
488
489 for ( int i = 0; i < 2; ++i )
490 {
491 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndexes.at( i ) );
492 circulator.goBoundaryClockwise();
493 localFaceIndex[i] = circulator.currentFaceIndex();
494
495 // new refined faces are in place, so we can link neighborhood
496 QgsTopologicalMesh::FaceNeighbors &neighborsNewFace = neighborhood[localFaceIndex.at( i )];
497 const QgsMeshFace newFace = faces.at( localFaceIndex.at( i ) );
498 int positionInNewFace = QgsTopologicalMesh::vertexPositionInFace( vertexIndexes.at( i ), newFace );
499 int newFaceRefinedIndexInChanges = faceRefinement.newFacesChangesIndex.at( ( positionInRefinedFace + ( 1 - i ) ) % refinedFaceSize );
500 neighborsNewFace[positionInNewFace] = newFaceRefinedIndexInChanges + startingFaceChangesGlobalIndex;
501
502 QgsTopologicalMesh::FaceNeighbors &neighborsRefinedFace = mFacesNeighborhoodToAdd[newFaceRefinedIndexInChanges];
503 const QgsMeshFace &newRefinedFace = mFacesToAdd.at( newFaceRefinedIndexInChanges );
504 int newRefinedFaceSize = newRefinedFace.size();
505 int positionInNewRefinedChange = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndexes.at( i ), newRefinedFace ) + newRefinedFaceSize - 1 ) % newRefinedFaceSize;
506 neighborsRefinedFace[positionInNewRefinedChange] = localFaceIndex.at( i ) + startingFaceIndex;
507 }
508
509 borderFace.edgeFace.append( localFaceIndex.at( 0 ) + startingFaceIndex );
510 }
511
512 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
513 {
514 int vertexIndex = face.at( positionInFace );
515 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndex );
516 circulator.goBoundaryClockwise();
517 int localFaceIndex = circulator.currentFaceIndex();
518
519 // all new border faces are not in place, so store information for later
520 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
521 }
522
523 if ( borderFace.unchangeFacesNeighbor.at( positionInFace ) )
524 {
525 int vertexIndex = face.at( positionInFace );
526 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndex );
527 circulator.goBoundaryClockwise();
528 int localFaceIndex = circulator.currentFaceIndex();
529
530 const QgsMeshFace &newFace = faces.at( localFaceIndex );
531 int positionInNewface = QgsTopologicalMesh::vertexPositionInFace( vertexIndex, newFace );
532 QgsTopologicalMesh::FaceNeighbors &neighborsNewFace = neighborhood[localFaceIndex];
533 int unchangedFaceIndex = neighborOfFace.at( positionInFace );
534 neighborsNewFace[positionInNewface] = unchangedFaceIndex;
535
536 if ( unchangedFaceIndex != -1 )
537 {
538 const QgsMeshFace &unchangedFace = mesh.face( unchangedFaceIndex );
539 int unchangedFaceSize = unchangedFace.size();
540 int positionInUnchangedFace = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndex, unchangedFace ) + unchangedFaceSize - 1 ) % unchangedFaceSize;
541 mNeighborhoodChanges.append( { unchangedFaceIndex, positionInUnchangedFace, faceIndex, localFaceIndex + startingFaceIndex } );
542 }
543
544 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
545 }
546 }
547
548 mFacesToAdd.append( faces );
549 mFacesNeighborhoodToAdd.append( neighborhood );
550
551 for ( p2t::Point *pt : points )
552 delete pt;
553 }
554 catch ( ... )
555 {
556 return false;
557 }
558 }
559
560 //all border faces are in place, now it is possible to finalize with completing their nieghborhood with other border faces
561 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
562 {
563 int faceIndex = it.key();
564 BorderFace &borderFace = it.value();
565 const QgsMeshFace &face = mesh.face( faceIndex );
566 int faceSize = face.size();
567
568 const QVector<int> neighbors = topology.neighborsOfFace( faceIndex );
569
570 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
571 {
572 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
573 {
574 int otherIndex = neighbors.at( positionInFace );
575 const QgsMeshFace &otherFace = mesh.face( otherIndex );
576 int otherFaceSize = otherFace.size();
577 int otherPositionInface = ( QgsTopologicalMesh::vertexPositionInFace( face.at( positionInFace ), otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
578 const BorderFace &otherBorderFace = borderFaces.value( otherIndex );
579 int otherNewFaceIndex = otherBorderFace.edgeFace.at( otherPositionInface );
580
581 int newFaceChangesIndex = borderFace.edgeFace.at( positionInFace ) - startingFaceChangesGlobalIndex;
582 const QgsMeshFace &newFace = mFacesToAdd.at( newFaceChangesIndex );
583 int newFacePositionInFace = QgsTopologicalMesh::vertexPositionInFace( face.at( positionInFace ), newFace );
584
585 mFacesNeighborhoodToAdd[newFaceChangesIndex][newFacePositionInFace] = otherNewFaceIndex;
586 }
587
588 // ... and look for vertexToFace
589 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
590 {
591 int vertexIndex = face.at( positionInFace );
592 if ( topology.firstFaceLinked( vertexIndex ) == faceIndex )
593 mVerticesToFaceChanges.append( { vertexIndex, faceIndex, borderFace.edgeFace.at( positionInFace ) } );
594 }
595 }
596 }
597
598 return true;
599}
600
602{
603 return QObject::tr( "Refine %n face(s)", nullptr, mInputFaces.count() );
604}
605
607{
608 if ( !layer || !layer->meshEditor() || !layer->nativeMesh() )
609 return false;
610
611 if ( mInputVertices.isEmpty() )
612 return false;
613
614 const QgsMesh mesh = *layer->nativeMesh();
615 QSet<int> concernedFaces;
616 mChangingVertexMap = QHash<int, int>();
617
618 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Vertex ) );
619 QgsExpressionContext context;
620 context.appendScope( expScope.release() );
621 context.lastScope()->setVariable( u"_native_mesh"_s, QVariant::fromValue( mesh ) );
622
623 QVector<QgsMeshVertex> newVertices;
624 newVertices.reserve( mInputVertices.count() );
625
626 int inputCount = mInputVertices.count();
628
629 bool calcX = !mExpressionX.isEmpty();
630 bool calcY = !mExpressionY.isEmpty();
631 bool calcZ = !mExpressionZ.isEmpty();
632 QgsExpression expressionX;
633 if ( calcX )
634 {
635 expressionX = QgsExpression( mExpressionX );
636 expressionX.prepare( &context );
637 }
638
639 QgsExpression expressionY;
640 if ( calcY )
641 {
642 expressionY = QgsExpression( mExpressionY );
643 expressionY.prepare( &context );
644 }
645
646 if ( calcX || calcY )
647 {
648 mNewXYValues.reserve( inputCount );
649 mOldXYValues.reserve( inputCount );
650 }
651
652 QgsExpression expressionZ;
653 if ( calcZ || mZFromTerrain )
654 {
655 expressionZ = QgsExpression( mExpressionZ );
656 expressionZ.prepare( &context );
657 mNewZValues.reserve( inputCount );
658 mOldZValues.reserve( inputCount );
659 }
660
661 QgsCoordinateTransform transformation;
662 const QgsAbstractTerrainProvider *terrainProvider = nullptr;
663
664 if ( mZFromTerrain && project )
665 {
666 terrainProvider = project->elevationProperties()->terrainProvider();
667 if ( terrainProvider )
668 {
669 transformation = QgsCoordinateTransform( layer->crs(), terrainProvider->crs(), project );
670 }
671 }
672
673
674 for ( int i = 0; i < mInputVertices.count(); ++i )
675 {
676 const int vertexIndex = mInputVertices.at( i );
677 context.lastScope()->setVariable( u"_mesh_vertex_index"_s, vertexIndex, false );
678
679 mChangingVertexMap[vertexIndex] = i;
680 const QVariant xvar = expressionX.evaluate( &context );
681 const QVariant yvar = expressionY.evaluate( &context );
682 const QVariant zvar = expressionZ.evaluate( &context );
683
684 const QgsMeshVertex &vert = mesh.vertex( vertexIndex );
685
686 if ( calcX || calcY )
687 {
688 mOldXYValues.append( QgsPointXY( vert ) );
689 mNewXYValues.append( QgsPointXY( vert ) );
690
691 const QList<int> facesAround = layer->meshEditor()->topologicalMesh().facesAroundVertex( vertexIndex );
692 concernedFaces.unite( qgis::listToSet( facesAround ) );
693 }
694
695 bool ok = false;
696 if ( calcX )
697 {
698 if ( xvar.isValid() )
699 {
700 double x = xvar.toDouble( &ok );
701 if ( ok )
702 {
703 mNewXYValues.last().setX( x );
704 }
705 else
706 return false;
707 }
708 else
709 {
710 return false;
711 }
712 }
713
714 if ( calcY )
715 {
716 if ( yvar.isValid() )
717 {
718 double y = yvar.toDouble( &ok );
719 if ( ok )
720 {
721 mNewXYValues.last().setY( y );
722 }
723 else
724 return false;
725 }
726 else
727 return false;
728 }
729
730 if ( calcZ && !mZFromTerrain )
731 {
732 double z = std::numeric_limits<double>::quiet_NaN();
733 if ( zvar.isValid() )
734 {
735 z = zvar.toDouble( &ok );
736 if ( !ok )
737 z = std::numeric_limits<double>::quiet_NaN();
738 }
739
740 mNewZValues.append( z );
741 mOldZValues.append( vert.z() );
742 }
743
744 if ( mZFromTerrain && terrainProvider )
745 {
746 QgsPointXY point;
747 bool vertexTransformed;
748 double elevation;
749
750 try
751 {
752 if ( calcX || calcY )
753 {
754 point = transformation.transform( mNewXYValues.last().x(), mNewXYValues.last().y() );
755 }
756 else
757 {
758 point = transformation.transform( vert.x(), vert.y() );
759 }
760 vertexTransformed = true;
761 }
762 catch ( const QgsCsException & )
763 {
764 vertexTransformed = false;
765 }
766
767 // default elevation is the previous Z
768 elevation = vert.z();
769
770 if ( vertexTransformed )
771 {
772 double terrainElevation = terrainProvider->heightAt( point.x(), point.y() );
773 // if elevation at terrain provider is NaN, use the original vertex Z value
774 if ( !std::isnan( terrainElevation ) )
775 {
776 elevation = terrainElevation;
777 }
778 }
779
780 mNewZValues.append( elevation );
781 mOldZValues.append( vert.z() );
782 }
783 }
784
785 auto transformFunction = [this, layer]( int vi ) -> const QgsMeshVertex { return transformedVertex( layer, vi ); };
786
787 mNativeFacesIndexesGeometryChanged = qgis::setToList( concernedFaces );
788 return ( !calcX && !calcY ) || layer->meshEditor()->canBeTransformed( mNativeFacesIndexesGeometryChanged, transformFunction );
789}
790
792{
793 return QObject::tr( "Transform %n vertices by expression", nullptr, mInputVertices.count() );
794}
795
796void QgsMeshTransformVerticesByExpression::setExpressions( const QString &expressionX, const QString &expressionY, const QString &expressionZ )
797{
798 mExpressionX = expressionX;
799 mExpressionY = expressionY;
800 mExpressionZ = expressionZ;
801
802 mChangingVertexMap.clear();
803}
804
805QgsTopologicalMesh::Changes QgsMeshTransformVerticesByExpression::apply( QgsMeshEditor *meshEditor )
806{
807 meshEditor->topologicalMesh().applyChanges( *this );
808 mIsFinished = true;
809 return *this;
810}
811
813{
814 int pos = mChangingVertexMap.value( vertexIndex, -1 );
815 if ( pos > -1 )
816 {
817 QgsPointXY pointXY;
818 double z;
819
820 if ( mNewXYValues.isEmpty() )
821 pointXY = layer->nativeMesh()->vertex( vertexIndex );
822 else
823 pointXY = mNewXYValues.at( pos );
824
825 if ( mNewZValues.isEmpty() )
826 z = layer->nativeMesh()->vertex( vertexIndex ).z();
827 else
828 z = mNewZValues.at( pos );
829
830 return QgsMeshVertex( pointXY.x(), pointXY.y(), z );
831 }
832 else
833 return layer->nativeMesh()->vertex( vertexIndex );
834}
835
837{
838 mZFromTerrain = enable;
839}
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:90
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:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double y
Definition qgspoint.h:57
QgsAbstractTerrainProvider * terrainProvider()
Returns the project's terrain provider.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
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.