コード例 #1
0
        /// <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, Vector3d> 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);
                }
            }
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
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);
 }
コード例 #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="getPosition"></param>
 /// <param name="setUnrolledPosition"></param>
 /// <param name="getUnrollFactor"></param>
 public static void Unroll <V, E, F>(HeMesh <V, E, F> mesh, F start, Func <V, Vector3d> getPosition, Action <V, Vector3d> 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);
 }
コード例 #5
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);
                }
            }
コード例 #6
0
        /// <summary>
        ///
        /// </summary>
        private static void CatmullClarkSmoothFixed <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vector3d> 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 Vector3d();
                var esum = new Vector3d();
                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);
            }
        }
コード例 #7
0
ファイル: Selection.cs プロジェクト: lymanzhang/SpatialSlur
        /// <summary>
        /// Returns all quad strips in the given mesh.
        /// </summary>
        public static IEnumerable <QuadStrip <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 <QuadStrip <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);
                }
            }
        }
コード例 #8
0
ファイル: Selection.cs プロジェクト: lymanzhang/SpatialSlur
 /// <summary>
 /// Returns a quad strip that includes the given halfedge.
 /// </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 QuadStrip <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));
 }
コード例 #9
0
 /// <summary>
 /// Applies a single iteration of Catmull-Clark subdivision to the given mesh.
 /// If using pre-allocated 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, Vector3d> 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);
 }
コード例 #10
0
 /// <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, Vector3d> 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();
 }
コード例 #11
0
        /// <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, Vector3d> 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);
        }
コード例 #12
0
        /*
         * /// <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


        #region Misc

        /// <summary>
        /// If using pre-allocated 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, Vector3d> 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);
                }
            }
        }
コード例 #13
0
        /// <summary>
        ///
        /// </summary>
        private static void CatmullClarkSmoothCornerFixed <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vector3d> 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
                {
                    Vector3d fsum = new Vector3d();
                    Vector3d esum = new Vector3d();
                    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);
                }
            }
        }
コード例 #14
0
        /// <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, Vector3d> 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);
        }
コード例 #15
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <typeparam name="E"></typeparam>
        /// <typeparam name="F"></typeparam>
        /// <param name="mesh"></param>
        private static void QuadSplitTopology <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
        {
            var verts  = mesh.Vertices;
            var hedges = mesh.Halfedges;
            var faces  = mesh.Faces;

            int ne = hedges.Count >> 1;
            int nf = faces.Count;

            int ev0 = verts.Count - ne; // index of first edge vertex
            int fv0 = ev0 - nf;         // index of first face vertex (also the number of vertices in the initial mesh)

            // split edges
            for (int i = 0; i < ne; i++)
            {
                var he = hedges[i << 1];
                if (he.IsUnused)
                {
                    continue;
                }

                var ev = verts[i + ev0];
                mesh.SplitEdgeImpl(he, ev);
            }

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

                var he = f.First;
                if (he.Start.Index >= fv0)
                {
                    he = he.Previous;                        // ensure halfedge starts from an old vertex
                }
                mesh.QuadSplitFace(he, verts[i + fv0]);
            }
        }
コード例 #16
0
        /// <summary>
        ///
        /// </summary>
        private static void CatmullClarkSmooth <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vector3d> position, SmoothBoundaryType boundaryType)
            where V : HeMesh <V, E, F> .Vertex
            where E : HeMesh <V, E, F> .Halfedge
            where F : HeMesh <V, E, F> .Face
        {
            switch (boundaryType)
            {
            case SmoothBoundaryType.Fixed:
                CatmullClarkSmoothFixed(mesh, position);
                return;

            case SmoothBoundaryType.CornerFixed:
                CatmullClarkSmoothCornerFixed(mesh, position);
                return;

            case SmoothBoundaryType.Free:
                CatmullClarkSmoothFree(mesh, position);
                return;
            }

            throw new NotSupportedException();
        }
コード例 #17
0
            /// <summary>
            ///
            /// </summary>
            /// <param name="mesh"></param>
            /// <param name="start"></param>
            /// <param name="getPosition"></param>
            /// <param name="setUnrolledPosition"></param>
            /// <param name="getUnrollFactor"></param>
            public static void Unroll(HeMesh <V, E, F> mesh, F start, Func <V, Vector3d> getPosition, Action <V, Vector3d> setUnrolledPosition, Func <E, double> getUnrollFactor = null)
            {
                const string errorMessage = "Face cycle detected. The given mesh cannot be unrolled.";

                start.UnusedCheck();
                mesh.Faces.OwnsCheck(start);

                // stack of edges with corresponding transforms
                var queue = new Queue <(E, Orient3d)>();
                var tag   = mesh.Faces.NextTag;

                // current unroll transform
                var unroll = Orient3d.Identity;

                // enqueue twin of each halfedge in the first face
                foreach (var he in start.Halfedges)
                {
                    var v = he.Start;
                    setUnrolledPosition(v, getPosition(v));
                    TryEnqueue(he.Twin);
                }

                // breadth-first walk and transform
                while (queue.Count > 0)
                {
                    (var he0, var m0) = queue.Dequeue();

                    var m1 = getUnrollFactor == null?
                             GetHalfedgeTransform(he0, getPosition) :
                                 GetHalfedgeTransform(he0, getPosition, getUnrollFactor(he0));

                    unroll = m0.Apply(ref m1);
                    var he1 = he0.Next;

                    // enqueue first hedge
                    TryEnqueue(he1.Twin);
                    he1 = he1.Next;

                    // transform and enqueue remaining hedges
                    do
                    {
                        var v = he1.Start;
                        setUnrolledPosition(v, unroll.Apply(getPosition(v)));

                        TryEnqueue(he1.Twin);
                        he1 = he1.Next;
                    } while (he1 != he0);
                }

                void TryEnqueue(E hedge)
                {
                    var f = hedge.Face;

                    if (f == null)
                    {
                        return;
                    }

                    if (f.Tag == tag)
                    {
                        throw new ArgumentException(errorMessage);
                    }

                    queue.Enqueue((hedge, unroll));
                    f.Tag = tag;
                }
            }
コード例 #18
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="mesh"></param>
 public void AppendFaceTopology(HeMesh <HeMesh3d.Vertex, HeMesh3d.Halfedge, HeMesh3d.Face> mesh)
 {
     AppendFaceTopology(mesh, Set, Set);
 }