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