/// <summary>
        /// Assumes quadrilateral faces.
        /// </summary>
        public static IEnumerable <HeQuadStrip <V, E, F> > GetQuadStrips <V, E, F>(HeMesh <V, E, F> mesh, bool flip)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var faces = mesh.Faces;

            var result  = new List <HeQuadStrip <V, E, F> >();
            var stack   = new Stack <E>();
            int currTag = faces.NextTag;

            for (int i = 0; i < faces.Count; i++)
            {
                var f = faces[i];
                if (f.IsUnused || f.Tag == currTag || !f.IsDegree(4))
                {
                    continue;                                                   // skip if unused, visited, or non-quads
                }
                stack.Push((flip) ? f.First.Next : f.First);

                foreach (var strip in GetQuadStrips <V, E, F>(stack, currTag))
                {
                    yield return(strip);
                }
            }
        }
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="UV"></typeparam>
 /// <typeparam name="UE"></typeparam>
 /// <typeparam name="UF"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="setVertex"></param>
 /// <param name="setHedge"></param>
 /// <param name="setFace"></param>
 /// <returns></returns>
 public TM[] CreateConnectedComponents <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UV> setVertex = null, Action <TE, UE> setHedge = null, Action <TF, UF> setFace = null)
     where UV : HeMesh <UV, UE, UF> .Vertex
     where UE : HeMesh <UV, UE, UF> .Halfedge
     where UF : HeMesh <UV, UE, UF> .Face
 {
     return(CreateConnectedComponents(mesh, out int[] compIds, out int[] edgeIds, setVertex, setHedge, setFace));
 }
Exemple #3
0
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="getStart"></param>
 /// <returns></returns>
 public static Strip <V, E, F> CreateStrip <V, E, F>(HeMesh <V, E, F> mesh, Func <F, E> getStart)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     return(new Strip <V, E, F>(mesh, getStart));
 }
Exemple #4
0
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="start"></param>
 /// <param name="setEdge"></param>
 public static void Unroll <V, E, F>(HeMesh <V, E, F> mesh, F start, Func <V, Vec3d> getPosition, Action <V, Vec3d> setUnrolledPosition, Func <E, double> getUnrollFactor = null)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     Impl <V, E, F> .Unroll(mesh, start, getPosition, setUnrolledPosition, getUnrollFactor);
 }
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <typeparam name="E"></typeparam>
        /// <typeparam name="F"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="position"></param>
        private static void QuadSplitGeometry <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            // create face vertices
            foreach (var f in mesh.Faces)
            {
                var v = mesh.AddVertex();

                if (!f.IsUnused)
                {
                    position.Set(v, f.Vertices.Mean(position.Get));
                }
            }

            // create edge vertices
            foreach (var he in mesh.Edges)
            {
                var v = mesh.AddVertex();

                if (!he.IsUnused)
                {
                    var p = (position.Get(he.Start) + position.Get(he.End)) * 0.5;
                    position.Set(v, p);
                }
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        public G CreateFromFaceTopology(HeMesh <HeMesh3d.Vertex, HeMesh3d.Halfedge, HeMesh3d.Face> mesh)
        {
            var graph = Create(mesh.Faces.Count, mesh.Halfedges.Count);

            graph.AppendFaceTopology(mesh);
            return(graph);
        }
Exemple #7
0
            /// <summary>
            ///
            /// </summary>
            /// <param name="mesh"></param>
            /// <param name="start"></param>
            /// <param name="setEdge"></param>
            public static void DetachFaceCycles(HeMesh <V, E, F> mesh, F start, Action <E, E> setEdge)
            {
                var currTag = mesh.Halfedges.NextTag;

                // tag traversed edges during BFS
                foreach (var he in mesh.GetFacesBreadthFirst2(start.Yield()))
                {
                    he.Edge.Tag = currTag;
                }

                var edges = mesh.Edges;
                var ne    = edges.Count;

                // detach all untagged edges
                for (int i = 0; i < ne; i++)
                {
                    var he0 = edges[i];

                    if (he0.IsUnused || he0.IsBoundary || he0.Tag == currTag)
                    {
                        continue;
                    }

                    var he1 = mesh.DetachEdgeImpl(he0);
                    setEdge(he1, he0);
                }
            }
Exemple #8
0
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="start"></param>
 /// <param name="setEdge"></param>
 public static void DetachFaceCycles <V, E, F>(HeMesh <V, E, F> mesh, F start, Action <E, E> setEdge)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     Impl <V, E, F> .DetachFaceCycles(mesh, start, setEdge);
 }
        /// <summary>
        ///
        /// </summary>
        private static void CatmullClarkSmoothFixed <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var verts = mesh.Vertices;

            int ev0 = verts.Count - mesh.Edges.Count; // index of first edge vertex
            int fv0 = ev0 - mesh.Faces.Count;         // index of first face vertex

            // set old vertices
            for (int i = 0; i < fv0; i++)
            {
                var v = verts[i];
                if (v.IsUnused || v.IsBoundary)
                {
                    continue;                             // skip boundary verts
                }
                var fsum = new Vec3d();
                var esum = new Vec3d();
                int n    = 0;

                foreach (var he in v.OutgoingHalfedges)
                {
                    fsum += position.Get(verts[he.Face.Index + fv0]);
                    esum += position.Get(verts[(he.Index >> 1) + ev0]);
                    n++;
                }

                var t = 1.0 / n;
                position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t);
            }
        }
Exemple #10
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="path"></param>
 public static void WriteToObj <TV, TE, TF>(HeMesh <TV, TE, TF> mesh, string path, Func <TV, Vec2d> getTexture = null)
     where TV : HeMesh <TV, TE, TF> .Vertex, IPosition3d, INormal3d
     where TE : HeMesh <TV, TE, TF> .Halfedge
     where TF : HeMesh <TV, TE, TF> .Face
 {
     WriteToObj(mesh, path, IPosition3d <TV> .Get, INormal3d <TV> .Get, getTexture);
 }
Exemple #11
0
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <returns></returns>
 public static Strip <V, E, F> CreateStrip <V, E, F>(HeMesh <V, E, F> mesh)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     return(new Strip <V, E, F>(mesh, f => f.First));
 }
Exemple #12
0
        /// <summary>
        /// Throws an exception if the topology of the given mesh is not valid.
        /// </summary>
        /// <typeparam name="TV"></typeparam>
        /// <typeparam name="TE"></typeparam>
        /// <typeparam name="TF"></typeparam>
        /// <param name="mesh"></param>
        internal static void CheckTopology <TV, TE, TF>(HeMesh <TV, TE, TF> mesh)
            where TV : HeMesh <TV, TE, TF> .Vertex
            where TE : HeMesh <TV, TE, TF> .Halfedge
            where TF : HeMesh <TV, TE, TF> .Face
        {
            var verts  = mesh.Vertices;
            var hedges = mesh.Halfedges;
            var faces  = mesh.Faces;

            // ensure halfedges are reciprocally linked
            foreach (var he in hedges)
            {
                if (he.IsUnused)
                {
                    continue;
                }
                if (he.Previous.Next != he && he.Next.Previous != he)
                {
                    Throw();
                }
                if (he.Start.IsUnused)
                {
                    Throw();
                }
                if (he.Face.IsUnused)
                {
                    Throw();
                }
            }

            // ensure consistent start vertex during circulation
            foreach (var v in verts)
            {
                foreach (var he in v.OutgoingHalfedges)
                {
                    if (he.Start != v)
                    {
                        Throw();
                    }
                }
            }

            // ensure consistent face during circulation
            foreach (var f in faces)
            {
                foreach (var he in f.Halfedges)
                {
                    if (he.Face != f)
                    {
                        Throw();
                    }
                }
            }

            void Throw()
            {
                throw new Exception("The topology of the given mesh is invalid");
            }
        }
Exemple #13
0
 /// <summary>
 /// Starts the triangulation from the halfedge with the smallest key in each face.
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="getKey"></param>
 /// <returns></returns>
 public static Fan <V, E, F> CreateFan <V, E, F, K>(HeMesh <V, E, F> mesh, Func <E, K> getKey)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
     where K : IComparable <K>
 {
     return(new Fan <V, E, F>(mesh, f => f.Halfedges.SelectMin(getKey)));
 }
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="hedge"></param>
 /// <returns></returns>
 public static HeQuadStrip <V, E, F> GetQuadStrip <V, E, F>(HeMesh <V, E, F> mesh, E hedge)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     mesh.Halfedges.OwnsCheck(hedge);
     return(GetQuadStrip <V, E, F>(hedge, mesh.Faces.NextTag));
 }
 /// <summary>
 /// Applies a single iteration of Catmull-Clark subdivision to the given mesh.
 /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices, edges, and faces in the initial mesh.
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="position"></param>
 public static void QuadSplit <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     QuadSplitGeometry(mesh, position);
     QuadSplitTopology(mesh);
 }
 /// <summary>
 ///
 /// </summary>
 /// <typeparam name="V"></typeparam>
 /// <typeparam name="E"></typeparam>
 /// <typeparam name="F"></typeparam>
 /// <param name="mesh"></param>
 /// <param name="position"></param>
 public static void Loop <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position)
     where V : HeMesh <V, E, F> .Vertex
     where E : HeMesh <V, E, F> .Halfedge
     where F : HeMesh <V, E, F> .Face
 {
     // TODO implement
     throw new NotImplementedException();
 }
Exemple #17
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="path"></param>
        /// <param name="graph"></param>
        /// <param name="setVertexAttributes"></param>
        /// <param name="setHedgeAttributes"></param>
        public static void ReadFromJson <V, E, F, VA, EA, FA>(string path, HeMesh <V, E, F> mesh, Action <V, VA> setVertexAttributes = null, Action <E, EA> setHedgeAttributes = null, Action <F, FA> setFaceAttributes = null)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var buffer = CoreIO.DeserializeJson <HeMeshJsonBuffer <VA, EA, FA> >(path);

            buffer.ReadTo(mesh, setVertexAttributes, setHedgeAttributes, setFaceAttributes);
        }
Exemple #18
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="UV"></typeparam>
        /// <typeparam name="UE"></typeparam>
        /// <typeparam name="UF"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="setVertex"></param>
        /// <param name="setHedge"></param>
        public void AppendVertexTopology <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UV> setVertex = null, Action <TE, UE> setHedge = null)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            int nhe = Halfedges.Count;
            int nv  = Vertices.Count;

            var meshHedges = mesh.Halfedges;
            var meshVerts  = mesh.Vertices;

            // append new elements
            for (int i = 0; i < meshVerts.Count; i++)
            {
                AddVertex();
            }

            for (int i = 0; i < meshHedges.Count; i += 2)
            {
                AddEdge();
            }

            // set vertex refs
            for (int i = 0; i < meshVerts.Count; i++)
            {
                var v0 = meshVerts[i];
                var v1 = Vertices[i + nv];

                // transfer attributes
                setVertex?.Invoke(v1, v0);

                if (v0.IsUnused)
                {
                    continue;
                }
                v1.First = Halfedges[v0.First + nhe];
            }

            // set halfedge refs
            for (int i = 0; i < meshHedges.Count; i++)
            {
                var he0 = meshHedges[i];
                var he1 = Halfedges[i + nhe];

                // transfer attributes
                setHedge?.Invoke(he1, he0);

                if (he0.IsUnused)
                {
                    continue;
                }
                he1.Previous = Halfedges[he0.Previous + nhe];
                he1.Next     = Halfedges[he0.Next + nhe];
                he1.Start    = Vertices[he0.Start + nv];
            }
        }
        /// <summary>
        /// Returns the dual of the given mesh.
        /// Action delegates specify how attributes of primal elements are mapped to attributes of dual elements.
        /// Note this method preserves indexical correspondance between primal and dual elements.
        /// </summary>
        /// <typeparam name="UV"></typeparam>
        /// <typeparam name="UE"></typeparam>
        /// <typeparam name="UF"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="setVertex"></param>
        /// <param name="setHedge"></param>
        /// <param name="setFace"></param>
        /// <returns></returns>
        public TM CreateDual <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UF> setVertex = null, Action <TE, UE> setHedge = null, Action <TF, UV> setFace = null)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            var dual = Create(mesh.Vertices.Capacity, mesh.Halfedges.Capacity, mesh.Faces.Capacity);

            dual.AppendDual(mesh, setVertex, setHedge, setFace);
            return(dual);
        }
Exemple #20
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="UV"></typeparam>
        /// <typeparam name="UE"></typeparam>
        /// <typeparam name="UF"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="setHedge"></param>
        /// <param name="setVertex"></param>
        /// <returns></returns>
        public TG CreateFromFaceTopology <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UF> setVertex = null, Action <TE, UE> setHedge = null)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            var result = Create(mesh.Faces.Count, mesh.Halfedges.Count);

            result.AppendFaceTopology(mesh, setVertex, setHedge);
            return(result);
        }
Exemple #21
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <typeparam name="E"></typeparam>
        public static void WriteToJson <V, E, F, VA, EA, FA>(HeMesh <V, E, F> mesh, string path, Func <V, VA> getVertexAttributes = null, Func <E, EA> getHedgeAttributes = null, Func <F, FA> getFaceAttributes = null)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var buffer = new HeMeshJsonBuffer <VA, EA, FA>();

            buffer.WriteFrom(mesh, getVertexAttributes, getHedgeAttributes, getFaceAttributes);
            CoreIO.SerializeJson(buffer, path);
        }
        /// <summary>
        /// Action delegates specify how attributes of original elements are mapped to attributes of copied elements.
        /// </summary>
        /// <typeparam name="UV"></typeparam>
        /// <typeparam name="UE"></typeparam>
        /// <typeparam name="UF"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="setVertex"></param>
        /// <param name="setHedge"></param>
        /// <param name="setFace"></param>
        /// <returns></returns>
        public TM CreateCopy <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UV> setVertex = null, Action <TE, UE> setHedge = null, Action <TF, UF> setFace = null)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            var copy = Create(mesh.Vertices.Capacity, mesh.Halfedges.Capacity, mesh.Faces.Capacity);

            copy.Append(mesh, setVertex, setHedge, setFace);
            return(copy);
        }
        /// <summary>
        /// If using external buffers to store vertex attributes, the number of vertices in the resulting mesh equals 8 times the number of edges in the given mesh.
        /// </summary>
        /// <typeparam name="UV"></typeparam>
        /// <typeparam name="UE"></typeparam>
        /// <typeparam name="UF"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="getPosition"></param>
        /// <param name="getScale"></param>
        /// <param name="getNormal"></param>
        /// <param name="getCenter"></param>
        /// <param name="setPosition"></param>
        /// <returns></returns>
        public TM CreateWeave <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Func <UV, Vec3d> getPosition, Func <UE, double> getScale, Func <UE, Vec3d> getNormal, Func <UF, Vec3d> getCenter, Action <TV, Vec3d> setPosition)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            int ne     = mesh.Edges.Count;
            var result = Create(ne << 3, ne << 4, ne << 3);

            CreateWeaveGeometry(mesh, result, getPosition, getScale, getNormal, getCenter, setPosition);
            CreateWeaveTopology(mesh, result);
            return(result);
        }
        /// <summary>
        /// Applies a single iteration of Catmull-Clark subdivision to the given mesh.
        /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices, edges, and faces in the initial mesh.
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <typeparam name="E"></typeparam>
        /// <typeparam name="F"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="position"></param>
        /// <param name="boundaryType"></param>
        public static void CatmullClark <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, SmoothBoundaryType boundaryType)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            // impl ref
            // http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface
            // http://w3.impa.br/~lcruz/courses/cma/surfaces.html

            CatmullClarkGeometry(mesh, position, boundaryType);
            QuadSplitTopology(mesh);
        }
Exemple #25
0
        /// <summary>
        /// Implementation currently ignores texture coordinates and normals.
        /// </summary>
        /// <typeparam name="TV"></typeparam>
        /// <typeparam name="TE"></typeparam>
        /// <typeparam name="TF"></typeparam>
        /// <param name="path"></param>
        /// <param name="mesh"></param>
        /// <param name="setPosition"></param>
        public static void ReadFromObj <TV, TE, TF>(string path, HeMesh <TV, TE, TF> mesh, Action <TV, Vec3d> setPosition)
            where TV : HeMesh <TV, TE, TF> .Vertex
            where TE : HeMesh <TV, TE, TF> .Halfedge
            where TF : HeMesh <TV, TE, TF> .Face
        {
            var verts = mesh.Vertices;
            var faces = mesh.Faces;
            var face  = new List <int>();

            using (var reader = new StreamReader(path))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    // skip empty lines and comments
                    if (line.Length == 0 || line[0] == '#')
                    {
                        continue;
                    }

                    // check the first character
                    var segments = line.Split(ObjUtil.Separators, StringSplitOptions.RemoveEmptyEntries);
                    switch (segments[0])
                    {
                    case "v":
                    {
                        // parse vertex
                        double x = double.Parse(segments[1]);
                        double y = double.Parse(segments[2]);
                        double z = double.Parse(segments[3]);

                        var v = mesh.AddVertex();
                        setPosition(v, new Vec3d(x, y, z));
                        break;
                    }

                    case "f":
                    {
                        // parse face
                        for (int i = 1; i < segments.Length; i++)
                        {
                            var ids = segments[i].Split(ObjUtil.FaceSeparators);
                            face.Add(int.Parse(ids[0]) - 1);
                        }

                        mesh.AddFace(face);
                        face.Clear();
                        break;
                    }
                    }
                }
            }
        }
        /*
         * /// <summary>
         * /// Applies a single iteration of Catmull-Clark subdivision to the given mesh.
         * /// http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface
         * /// http://w3.impa.br/~lcruz/courses/cma/surfaces.html
         * /// </summary>
         * /// <typeparam name="V"></typeparam>
         * /// <typeparam name="E"></typeparam>
         * /// <typeparam name="F"></typeparam>
         * /// <param name="mesh"></param>
         * /// <param name="position"></param>
         * /// <param name="boundaryType"></param>
         * public static void CatmullClark<V, E, F>(HeMesh<V, E, F> mesh, Property<V, Vec3d> position, SmoothBoundaryType boundaryType, bool parallel)
         *  where V : HeMeshBase<V, E, F>.Vertex
         *  where E : HeMeshBase<V, E, F>.Halfedge
         *  where F : HeMeshBase<V, E, F>.Face
         * {
         *  CatmullClarkGeometry(mesh, position, boundaryType, parallel);
         *  QuadSplitTopology(mesh);
         * }
         *
         *
         * /// <summary>
         * ///
         * /// </summary>
         * private static void CatmullClarkGeometry<V, E, F>(HeMesh<V, E, F> mesh, Property<V, Vec3d> position, SmoothBoundaryType boundaryType, bool parallel)
         * where V : HeMeshBase<V, E, F>.Vertex
         * where E : HeMeshBase<V, E, F>.Halfedge
         * where F : HeMeshBase<V, E, F>.Face
         * {
         *  var verts = mesh.Vertices;
         *  var edges = mesh.Edges;
         *  var faces = mesh.Faces;
         *
         *  int fv0 = verts.Count; // index of first face vertex
         *  int ev0 = verts.Count + faces.Count;
         *
         *  // add all new vertices
         *  mesh.AddVertices(faces.Count);
         *  mesh.AddVertices(edges.Count);
         *
         *  // set attributes of face vertices
         *  Action<Tuple<int, int>> setFaceVerts = range =>
         *   {
         *       for (int i = range.Item1; i < range.Item2; i++)
         *       {
         *           var f = faces[i];
         *
         *           if (!f.IsUnused)
         *               position.Set(verts[i + fv0], f.Vertices.Mean(position.Get));
         *       }
         *   };
         *
         *  // set attributes of edge vertices
         *  Action<Tuple<int, int>> setEdgeVerts = range =>
         *  {
         *      for (int i = range.Item1; i < range.Item2; i++)
         *      {
         *          var he0 = edges[i];
         *          if (he0.IsUnused) continue;
         *
         *          if (he0.IsBoundary)
         *          {
         *              position.Set(verts[i + ev0], he0.Lerp(position.Get, 0.5));
         *              continue;
         *          }
         *
         *          var he1 = he0.Twin;
         *          var p0 = position.Get(he0.Start);
         *          var p1 = position.Get(he1.Start);
         *          var p2 = position.Get(verts[he0.Face.Index + fv0]);
         *          var p3 = position.Get(verts[he1.Face.Index + fv0]);
         *          position.Set(verts[i + ev0], (p0 + p1 + p2 + p3) * 0.25);
         *      }
         *  };
         *
         *  // set attributes of old vertices
         *  //CatmullClarkSmooth(mesh, position, boundaryType);
         *  Action<Tuple<int, int>> setOldVerts = range =>
         *  {
         *      for (int i = range.Item1; i < range.Item2; i++)
         *      {
         *          var v = verts[i];
         *          if (v.IsUnused) continue;
         *
         *          if (v.IsBoundary)
         *          {
         *              var he0 = v.FirstOut;
         *              var he1 = he0.PrevInFace;
         *              var p0 = position.Get(verts[(he0.Index >> 1) + ev0]);
         *              var p1 = position.Get(verts[(he1.Index >> 1) + ev0]);
         *              position.Set(v, position.Get(v) * 0.5 + (p0 + p1) * 0.25);
         *          }
         *          else
         *          {
         *              Vec3d fsum = new Vec3d();
         *              Vec3d esum = new Vec3d();
         *              int n = 0;
         *
         *              foreach (var he in v.OutgoingHalfedges)
         *              {
         *                  fsum += position.Get(verts[he.Face.Index + fv0]);
         *                  esum += position.Get(verts[(he.Index >> 1) + ev0]);
         *                  n++;
         *              }
         *
         *              double t = 1.0 / n;
         *              position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t);
         *          }
         *      }
         *  };
         *
         *
         *  if (parallel)
         *  {
         *      Parallel.ForEach(Partitioner.Create(0, faces.Count), setFaceVerts);
         *      Parallel.ForEach(Partitioner.Create(0, edges.Count), setEdgeVerts);
         *      Parallel.ForEach(Partitioner.Create(0, verts.Count), setOldVerts);
         *  }
         *  else
         *  {
         *      setFaceVerts(Tuple.Create(0, faces.Count));
         *      setEdgeVerts(Tuple.Create(0, edges.Count));
         *      setOldVerts(Tuple.Create(0, verts.Count));
         *  }
         * }
         */

        #endregion


        /// <summary>
        /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices and faces in the initial mesh.
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <typeparam name="E"></typeparam>
        /// <typeparam name="F"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="position"></param>
        /// <param name="skipBoundary"></param>
        public static void Diagonalize <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, bool skipBoundary)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var edges = mesh.Edges;
            var faces = mesh.Faces;

            int ne = edges.Count;
            int nf = faces.Count;

            // stellate faces
            for (int i = 0; i < nf; i++)
            {
                var f = faces[i];
                if (f.IsUnused)
                {
                    continue;
                }

                var v = mesh.AddVertex();
                position.Set(v, f.GetBarycenter(position.Get));
                mesh.PokeFaceImpl(f.First, v);
            }

            // merge faces
            if (skipBoundary)
            {
                for (int i = 0; i < ne; i++)
                {
                    var he = edges[i];
                    if (he.IsUnused || he.IsBoundary)
                    {
                        continue;
                    }
                    mesh.MergeFaces(he);
                }
            }
            else
            {
                for (int i = 0; i < ne; i++)
                {
                    var he = edges[i];
                    if (he.IsUnused)
                    {
                        continue;
                    }
                    mesh.MergeFaces(he);
                }
            }
        }
        /// <summary>
        ///
        /// </summary>
        private static void CatmullClarkSmoothCornerFixed <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var verts = mesh.Vertices;

            int ev0 = verts.Count - mesh.Edges.Count; // index of first edge vertex
            int fv0 = ev0 - mesh.Faces.Count;         // index of first face vertex

            // set old vertices
            for (int i = 0; i < fv0; i++)
            {
                var v = verts[i];
                if (v.IsUnused)
                {
                    continue;
                }

                if (v.IsBoundary)
                {
                    var he0 = v.First;
                    if (he0.IsAtDegree2)
                    {
                        continue;                  // skip corner verts
                    }
                    var he1 = he0.Previous;
                    var p0  = position.Get(verts[(he0.Index >> 1) + ev0]);
                    var p1  = position.Get(verts[(he1.Index >> 1) + ev0]);
                    position.Set(v, position.Get(v) * 0.5 + (p0 + p1) * 0.25);
                }
                else
                {
                    Vec3d fsum = new Vec3d();
                    Vec3d esum = new Vec3d();
                    int   n    = 0;

                    foreach (var he in v.OutgoingHalfedges)
                    {
                        fsum += position.Get(verts[he.Face.Index + fv0]);
                        esum += position.Get(verts[(he.Index >> 1) + ev0]);
                        n++;
                    }

                    double t = 1.0 / n;
                    position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t);
                }
            }
        }
        /// <summary>
        /// If using external buffers to store vertex attributes, the number of vertices in the resulting mesh the equals the sum of the number of vertices and halfedges in the given mesh.
        /// </summary>
        /// <typeparam name="UV"></typeparam>
        /// <typeparam name="UE"></typeparam>
        /// <typeparam name="UF"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="getPosition"></param>
        /// <param name="getScale"></param>
        /// <param name="getCenter"></param>
        /// <param name="setPosition"></param>
        /// <returns></returns>
        public TM CreateFramedDual <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Func <UV, Vec3d> getPosition, Func <UV, double> getScale, Func <UF, Vec3d> getCenter, Action <TV, Vec3d> setPosition)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            // TODO implement
            throw new NotImplementedException();

            int ne     = mesh.Edges.Count;
            var result = Create(ne << 3, ne << 4, ne << 3);

            //CreateWeaveGeometry(mesh, result, getPosition, getScale, getNormal, getCenter, setPosition);
            //CreateWeaveTopology(mesh, result);
            return(result);
        }
        /// <summary>
        ///
        /// </summary>
        private void CreateWeaveGeometry <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, TM newMesh, Func <UV, Vec3d> getPosition, Func <UE, double> getScale, Func <UE, Vec3d> getNormal, Func <UF, Vec3d> getCenter, Action <TV, Vec3d> setPosition)
            where UV : HeMesh <UV, UE, UF> .Vertex
            where UE : HeMesh <UV, UE, UF> .Halfedge
            where UF : HeMesh <UV, UE, UF> .Face
        {
            var edges = mesh.Edges;
            int ne    = edges.Count;

            // bulk add new vertices
            newMesh.AddVertices(ne << 3);
            var newVerts = newMesh.Vertices;

            // add vertices (8 per halfedge pair in m0)
            for (int i = 0; i < ne; i++)
            {
                var he0 = edges[i];
                var he1 = he0.Twin;

                var f0 = he0.Face;
                var f1 = he1.Face;

                // scale points to mid point of edge
                Vec3d p0 = getPosition(he0.Start);
                Vec3d p1 = getPosition(he1.Start);

                Vec3d p = (p0 + p1) * 0.5;
                var   t = getScale(he0);

                p0 = Vec3d.Lerp(p, p0, t);
                p1 = Vec3d.Lerp(p, p1, t);
                Vec3d p2 = (f0 == null) ? new Vec3d() : Vec3d.Lerp(p, getCenter(f0), t);
                Vec3d p3 = (f1 == null) ? new Vec3d() : Vec3d.Lerp(p, getCenter(f1), t);

                // set vertex positions
                Vec3d d = he0.IsBoundary ? Vec3d.Zero : getNormal(he0);
                int   j = i << 3;

                setPosition(newVerts[j], p0 - d);
                setPosition(newVerts[j + 1], p2 - d);
                setPosition(newVerts[j + 2], p0 + d);
                setPosition(newVerts[j + 3], p2 + d);

                setPosition(newVerts[j + 4], p1 - d);
                setPosition(newVerts[j + 5], p3 - d);
                setPosition(newVerts[j + 6], p1 + d);
                setPosition(newVerts[j + 7], p3 + d);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <typeparam name="E"></typeparam>
        /// <typeparam name="F"></typeparam>
        /// <param name="mesh"></param>
        /// <param name="position"></param>
        /// <param name="boundaryType"></param>
        private static void CatmullClarkGeometry <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, SmoothBoundaryType boundaryType)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            var verts = mesh.Vertices;
            int fv0   = verts.Count; // index of first face vertex

            // create face vertices
            foreach (var f in mesh.Faces)
            {
                var v = mesh.AddVertex();

                if (!f.IsUnused)
                {
                    position.Set(v, f.Vertices.Mean(position.Get));
                }
            }

            // create edge vertices
            foreach (var he0 in mesh.Edges)
            {
                var v = mesh.AddVertex();
                if (he0.IsUnused)
                {
                    continue;
                }

                if (he0.IsBoundary)
                {
                    var p = (position.Get(he0.Start) + position.Get(he0.End)) * 0.5;
                    position.Set(v, p);
                    continue;
                }

                var he1 = he0.Twin;
                var p0  = position.Get(he0.Start);
                var p1  = position.Get(he1.Start);
                var p2  = position.Get(verts[he0.Face.Index + fv0]);
                var p3  = position.Get(verts[he1.Face.Index + fv0]);
                position.Set(v, (p0 + p1 + p2 + p3) * 0.25);
            }

            // smooth old vertices
            CatmullClarkSmooth(mesh, position, boundaryType);
        }