Jump To …

PlanktonVertexList.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Plankton
{
#

PlanktonVertexList

Provides access to the vertices and PlanktonVertex related functionality of a Mesh.

    public class PlanktonVertexList : IEnumerable<PlanktonVertex>
    {
        private readonly PlanktonMesh _mesh;
        private List<PlanktonVertex> _list;
#

.ctor

Initializes a new instance of the PlanktonVertexList class. Should be called from the mesh constructor.

Parameters

  • owner: The PlanktonMesh to which this list of vertices belongs.
        internal PlanktonVertexList(PlanktonMesh owner)
        {
            this._list = new List<PlanktonVertex>();
            this._mesh = owner;
        }
#

Count

Gets the number of vertices.

        public int Count
        {
            get
            {
                return this._list.Count;
            }
        }
        
        #region methods
        #region vertex access
        #region adding
#

Add

Adds a new vertex to the end of the Vertex list.

Parameters

  • vertex: Vertex to add.

Returns

The index of the newly added vertex.

        internal int Add(PlanktonVertex vertex)
        {
            if (vertex == null) return -1;
            this._list.Add(vertex);
            return this.Count - 1;
        }
#

Add

Adds a new vertex to the end of the Vertex list.

Parameters

  • vertex: Vertex to add.

Returns

The index of the newly added vertex.

        internal int Add(PlanktonXYZ vertex)
        {            
            this._list.Add(new PlanktonVertex(vertex.X,vertex.Y,vertex.Z));
            return this.Count - 1;
        }
#

Add

Adds a new vertex to the end of the Vertex list.

Parameters

  • x: X component of new vertex coordinate.
  • y: Y component of new vertex coordinate.
  • z: Z component of new vertex coordinate.

Returns

The index of the newly added vertex.

        public int Add(double x, double y, double z)
        {
            return this.Add(new PlanktonVertex(x, y, z));
        }
#

Add

Adds a new vertex to the end of the Vertex list.

Parameters

  • x: X component of new vertex coordinate.
  • y: Y component of new vertex coordinate.
  • z: Z component of new vertex coordinate.

Returns

The index of the newly added vertex.

        public int Add(float x, float y, float z)
        {
            return this.Add(new PlanktonVertex(x, y, z));
        }
        #endregion
#

AddVertices

Adds a series of new vertices to the end of the vertex list.

Parameters

  • vertices: A list, an array or any enumerable set of PlanktonXYZ.

Returns

Indices of the newly created vertices.

        public int[] AddVertices(IEnumerable<PlanktonXYZ> vertices)
        {
            return vertices.Select(v => this.Add(v)).ToArray();
        }
#

index

Returns the PlanktonVertex at the given index.

Parameters

  • index: Index of vertex to get. Must be larger than or equal to zero and smaller than the Vertex Count of the mesh.

Returns

The vertex at the given index.

        public PlanktonVertex this[int index]
        {
            get
            {
                return this._list[index];
            }
            internal set
            {
                this._list[index] = value;
            }
        }
#

SetVertex

Sets or adds a vertex to the Vertex List.

If [index] is less than [Count], the existing vertex at [index] will be modified.

If [index] equals [Count], a new vertex is appended to the end of the vertex list.

If [index] is larger than [Count], the function will return false.

Parameters

  • vertexIndex: Index of vertex to set.
  • x: X component of vertex location.
  • y: Y component of vertex location.
  • z: Z component of vertex location.

Returns

on success, on failure.

        public bool SetVertex(int vertexIndex, float x, float y, float z)
        {
            if (vertexIndex >= 0 && vertexIndex < _list.Count)
            {
                var v = this._list[vertexIndex];
                v.X = x;
                v.Y = y;
                v.Z = z;
            }
            else if (vertexIndex == _list.Count)
            {
                this.Add(x, y, z);
            }
            else { return false; }
            
            return true;
        }
#

SetVertex

Sets or adds a vertex to the Vertex List.

If [index] is less than [Count], the existing vertex at [index] will be modified.

If [index] equals [Count], a new vertex is appended to the end of the vertex list.

If [index] is larger than [Count], the function will return false.

Parameters

  • vertexIndex: Index of vertex to set.
  • x: X component of vertex location.
  • y: Y component of vertex location.
  • z: Z component of vertex location.

Returns

on success, on failure.

        public bool SetVertex(int vertexIndex, double x, double y, double z)
        {
            if (vertexIndex >= 0 && vertexIndex < _list.Count)
            {
                var v = this._list[vertexIndex];
                v.X = (float)x;
                v.Y = (float)y;
                v.Z = (float)z;
            }
            else if (vertexIndex == _list.Count)
            {
                this.Add(x, y, z);
            }
            else { return false; }
            
            return true;
        }
        #endregion
#

CompactHelper

Helper method to remove dead vertices from the list, re-index and compact.

        internal int CompactHelper()
        {
            int marker = 0; // Location where the current vertex should be moved to
            
            // Run through all the vertices
            for (int iter = 0; iter < _list.Count; iter++)
            {
                // If vertex is alive, check if we need to shuffle it down the list
                if (!_list[iter].IsUnused)
                {
                    if (marker < iter)
                    {
                        // Room to shuffle. Copy current vertex to marked slot.
                        _list[marker] = _list[iter];
                        
                        // Update all halfedges which start here
                        int first = _list[marker].OutgoingHalfedge;
                        foreach (int h in _mesh.Halfedges.GetVertexCirculator(first))
                        {
                            _mesh.Halfedges[h].StartVertex = marker;
                        }
                    }
                    marker++; // That spot's filled. Advance the marker.
                }
            }
            
            // Trim list down to new size
            if (marker < _list.Count) { _list.RemoveRange(marker, _list.Count - marker); }
            
            return _list.Count - marker;
        }
#

CullUnused

Removes all vertices that are currently not used by the Halfedge list.

Returns

The number of unused vertices that were removed.

        public int CullUnused()
        {
            return this.CompactHelper();
        }
        
        #region traversals
#

GetHalfedgesCirculator

Traverses the halfedge indices which originate from a vertex.

Parameters

  • v: A vertex index.

Returns

An enumerable of halfedge indices incident to the specified vertex. Ordered clockwise around the vertex.

        [Obsolete("GetHalfedgesCirculator(int) is deprecated, please use" +
                  "Halfedges.GetVertexCirculator(int) instead.")]
        public IEnumerable<int> GetHalfedgesCirculator(int v)
        {
            int he_first = this[v].OutgoingHalfedge;
            if (he_first < 0) yield break; // vertex has no connectivity, exit
            int he_current = he_first;
            var hs = _mesh.Halfedges;
            do
            {
                yield return he_current;
                he_current = hs[hs.GetPairHalfedge(he_current)].NextHalfedge;
            }
            while (he_current != he_first);
        }
#

GetHalfedgesCirculator

Traverses the halfedge indices which originate from a vertex.

Parameters

  • v: A vertex index.
  • first: A halfedge index. Halfedge must start at the specified vertex.

Returns

An enumerable of halfedge indices incident to the specified vertex. Ordered clockwise around the vertex. The returned enumerable will start with the specified halfedge.

        [Obsolete("GetHalfedgesCirculator(int,int) is deprecated, please use" +
            "Halfedges.GetVertexCirculator(int) instead.")]
        public IEnumerable<int> GetHalfedgesCirculator(int v, int first)
        {
            if (_mesh.Halfedges[first].StartVertex != v)
                throw new ArgumentOutOfRangeException("Halfedge does not start at vertex.");
            // TODO: The code below is the same as above.
            // Can we refactor (without extra, unnecessary iterators)?
            int h = first;
            var hs = _mesh.Halfedges;
            do
            {
                yield return h;
                h = hs[hs.GetPairHalfedge(h)].NextHalfedge;
            }
            while (h != first);
        }
        #endregion

        #region adjacency queries
#

GetHalfedges

Gets the halfedges which originate from a vertex.

Parameters

  • v: A vertex index.

Returns

The indices of halfedges incident to a particular vertex. Ordered clockwise around the vertex.

        public int[] GetHalfedges(int v)
        {
            return _mesh.Halfedges.GetVertexCirculator(this[v].OutgoingHalfedge).ToArray();
        }
#

GetIncomingHalfedges

Gets the halfedges which end at a vertex.

Parameters

  • v: A vertex index.

Returns

The opposing halfedge for each returned by GetHalfedges(int). Ordered clockwise around the vertex.

        public int[] GetIncomingHalfedges(int v)
        {
            return _mesh.Halfedges.GetVertexCirculator(this[v].OutgoingHalfedge)
                .Select(h => _mesh.Halfedges.GetPairHalfedge(h)).ToArray();
        }
#

GetVertexNeighbours

Gets vertex neighbours (a.k.a. 1-ring).

Parameters

  • f: A vertex index.

Returns

An array of vertex indices incident to the specified vertex. Ordered clockwise around the vertex.

        public int[] GetVertexNeighbours(int v)
        {
            var hs = _mesh.Halfedges;
            return _mesh.Halfedges.GetVertexCirculator(this[v].OutgoingHalfedge)
                .Select(h => hs[hs.GetPairHalfedge(h)].StartVertex).ToArray();
        }
#

GetVertexFaces

Gets faces incident to a vertex.

Parameters

  • v: A vertex index.

Returns

An array of face indices incident to the specified vertex. Ordered clockwise around the vertex

        public int[] GetVertexFaces(int v)
        {
            return _mesh.Halfedges.GetVertexCirculator(this[v].OutgoingHalfedge)
                .Select(h => _mesh.Halfedges[h].AdjacentFace).ToArray();
        }
#

GetIncomingHalfedge

Gets the first halfedge for a vertex.

Parameters

  • v: A vertex index.

Returns

The index of the halfedge paired with the specified vertex's .

        public int GetIncomingHalfedge(int v)
        {
            return _mesh.Halfedges.GetPairHalfedge(this[v].OutgoingHalfedge);
        }
        #endregion
#

NakedEdgeCount

Gets the number of naked edges incident to this vertex.

Parameters

  • v: A vertex index.

Returns

The number of incident halfedges which lie on a boundary.

        public int NakedEdgeCount(int v)
        {
            int nakedCount = 0;
            var hs = _mesh.Halfedges;
            foreach (int i in _mesh.Halfedges.GetVertexCirculator(this[v].OutgoingHalfedge))
            {
                if (hs[i].AdjacentFace == -1 || hs[hs.GetPairHalfedge(i)].AdjacentFace == -1)
                    nakedCount++;
            }
            return nakedCount;
        }
#

GetValence

Gets the number of edges incident to this vertex.

Parameters

  • v: A vertex index.

Returns

The number of incident edges.

        public int GetValence(int v)
        {
            int h = this[v].OutgoingHalfedge;
            return _mesh.Halfedges.GetVertexCirculator(h).Count();
        }
#

IsBoundary

A vertex is on a boundary if its outgoing halfedge has no adjacent face.

Parameters

  • index: The index of a vertex.

Returns

if the specified vertex is on a boundary; otherwise, . Also returns if the vertex is unused (i.e. no outgoing halfedge).

        public bool IsBoundary(int index)
        {
            int h = this[index].OutgoingHalfedge;
            return (h < -1 || _mesh.Halfedges[h].AdjacentFace == -1);
        }
#

GetNormal

Gets the normal vector at a vertex.

Parameters

  • index: The index of a vertex.

Returns

The area weighted vertex normal.

        public PlanktonXYZ GetNormal(int index)
        {
            PlanktonXYZ vertex = this[index].ToXYZ();
            PlanktonXYZ normal = new PlanktonXYZ();

            var ring = this.GetVertexNeighbours(index);
            int n = ring.Length;

            for (int i = 0; i < n-1; i++)
            {
                normal += PlanktonXYZ.CrossProduct(
                    this[ring[i]].ToXYZ() - vertex, 
                    this[ring[i+1]].ToXYZ() - vertex);
            }

            if (this.IsBoundary(index) == false)
            {
                normal += PlanktonXYZ.CrossProduct(
                    this[n-1].ToXYZ() - vertex,
                    this[0].ToXYZ() - vertex);
            }

            return normal * (-1.0f / normal.Length); // return unit vector
        }
#

GetNormals

Gets the normal vectors for all vertices in the mesh.

Returns

The area weighted vertex normals of all vertices in the mesh.

Remarks

This will be accurate at the time of calling but will quickly become outdated if you start fiddling with the mesh.

        public PlanktonXYZ[] GetNormals()
        {
            return Enumerable.Range(0, this.Count).Select(i => this.GetNormal(i)).ToArray();
        }
#

GetPositions

Gets the positions of all vertices.

Returns

The positions of all vertices in the mesh.

        public PlanktonXYZ[] GetPositions()
        {
            return Enumerable.Range(0, this.Count).Select(i => this[i].ToXYZ()).ToArray();
        }

        #region Euler operators
#

MergeVertices

Merges two vertices by collapsing the pair of halfedges between them.

Parameters

  • halfedge: The index of a halfedge between the two vertices to be merged. The starting vertex of this halfedge will be retained.

Returns

The successor of index around its vertex, or -1 on failure.

Remarks

The invariant will return a, leaving the mesh unchanged.

        public int MergeVertices(int halfedge)
        {
            return _mesh.Halfedges.CollapseEdge(halfedge);

        }
#

SplitVertex

Splits the vertex into two, joined by a new pair of halfedges.

Parameters

  • first: The index of a halfedge which starts at the vertex to split.
  • second: The index of a second halfedge which starts at the vertex to split.

Returns

The new halfedge which starts at the existing vertex.

Remarks

After the split, the second halfedge will be starting at the newly added vertex.

        public int SplitVertex(int first, int second)
        {
            var hs = _mesh.Halfedges;
            // Check that both halfedges start at the same vertex
            int v_old = hs[first].StartVertex;
            if (v_old != hs[second].StartVertex) { return -1; } // TODO: return ArgumentException instead?

            // Create a copy of the existing vertex (user can move it afterwards if needs be)
            int v_new = this.Add(this[v_old].ToXYZ()); // copy vertex by converting to XYZ and back

            // Go around outgoing halfedges, from 'second' to just before 'first'
            // Set start vertex to new vertex
            bool reset_v_old = false;
            foreach (int h in hs.GetVertexCirculator(second))
            {
                if (h == first) { break; }
                hs[h].StartVertex = v_new;
                // If new vertex has no outgoing yet and current he is naked...
                if (this[v_new].OutgoingHalfedge == -1 && hs[h].AdjacentFace == -1)
                    this[v_new].OutgoingHalfedge = h;
                // Also check whether existing vert's he is now incident to new one
                if (h == this[v_old].OutgoingHalfedge) { reset_v_old = true; }
            }
            // If no naked halfedges, just use 'second'
            if (this[v_new].OutgoingHalfedge == -1) { this[v_new].OutgoingHalfedge = second; }

            // Add the new pair of halfedges from old vertex to new
            int h_new = hs.AddPair(v_old, v_new, hs[second].AdjacentFace);
            int h_new_pair = hs.GetPairHalfedge(h_new);
            hs[h_new_pair].AdjacentFace = hs[first].AdjacentFace;

            // Link new pair into mesh
            hs.MakeConsecutive(hs[first].PrevHalfedge, h_new_pair);
            hs.MakeConsecutive(h_new_pair, first);
            hs.MakeConsecutive(hs[second].PrevHalfedge, h_new);
            hs.MakeConsecutive(h_new, second);

            // Re-set existing vertex's outgoing halfedge, if necessary
            if (reset_v_old)
            {
                this[v_old].OutgoingHalfedge = h_new;
                foreach (int h in hs.GetVertexCirculator(h_new))
                {
                    if (hs[h].AdjacentFace == -1) { this[v_old].OutgoingHalfedge = h; }
                }
            }

            // return the new vertex which starts at the existing vertex
            return h_new;
        }
#

EraseCenterVertex

Erases a vertex and all incident halfedges by merging its incident faces.

Parameters

  • halfedgeIndex: The index of a halfedge which starts at the vertex to erase. The retained face will be the one adjacent to this halfedge.

Returns

The successor of halfedgeIndex around its original face.

        public int EraseCenterVertex(int halfedgeIndex)
        {
            int vertexIndex = _mesh.Halfedges[halfedgeIndex].StartVertex;

            // Check that the vertex is completely surrounded by faces
            if (this.IsBoundary(vertexIndex))
                throw new ArgumentException("Center vertex must not be on a boundary");

            // Get outgoing halfedges around vertex, starting with specified halfedge
            int[] vertexHalfedges = _mesh.Halfedges.GetVertexCirculator(halfedgeIndex).ToArray();

            // Check for 2-valent vertices in the 1-ring (no antennas)
            int v;
            foreach (int h in vertexHalfedges)
            {
                v = _mesh.Halfedges.EndVertex(h);
                if (this.GetHalfedges(v).Length < 3)
                    throw new ArgumentException("Vertex in 1-ring is 2-valent");
            }

            // Store face to keep and set its first halfedge
            int faceIndex = _mesh.Halfedges[halfedgeIndex].AdjacentFace;
            int firstHalfedge = _mesh.Halfedges[halfedgeIndex].NextHalfedge;
            _mesh.Faces[faceIndex].FirstHalfedge = firstHalfedge;

            // Remove incident halfedges and mark faces for deletion (except first face)
            _mesh.Halfedges.RemovePairHelper(vertexHalfedges[0]);
            for (int i = 1; i < vertexHalfedges.Length; i++)
            {
                _mesh.Faces[_mesh.Halfedges[vertexHalfedges[i]].AdjacentFace] = PlanktonFace.Unset;
                _mesh.Halfedges.RemovePairHelper(vertexHalfedges[i]);
            }

            // Set adjacent face for all halfedges in hole
            foreach (int h in _mesh.Halfedges.GetFaceCirculator(firstHalfedge))
            {
                _mesh.Halfedges[h].AdjacentFace = faceIndex;
            }

            // Mark center vertex for deletion
            this[vertexIndex] = PlanktonVertex.Unset;

            return _mesh.Faces[faceIndex].FirstHalfedge;
        }
        #endregion
#

TruncateVertex

Truncates a vertex by creating a face with vertices on each of the outgoing halfedges.

Parameters

  • v: The index of a vertex.

Returns

The index of the newly created face.

        public int TruncateVertex(int v)
        {
            var hs = this.GetHalfedges(v);
        
            // set h_new and move original vertex
            int h_new = hs[0];
        
            // circulate outgoing halfedges (clockwise, skip first)
            for (int i = 1; i < hs.Length; i++)
            {
                // split vertex
                int h_tmp = this.SplitVertex(hs[i], h_new);
                h_new = h_tmp; // tidy-up if 'vs' is removed
            }

            // split face to create new truncated face
            int splitH = this._mesh.Faces.SplitFace(hs[0], h_new);

            return this._mesh.Halfedges[this._mesh.Halfedges.GetPairHalfedge(splitH)].AdjacentFace;
        }
        #endregion
        
        #region IEnumerable implementation
#

GetEnumerator

Gets an enumerator that yields all faces in this collection.

Returns

An enumerator.

        public IEnumerator<PlanktonVertex> GetEnumerator()
        {
            return this._list.GetEnumerator();
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion
    }
}