65static int vertexPositionInFace(
int vertexIndex,
const QgsMeshFace &face )
67 return face.indexOf( vertexIndex );
70static int vertexPositionInFace(
const QgsMesh &mesh,
int vertexIndex,
int faceIndex )
72 if ( faceIndex < 0 || faceIndex >= mesh.
faceCount() )
75 return vertexPositionInFace( vertexIndex, mesh.
face( faceIndex ) );
82 QSet<int> facesToRefine = qgis::listToSet(
mInputFaces );
83 QHash<int, FaceRefinement> facesRefinement;
84 QHash<int, BorderFace> borderFaces;
86 createNewVerticesAndRefinedFaces( meshEditor, facesToRefine, facesRefinement );
87 if ( !createNewBorderFaces( meshEditor, facesToRefine, facesRefinement, borderFaces ) )
112void QgsMeshEditRefineFaces::createNewVerticesAndRefinedFaces(
QgsMeshEditor *meshEditor,
113 QSet<int> &facesToRefine,
114 QHash<int, FaceRefinement> &facesRefinement )
120 int startingGlobalFaceIndex = mesh.
faceCount();
122 auto canBeRefined = [ & ](
int fi )->
bool
126 int fs = mesh.
face( fi ).size();
127 return fs == 3 || fs == 4;
131 for (
const int faceIndex : std::as_const(
mInputFaces ) )
133 FaceRefinement refinement;
136 int faceSize = face.size();
138 QVector<int> addedVerticesIndex( faceSize, -1 );
140 if ( canBeRefined( faceIndex ) )
142 refinement.newVerticesLocalIndex.reserve( faceSize );
143 refinement.refinedFaceNeighbor.reserve( faceSize );
144 refinement.borderFaceNeighbor.reserve( faceSize );
147 double zValueSum = 0;
149 for (
int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
151 refinement.refinedFaceNeighbor.append(
false );
152 refinement.borderFaceNeighbor.append(
false );
154 int neighborFaceIndex = neighbors.at( positionInFace );
155 bool needCreateVertex =
true;
156 if ( neighborFaceIndex != -1 && facesToRefine.contains( neighborFaceIndex ) && canBeRefined( neighborFaceIndex ) )
158 int neighborFaceSize = mesh.
face( neighborFaceIndex ).size();
159 int positionVertexInNeighbor = vertexPositionInFace( mesh, face.at( positionInFace ), neighborFaceIndex );
160 positionVertexInNeighbor = ( positionVertexInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
161 refinement.refinedFaceNeighbor[positionInFace] =
true;
162 QHash<int, FaceRefinement>::iterator it = facesRefinement.find( neighborFaceIndex );
163 if ( it != facesRefinement.end() )
165 FaceRefinement &neighborRefinement = it.value();
166 int existingVertexLocalIndex = neighborRefinement.newVerticesLocalIndex.at( positionVertexInNeighbor );
167 refinement.newVerticesLocalIndex.append( existingVertexLocalIndex );
168 needCreateVertex =
false;
173 if ( needCreateVertex )
176 const QgsMeshVertex &vertex2 = mesh.
vertex( face.at( ( positionInFace + 1 ) % faceSize ) );
178 refinement.newVerticesLocalIndex.append(
mVerticesToAdd.count() );
182 ( vertex1.
y() + vertex2.
y() ) / 2,
183 ( vertex1.
z() + vertex2.
z() ) / 2 ) );
191 int faceStartIndex = startingGlobalFaceIndex +
mFacesToAdd.count();
195 for (
int i = 0; i < faceSize; ++i )
198 refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex,
199 refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex} );
200 refinement.newFacesChangesIndex.append(
mFacesToAdd.count() );
205 QgsMeshFace newFace( {refinement.newVerticesLocalIndex.at( 0 ) + startingVertexIndex,
206 refinement.newVerticesLocalIndex.at( 1 ) + startingVertexIndex,
207 refinement.newVerticesLocalIndex.at( ( 2 ) % faceSize ) + startingVertexIndex} );
208 refinement.newFacesChangesIndex.append(
mFacesToAdd.count() );
215 int centerVertexIndex =
mVerticesToAdd.count() + startingVertexIndex;
220 for (
int i = 0; i < faceSize; ++i )
223 refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex,
225 refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex} );
226 refinement.newFacesChangesIndex.append(
mFacesToAdd.count() );
234 refinement.newCenterVertexIndex = -1;
236 facesRefinement.insert( faceIndex, refinement );
239 for (
int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
241 if ( addedVerticesIndex.at( positionInFace ) != -1 )
244 refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex;
247 int vertexIndex = face.at( positionInFace );
249 mVerticesToFaceChanges.append( {vertexIndex, faceIndex, refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex} );
255 facesToRefine.remove( faceIndex );
260 for ( QHash<int, FaceRefinement>::iterator it = facesRefinement.begin(); it != facesRefinement.end(); ++it )
262 int faceIndex = it.key();
263 FaceRefinement &faceRefinement = it.value();
265 int faceSize = face.size();
269 for (
int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
271 if ( faceRefinement.refinedFaceNeighbor.at( positionInFace ) )
273 int neighborIndex = neighbors.at( positionInFace );
274 int firstVertexIndex = face.at( positionInFace );
275 int secondVertexIndex = faceRefinement.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
277 const FaceRefinement &otherRefinement = facesRefinement.value( neighborIndex );
279 int otherFaceSize = otherFace.size();
280 int otherPositionInface = ( vertexPositionInFace( firstVertexIndex, otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
282 int newFace1ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace ) );
283 const QgsMeshFace &newFace1 = mFacesToAdd.at( newFace1ChangesIndex );
284 int positionInNewface1Index = vertexPositionInFace( firstVertexIndex, newFace1 );
286 int newFace2ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace + 1 ) % faceSize );
287 const QgsMeshFace &newFace2 = mFacesToAdd.at( newFace2ChangesIndex );
288 int positionInNewface2Index = vertexPositionInFace( secondVertexIndex, newFace2 );
290 int otherNewFace1ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface ) % otherFaceSize );
291 int otherNewFace2ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface + 1 ) % otherFaceSize );
293 mFacesNeighborhoodToAdd[newFace1ChangesIndex][positionInNewface1Index] = otherNewFace2ChangesIndex + startingGlobalFaceIndex;
294 mFacesNeighborhoodToAdd[newFace2ChangesIndex][positionInNewface2Index] = otherNewFace1ChangesIndex + startingGlobalFaceIndex;
300bool QgsMeshEditRefineFaces::createNewBorderFaces(
QgsMeshEditor *meshEditor,
301 const QSet<int> &facesToRefine,
302 QHash<int, FaceRefinement> &facesRefinement,
303 QHash<int, BorderFace> &borderFaces )
309 int startingFaceChangesGlobalIndex = mesh.
faceCount();
312 for (
int faceIndexToRefine : facesToRefine )
315 int faceToRefineSize = faceToRefine.size();
317 const QVector<int> &neighbors = topology.
neighborsOfFace( faceIndexToRefine );
319 QHash<int, FaceRefinement>::iterator itFace = facesRefinement.find( faceIndexToRefine );
321 if ( itFace == facesRefinement.end() )
324 FaceRefinement &refinement = itFace.value();
326 for (
int posInFaceToRefine = 0; posInFaceToRefine < faceToRefineSize; ++posInFaceToRefine )
328 int neighborFaceIndex = neighbors.at( posInFaceToRefine );
329 if ( neighborFaceIndex != -1 && !facesToRefine.contains( neighborFaceIndex ) )
332 int neighborFaceSize = neighborFace.size();
333 int positionInNeighbor = vertexPositionInFace( mesh, faceToRefine.at( posInFaceToRefine ), neighborFaceIndex );
334 positionInNeighbor = ( positionInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
336 QHash<int, BorderFace>::iterator it = borderFaces.find( neighborFaceIndex );
337 if ( it == borderFaces.end() )
339 BorderFace borderFace;
340 for (
int i = 0; i < neighborFaceSize; ++i )
342 borderFace.unchangeFacesNeighbor.append(
false );
343 borderFace.borderFacesNeighbor.append(
false );
344 if ( i == positionInNeighbor )
346 borderFace.refinedFacesNeighbor.append(
true );
347 borderFace.newVerticesLocalIndex.append( refinement.newVerticesLocalIndex.at( posInFaceToRefine ) );
351 borderFace.refinedFacesNeighbor.append(
false );
352 borderFace.newVerticesLocalIndex.append( -1 );
355 borderFaces.insert( neighborFaceIndex, borderFace );
359 BorderFace &borderFace = it.value();
360 for (
int i = 0; i < neighborFaceSize; ++i )
362 if ( i == positionInNeighbor )
364 borderFace.unchangeFacesNeighbor[i] =
false;
365 borderFace.borderFacesNeighbor[i] =
false;
366 borderFace.refinedFacesNeighbor[i] =
true;
367 borderFace.newVerticesLocalIndex[i] = refinement.newVerticesLocalIndex.at( posInFaceToRefine );
376 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
378 int faceIndex = it.key();
379 BorderFace &borderFace = it.value();
382 int faceSize = face.size();
385 for (
int posInFace = 0; posInFace < faceSize; ++posInFace )
387 int neighborIndex = neighbors.at( posInFace );
389 if ( neighborIndex != -1 )
392 int neighborFaceSize = neighborFace.size();
393 int posInNeighbor = vertexPositionInFace( mesh, face.at( posInFace ), neighborIndex );
394 posInNeighbor = ( posInNeighbor - 1 + neighborFaceSize ) % neighborFaceSize;
396 QHash<int, FaceRefinement>::iterator itRefinement = facesRefinement.find( neighborIndex );
397 if ( itRefinement != facesRefinement.end() )
399 FaceRefinement &neighborRefinement = itRefinement.value();
400 neighborRefinement.borderFaceNeighbor[posInNeighbor] =
true;
401 borderFace.refinedFacesNeighbor[posInFace] =
true;
405 QHash<int, BorderFace>::iterator itNeighborBorder = borderFaces.find( neighborIndex );
406 if ( itNeighborBorder == borderFaces.end() )
407 borderFace.unchangeFacesNeighbor[posInFace] =
true;
410 BorderFace &neighborBorderFace = itNeighborBorder.value();
411 neighborBorderFace.borderFacesNeighbor[posInNeighbor] =
true;
412 borderFace.borderFacesNeighbor[posInFace] =
true;
417 borderFace.unchangeFacesNeighbor[posInFace] =
true;
423 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
425 int faceIndex = it.key();
426 BorderFace &borderFace = it.value();
429 int faceSize = face.size();
431 QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
432 std::vector<p2t::Point *> points;
433 for (
int i = 0; i < faceSize; ++i )
436 points.push_back(
new p2t::Point( vert.
x(), vert.
y() ) );
437 mapPoly2TriPointToVertex.insert( points.back(), face.at( i ) );
438 if ( borderFace.refinedFacesNeighbor.at( i ) )
440 int localVertexIndex = borderFace.newVerticesLocalIndex.at( i );
442 points.push_back(
new p2t::Point( newVert.
x(), newVert.
y() ) );
443 mapPoly2TriPointToVertex.insert( points.back(), localVertexIndex + startingVertexIndex );
449 std::unique_ptr<p2t::CDT> cdt(
new p2t::CDT( points ) );
451 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
452 QVector<QgsMeshFace> faces( triangles.size() );
453 for (
size_t i = 0; i < triangles.size(); ++i )
456 triangle.resize( 3 );
457 QVector<QgsMeshVertex> vertices( 3 );
458 for (
int j = 0; j < 3; j++ )
460 int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
462 throw std::exception();;
463 triangle[j] = vertInd;
464 if ( vertInd >= startingVertexIndex )
467 vertices[j] = mesh.
vertex( vertInd );
469 QgsMeshUtils::setCounterClockwise( triangle, vertices[0], vertices[1], vertices[2] );
476 QVector<QgsTopologicalMesh::FaceNeighbors> neighborhood = topologicalFaces.
facesNeighborhood();
479 for (
int i = 0; i < neighborhood.count(); ++i )
482 for (
int j = 0; j < neighbors.count(); ++j )
484 if ( neighbors[j] != -1 )
485 neighbors[j] = neighbors[j] + startingFaceIndex;
492 for (
int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
494 if ( borderFace.refinedFacesNeighbor.at( positionInFace ) )
497 QVector<int> vertexIndexes( 2 );
498 QVector<int> localFaceIndex( 2 );
499 vertexIndexes[0] = face.at( positionInFace );
500 vertexIndexes[1] = borderFace.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
502 int refinedFaceIndex = neighborOfFace.at( positionInFace );
503 const FaceRefinement &faceRefinement = facesRefinement.value( refinedFaceIndex );
505 int refinedFaceSize = refinedFace.size();
506 int positionInRefinedFace = ( vertexPositionInFace( vertexIndexes[0], refinedFace ) + refinedFaceSize - 1 ) % refinedFaceSize;
508 for (
int i = 0; i < 2; ++i )
511 circulator.goBoundaryClockwise();
512 localFaceIndex[i] = circulator.currentFaceIndex();
516 const QgsMeshFace newFace = faces.at( localFaceIndex.at( i ) );
517 int positionInNewFace = vertexPositionInFace( vertexIndexes.at( i ), newFace );
518 int newFaceRefinedIndexInChanges = faceRefinement.newFacesChangesIndex.at( ( positionInRefinedFace + ( 1 - i ) ) % refinedFaceSize ) ;
519 neighborsNewFace[positionInNewFace] = newFaceRefinedIndexInChanges + startingFaceChangesGlobalIndex;
523 int newRefinedFaceSize = newRefinedFace.size();
524 int positionInNewRefinedChange = ( vertexPositionInFace( vertexIndexes.at( i ), newRefinedFace ) + newRefinedFaceSize - 1 ) % newRefinedFaceSize;
525 neighborsRefinedFace[positionInNewRefinedChange] = localFaceIndex.at( i ) + startingFaceIndex;
528 borderFace.edgeFace.append( localFaceIndex.at( 0 ) + startingFaceIndex );
531 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
533 int vertexIndex = face.at( positionInFace );
535 circulator.goBoundaryClockwise();
536 int localFaceIndex = circulator.currentFaceIndex();
539 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
542 if ( borderFace.unchangeFacesNeighbor.at( positionInFace ) )
544 int vertexIndex = face.at( positionInFace );
546 circulator.goBoundaryClockwise();
547 int localFaceIndex = circulator.currentFaceIndex();
549 const QgsMeshFace &newFace = faces.at( localFaceIndex );
550 int positionInNewface = vertexPositionInFace( vertexIndex, newFace );
552 int unchangedFaceIndex = neighborOfFace.at( positionInFace );
553 neighborsNewFace[positionInNewface] = unchangedFaceIndex;
555 if ( unchangedFaceIndex != -1 )
558 int unchangedFaceSize = unchangedFace.size();
559 int positionInUnchangedFace = ( vertexPositionInFace( vertexIndex, unchangedFace ) + unchangedFaceSize - 1 ) % unchangedFaceSize;
560 mNeighborhoodChanges.append( {unchangedFaceIndex, positionInUnchangedFace, faceIndex, localFaceIndex + startingFaceIndex} );
563 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
570 for ( p2t::Point *pt : points )
581 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
583 int faceIndex = it.key();
584 BorderFace &borderFace = it.value();
586 int faceSize = face.size();
590 for (
int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
592 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
594 int otherIndex = neighbors.at( positionInFace );
596 int otherFaceSize = otherFace.size();
597 int otherPositionInface = ( vertexPositionInFace( face.at( positionInFace ), otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
598 const BorderFace &otherBorderFace = borderFaces.value( otherIndex );
599 int otherNewFaceIndex = otherBorderFace.edgeFace.at( otherPositionInface );
601 int newFaceChangesIndex = borderFace.edgeFace.at( positionInFace ) - startingFaceChangesGlobalIndex;
603 int newFacePositionInFace = vertexPositionInFace( face.at( positionInFace ), newFace );
609 for (
int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
611 int vertexIndex = face.at( positionInFace );
623 return QObject::tr(
"Refine %n face(s)",
nullptr,
mInputFaces.count() );
635 QSet<int> concernedFaces;
636 mChangingVertexMap = QHash<int, int>();
641 context.
lastScope()->
setVariable( QStringLiteral(
"_mesh_layer" ), QVariant::fromValue( layer ) );
643 QVector<QgsMeshVertex> newVertices;
649 bool calcX = !mExpressionX.isEmpty();
650 bool calcY = !mExpressionY.isEmpty();
651 bool calcZ = !mExpressionZ.isEmpty();
656 expressionX.
prepare( &context );
663 expressionY.
prepare( &context );
666 if ( calcX || calcY )
676 expressionZ.
prepare( &context );
686 mChangingVertexMap[vertexIndex] = i;
687 const QVariant xvar = expressionX.
evaluate( &context );
688 const QVariant yvar = expressionY.
evaluate( &context );
689 const QVariant zvar = expressionZ.
evaluate( &context );
693 if ( calcX || calcY )
699 concernedFaces.unite( qgis::listToSet( facesAround ) );
705 if ( xvar.isValid() )
707 double x = xvar.toDouble( &ok );
723 if ( yvar.isValid() )
725 double y = yvar.toDouble( &ok );
739 double z = std::numeric_limits<double>::quiet_NaN();
740 if ( zvar.isValid() )
742 z = zvar.toDouble( &ok );
744 z = std::numeric_limits<double>::quiet_NaN();
752 auto transformFunction = [
this, layer ](
int vi )->
const QgsMeshVertex
763 return QObject::tr(
"Transform %n vertices by expression",
nullptr,
mInputVertices.count() );
768 mExpressionX = expressionX;
769 mExpressionY = expressionY;
770 mExpressionZ = expressionZ;
772 mChangingVertexMap.clear();
784 int pos = mChangingVertexMap.value( vertexIndex, -1 );
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()
Destructor.
void clear()
Removes all data provided to the editing or created by the editing.
QList< int > mInputVertices
virtual bool isFinished() const
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
QgsMeshAdvancedEditing()
Constructor.
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.
QgsMeshEditRefineFaces()
Constructor.
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)
Convenient class that turn around a vertex and provide information about faces and vertices.
A class to represent a 2D point.
Point geometry type, with support for z-dimension and m-values.
Class that contains topological differences between two states of a topological mesh,...
QList< int > mChangeCoordinateVerticesIndexes
void clearChanges()
Clears all changes.
QVector< FaceNeighbors > mFacesNeighborhoodToRemove
QList< QgsPointXY > mNewXYValues
QList< std::array< int, 4 > > mNeighborhoodChanges
QList< int > mNativeFacesIndexesGeometryChanged
QVector< QgsMeshFace > mFacesToAdd
QVector< FaceNeighbors > mFacesNeighborhoodToAdd
QList< std::array< int, 3 > > mVerticesToFaceChanges
QList< QgsPointXY > mOldXYValues
QList< double > mNewZValues
QVector< QgsMeshVertex > mVerticesToAdd
int mAddedFacesFirstIndex
QVector< int > mVertexToFaceToAdd
QList< int > mFaceIndexesToRemove
QVector< QgsMeshFace > mFacesToRemove
QList< double > mOldZValues
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...
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.