示例#1
0
 public static bool EdgeSwap(TriMesh.Edge edge)
 {
     if (edge.OnBoundary)
     {
         return(false);
     }
     //逆时针90度,左右变为下上
     TriMesh.HalfEdge hf1         = edge.HalfEdge0;
     TriMesh.HalfEdge hf2         = edge.HalfEdge1;
     TriMesh.Vertex   top         = hf1.ToVertex;
     TriMesh.Vertex   buttom      = hf2.ToVertex;
     TriMesh.HalfEdge topLeft     = hf1.Next;
     TriMesh.HalfEdge buttomLeft  = hf1.Previous;
     TriMesh.HalfEdge topRight    = hf2.Previous;
     TriMesh.HalfEdge buttomRight = hf2.Next;
     top.HalfEdge      = topLeft;
     buttom.HalfEdge   = buttomRight;
     hf1.ToVertex      = topLeft.ToVertex;
     hf2.ToVertex      = buttomRight.ToVertex;
     hf1.Face.HalfEdge = hf1;
     hf2.Face.HalfEdge = hf2;
     topLeft.Face      = hf2.Face;
     buttomRight.Face  = hf1.Face;
     ConnectHalfEdge(topLeft, hf2, topRight);
     ConnectHalfEdge(buttomRight, hf1, buttomLeft);
     return(true);
 }
示例#2
0
 protected override void BeforeMerge(TriMesh.Edge edge)
 {
     base.BeforeMerge(edge);
     TriMesh.Vertex v1 = edge.Vertex0;
     TriMesh.Vertex v2 = edge.Vertex1;
     vertexMatrix[v1] += vertexMatrix[v2];
 }
示例#3
0
        TriMesh.Edge CreateNewEdge(TriMesh.Vertex from, TriMesh.Vertex to)
        {
            TriMesh mesh = (TriMesh)from.Mesh;

            // Create new edge
            TriMesh.Edge edge = new TriMesh.Edge();
            mesh.AppendToEdgeList(edge);
            // Create new halfedges
            TriMesh.HalfEdge hf0 = new TriMesh.HalfEdge();
            mesh.AppendToHalfedgeList(hf0);
            hf0.Opposite = new TriMesh.HalfEdge();
            mesh.AppendToHalfedgeList(hf0.Opposite);
            // Connect opposite halfedge to inner halfedge
            hf0.Opposite.Opposite = hf0;
            // Connect edge to halfedges
            edge.HalfEdge0 = hf0;
            // Connect halfedges to edge
            hf0.Edge          = edge;
            hf0.Opposite.Edge = edge;
            // Connect halfedges to vertices
            hf0.ToVertex          = to;
            hf0.Opposite.ToVertex = from;
            // Connect vertex to outgoing halfedge if it doesn't have one yet
            if (from.HalfEdge == null)
            {
                from.HalfEdge = hf0;
            }
            return(edge);
        }
示例#4
0
 public bool Forward()
 {
     if (this.index < this.method.Logs.Count)
     {
         EdgeContext    ctx   = this.method.Logs[this.index];
         TriMesh.Vertex left  = null;
         TriMesh.Vertex right = null;
         foreach (var v in this.Mesh.Vertices)
         {
             if (v.Index == ctx.Left)
             {
                 left = v;
             }
             else if (v.Index == ctx.Right)
             {
                 right = v;
             }
         }
         TriMesh.Edge edge = left.FindEdgeTo(right);
         MergeEdge.Merge(edge, ctx.MidPos);
         index++;
         return(true);
     }
     else
     {
         int r = this.method.Run(this.Mesh.Faces.Count - 1);
         index += r;
         return(false);
     }
 }
示例#5
0
        /// <summary>
        /// 两端点都在边界上
        /// </summary>
        /// <param name="edge"></param>
        void CutBothBoundary(TriMesh.Edge edge)
        {
            TriMesh.HalfEdge   hf       = edge.HalfEdge0;
            TriMesh.HalfEdge[] leftArr  = this.GetToBoundaryAntiClockWise(hf);
            TriMesh.HalfEdge[] rightArr = this.GetToBoundaryClockWise(hf.Next);


            for (int i = 1; i < leftArr.Length; i++)
            {
                TriMeshModify.RemoveEdge(leftArr[i].Edge);
            }

            for (int i = 0; i < rightArr.Length; i++)
            {
                TriMeshModify.RemoveEdge(rightArr[i].Edge);
            }

            Vector3D vec = this.GetMoveVector(hf.Opposite);

            TriMesh.Vertex left  = this.Clone(hf.FromVertex, vec);
            TriMesh.Vertex right = this.Clone(hf.ToVertex, vec);

            for (int i = 2; i < leftArr.Length; i++)
            {
                this.mesh.Faces.AddTriangles(left, leftArr[i - 1].ToVertex, leftArr[i].ToVertex);
            }
            for (int i = 1; i < rightArr.Length; i++)
            {
                this.mesh.Faces.AddTriangles(right, rightArr[i].ToVertex, rightArr[i - 1].ToVertex);
            }
            this.mesh.Faces.AddTriangles(left, right, leftArr[1].ToVertex);
        }
示例#6
0
        private static void InsertEdge(TriMesh mesh, TriMesh.HalfEdge inner, TriMesh.HalfEdge outer)
        {
            //TriMesh.Edge left = new TriMesh.Edge();
            //left.HalfEdge0 = outer;
            //outer.Edge = left;
            //inner.Next.Edge = left;

            //TriMesh.Edge right = outer.Edge;
            //right.HalfEdge0 = inner;
            //inner.Edge = right;
            //outer.Opposite.Edge = right;

            //inner.Opposite = outer.Opposite;
            //inner.Next.Opposite = outer;
            //outer.Opposite.Opposite = inner;
            //outer.Opposite = inner.Next;
            inner.Opposite          = outer.Opposite;
            inner.Next.Opposite     = outer;
            outer.Opposite.Opposite = inner;
            outer.Opposite          = inner.Next;
            outer.Edge.HalfEdge0    = outer;

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0      = inner;
            inner.Edge          = edge;
            inner.Opposite.Edge = edge;
            inner.Next.Edge     = outer.Edge;

            mesh.AppendToEdgeList(edge);
        }
示例#7
0
        public static List <TriMesh.HalfEdge> RetrieveOneRingEdgeOfEdge(TriMesh.Edge edge)
        {
            TriMesh mesh = (TriMesh)edge.Mesh;
            List <TriMesh.HalfEdge> list = new List <TriMesh.HalfEdge>();

            TriMesh.HalfEdge hf0             = edge.HalfEdge0;
            TriMesh.HalfEdge currentHalfedge = hf0.Next.Opposite;
            while (currentHalfedge.Next.Opposite != hf0)
            {
                if (!currentHalfedge.OnBoundary)
                {
                    list.Add(currentHalfedge.Previous);
                }
                currentHalfedge = currentHalfedge.Next.Opposite;
            }
            ;
            TriMesh.HalfEdge hf1 = edge.HalfEdge1;
            currentHalfedge = hf1.Next.Opposite;
            while (currentHalfedge.Next.Opposite != hf1)
            {
                if (!currentHalfedge.OnBoundary)
                {
                    list.Add(currentHalfedge.Previous);
                }
                currentHalfedge = currentHalfedge.Next.Opposite;
            }
            ;
            return(list);
        }
示例#8
0
        private static void InsertEdge(TriMesh mesh, TriMesh.HalfEdge inner, TriMesh.HalfEdge outer)
        {
            //TriMesh.Edge left = new TriMesh.Edge();
            //left.HalfEdge0 = outer;
            //outer.Edge = left;
            //inner.Next.Edge = left;

            //TriMesh.Edge right = outer.Edge;
            //right.HalfEdge0 = inner;
            //inner.Edge = right;
            //outer.Opposite.Edge = right;

            //inner.Opposite = outer.Opposite;
            //inner.Next.Opposite = outer;
            //outer.Opposite.Opposite = inner;
            //outer.Opposite = inner.Next;
            inner.Opposite = outer.Opposite;
            inner.Next.Opposite = outer;
            outer.Opposite.Opposite = inner;
            outer.Opposite = inner.Next;
            outer.Edge.HalfEdge0 = outer;

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0 = inner;
            inner.Edge = edge;
            inner.Opposite.Edge = edge;
            inner.Next.Edge = outer.Edge;

            mesh.AppendToEdgeList(edge);
        }
示例#9
0
        public static TriMesh.Vertex MergeEdge(TriMesh.Edge edge, Vector3D position)
        {
            TriMesh mesh = (TriMesh)edge.Mesh;

            TriMesh.Vertex   v0  = edge.Vertex0;
            TriMesh.Vertex   v1  = edge.Vertex1;
            TriMesh.HalfEdge hf0 = edge.HalfEdge0;
            TriMesh.HalfEdge hf1 = edge.HalfEdge1;

            v0.Traits.Position = position;

            foreach (var item in v1.HalfEdges)
            {
                //if (item.ToVertex != v0)
                {
                    item.Opposite.ToVertex = v0;
                }
            }

            MergeOneSide(hf0);
            MergeOneSide(hf1);

            mesh.RemoveVertex(v1);
            mesh.RemoveEdge(edge);
            v1.HalfEdge    = null;
            edge.HalfEdge0 = null;

            return(v0);
        }
示例#10
0
        protected Vector3D Transport(Vector3D wI, TriMesh.Face faceI, TriMesh.Face faceJ, double angle)
        {
            /*
             * triangles i and j according to the following labels:
             *
             *              b
             *             /|\
             *            / | \
             *           /  |  \
             *          /   |   \
             *         c  i | j  d
             \   |   /
             \  |  /
             \ | /
             \|/
             \              a
             \
             */

            //Find Shared edge IJ
            TriMesh.HalfEdge sharedEdgeI = null;
            TriMesh.HalfEdge sharedEdgeJ = null;
            TriMesh.Edge     sharedEdge  = null;

            foreach (TriMesh.HalfEdge edgeI in faceI.Halfedges)
            {
                foreach (TriMesh.HalfEdge edgeJ in faceJ.Halfedges)
                {
                    if (edgeI.Opposite == edgeJ)
                    {
                        sharedEdge  = edgeI.Edge;
                        sharedEdgeI = edgeI;
                        sharedEdgeJ = edgeJ;
                        break;
                    }
                }
            }

            if (sharedEdge == null)
            {
                throw new Exception("Error");
            }

            //Find vertex correspondent to figure above
            Vector3D av = sharedEdgeI.FromVertex.Traits.Position;
            Vector3D bv = sharedEdgeJ.FromVertex.Traits.Position;
            Vector3D cv = sharedEdgeI.Next.ToVertex.Traits.Position;
            Vector3D dv = sharedEdgeJ.Next.ToVertex.Traits.Position;

            //Compute the basis
            Matrix3D Ei = Orthogonalize(bv - av, cv - av);
            Matrix3D Ej = Orthogonalize(bv - av, bv - dv);

            //Build Rotate Matrix between two Faces
            Matrix3D rotateMatrix = Matrix3D.Rotate(angle);

            Vector3D wj = (Ej * rotateMatrix * Ei.Inverse() * wI);

            return(wj);
        }
示例#11
0
 public static void ShowOneRingVertexOfEdge(TriMesh.Edge edge)
 {
     foreach (var item in  RetrieveOneRingVertexOfEdge(edge))
     {
         item.Traits.SelectedFlag = 8;
         item.Traits.Color        = RetrieveResult.Instance.VertexResult;
     }
 }
示例#12
0
        private static void MergeOneSide(TriMesh.HalfEdge hf)
        {
            TriMesh mesh = (TriMesh)hf.Mesh;

            if (hf.OnBoundary)
            {
                hf.ToVertex.HalfEdge = hf.Next;
                hf.Previous.Next     = hf.Next;
                hf.Next.Previous     = hf.Previous;
            }
            else
            {
                TriMesh.Edge     remain     = hf.Next.Edge;
                TriMesh.Edge     remove     = hf.Previous.Edge;
                TriMesh.HalfEdge outerLeft  = hf.Previous.Opposite;
                TriMesh.HalfEdge outerRight = hf.Next.Opposite;

                if (outerLeft.Next == outerRight || outerLeft.Previous == outerRight)
                {
                    outerLeft.Edge.Traits.SelectedFlag     = 1;
                    hf.FromVertex.Traits.SelectedFlag      = 1;
                    outerLeft.ToVertex.Traits.SelectedFlag = 1;
                    //throw new Exception();
                }

                remain.HalfEdge0 = outerRight;
                outerLeft.Edge   = remain;

                outerLeft.Opposite  = outerRight;
                outerRight.Opposite = outerLeft;

                hf.ToVertex.HalfEdge = outerRight.Next;
                //hf.ToVertex.HalfEdge = outerLeft;
                TriMesh.Vertex top = hf.Next.ToVertex;
                top.HalfEdge = outerRight;

                mesh.RemoveFace(hf.Face);
                mesh.RemoveHalfedge(hf.Previous);
                mesh.RemoveHalfedge(hf.Next);
                mesh.RemoveEdge(remove);

                hf.Previous.Previous = null;
                hf.Previous.Next     = null;
                hf.Previous.Opposite = null;
                hf.Next.Previous     = null;
                hf.Next.Next         = null;
                hf.Next.Opposite     = null;
                remove.HalfEdge0     = null;
            }
            mesh.RemoveHalfedge(hf);
            hf.ToVertex = null;
            hf.Next     = null;
            hf.Previous = null;
            hf.Opposite = null;
            hf.Face     = null;
        }
示例#13
0
        public static List <TriMesh.Vertex> RetrieveOneRingVertexOfEdge(TriMesh.Edge edge)
        {
            List <TriMesh.HalfEdge> oneRingEdges = RetrieveOneRingEdgeOfEdge(edge);

            List <TriMesh.Vertex> oneRingVertice = new List <TriMesh.Vertex>();

            for (int i = 0; i < oneRingEdges.Count; i++)
            {
                oneRingVertice.Add(oneRingEdges[i].ToVertex);
            }
            return(oneRingVertice);
        }
示例#14
0
 public bool EdgeCollapse(TriMesh.Edge edge)
 {
     if (this.EdgeCanCollapse(edge) && TriMeshModify.IsMergeable(edge))
     {
         TriMeshModify.MergeEdge(edge);
         return(true);
     }
     else
     {
         return(false);
     }
 }
示例#15
0
        protected override double GetValue(TriMesh.Edge edge)
        {
            Vector3D n0    = this.traits.VertexAreaWeightNormal[edge.Vertex0.Index];
            Vector3D n1    = this.traits.VertexAreaWeightNormal[edge.Vertex1.Index];
            double   angle = Math.Acos(n0.Dot(n1));

            if (angle < 0)
            {
                throw new Exception();
            }
            return(angle * this.traits.EdgeLength[edge.Index]);
        }
示例#16
0
 public bool EdgeFlip(TriMesh.Edge edge)
 {
     if (this.EdgeCanFlip(edge))
     {
         TriMeshModify.EdgeSwap(edge);
         return(true);
     }
     else
     {
         return(false);
     }
 }
示例#17
0
        public static bool Contains(TriMesh.Edge edge, Vector3D v)
        {
            Vector3D from   = edge.Vertex0.Traits.Position;
            Vector3D to     = edge.Vertex1.Traits.Position;
            Vector3D dir    = (from - to).Normalize();
            double   length = (from - to).Length();

            return(((v - to).Normalize() - dir).Length() < 0.0001f);
            //return (v - to).Normalize() == dir &&
            //    (v - from).Length() < length &&
            //    (v - to).Length() < length;
        }
示例#18
0
        public static void RemoveEdge(TriMesh.Edge edge)
        {
            TriMesh mesh = (TriMesh)edge.Mesh;

            if (edge.HalfEdge0.Next == edge.HalfEdge1)
            {
                //TriMesh.HalfEdge hf = edge.Vertex0.HalfEdge;
                //hf.Previous.Next = hf.Opposite.Next;
                //hf.Next.Previous = hf.Previous;
                //edge.Vertex1.HalfEdge = hf.Previous;
                edge.HalfEdge0.Previous.Next = edge.HalfEdge1.Next;
                edge.HalfEdge1.Next.Previous = edge.HalfEdge0.Previous;
                edge.Vertex1.HalfEdge        = edge.HalfEdge0.Previous;
                edge.Vertex0.HalfEdge        = null;
            }
            else if (edge.HalfEdge1.Next == edge.HalfEdge0)
            {
                //TriMesh.HalfEdge hf = edge.Vertex1.HalfEdge;
                //hf.Previous.Next = hf.Opposite.Next;
                //hf.Next.Previous = hf.Previous;
                //edge.Vertex0.HalfEdge = hf.Previous;
                edge.HalfEdge1.Previous.Next = edge.HalfEdge0.Next;
                edge.HalfEdge0.Next.Previous = edge.HalfEdge1.Previous;
                edge.Vertex0.HalfEdge        = edge.HalfEdge1.Previous;
                edge.Vertex1.HalfEdge        = null;
            }
            else
            {
                edge.HalfEdge0.Next.Previous     = edge.HalfEdge1.Previous;
                edge.HalfEdge0.Previous.Next     = edge.HalfEdge1.Next;
                edge.HalfEdge1.Next.Previous     = edge.HalfEdge0.Previous;
                edge.HalfEdge1.Previous.Next     = edge.HalfEdge0.Next;
                edge.HalfEdge0.ToVertex.HalfEdge = edge.HalfEdge0.Next;
                edge.HalfEdge1.ToVertex.HalfEdge = edge.HalfEdge1.Next;
                edge.HalfEdge0.Next.Face         = null;
                edge.HalfEdge0.Previous.Face     = null;
                edge.HalfEdge1.Next.Face         = null;
                edge.HalfEdge1.Previous.Face     = null;
                if (edge.Face0 != null)
                {
                    mesh.RemoveFace(edge.Face0);
                }
                if (edge.Face1 != null)
                {
                    mesh.RemoveFace(edge.Face1);
                }
            }

            mesh.RemoveHalfedge(edge.HalfEdge1);
            mesh.RemoveHalfedge(edge.HalfEdge0);
            mesh.RemoveEdge(edge);
        }
示例#19
0
        public bool EdgeCanCollapse(TriMesh.Edge edge)
        {
            if (edge.Vertex0.OnBoundary || edge.Vertex1.OnBoundary)
            {
                return(false);
            }
            int left   = edge.Vertex1.HalfEdgeCount;
            int right  = edge.Vertex0.HalfEdgeCount;
            int top    = edge.HalfEdge0.Next.ToVertex.HalfEdgeCount;
            int bottom = edge.HalfEdge1.Next.ToVertex.HalfEdgeCount;

            return(left + right < 12 && top > 5 && bottom > 5);
        }
示例#20
0
        protected virtual void SelectByPoint()
        {
            int minIndex = SelectVertexByPoint(false);

            if (curVertex != -1)
            {
                TriMesh.Edge edge = TriMeshUtil.FindEdge(mesh.Vertices[curVertex], mesh.Vertices[minIndex]);
                if (edge != null)
                {
                    edge.Traits.SelectedFlag = 1;
                }
            }
            curVertex = minIndex;
        }
示例#21
0
        public bool EdgeCanFlip(TriMesh.Edge edge)
        {
            if (edge.OnBoundary)
            {
                return(false);
            }
            int    left   = edge.Vertex1.HalfEdgeCount;
            int    right  = edge.Vertex0.HalfEdgeCount;
            int    top    = edge.HalfEdge0.Next.ToVertex.HalfEdgeCount;
            int    bottom = edge.HalfEdge1.Next.ToVertex.HalfEdgeCount;
            double dot    = this.GetNormal(edge.Face0).Dot(this.GetNormal(edge.Face1));

            return(left > 5 && right > 5 && top < 7 && bottom < 7 && dot > 0.95);
        }
示例#22
0
        public void ClusterVertexs(TriMesh.Vertex[] vertices,
                                   Vector3D position)
        {
            Queue <TriMesh.Edge> connectedEdge =
                new Queue <TriMesh.Edge>();
            Dictionary <TriMesh.Edge, bool> processedFlag =
                new Dictionary <HalfEdgeMesh.Edge, bool>();

            foreach (TriMesh.Vertex v in vertices)
            {
                foreach (TriMesh.HalfEdge hf in v.HalfEdges)
                {
                    foreach (TriMesh.Vertex vx in vertices)
                    {
                        if (hf.ToVertex == vx &&
                            !processedFlag.ContainsKey(hf.Edge))
                        {
                            connectedEdge.Enqueue(hf.Edge);
                            processedFlag[hf.Edge] = true;
                        }
                    }
                }
            }
            int n = 0;

            while (connectedEdge.Count > 0)
            {
                TriMesh.Edge e = connectedEdge.Dequeue();
                if (e.HalfEdge0 == null || e.HalfEdge1 == null)
                {
                    continue;
                }
                if (TriMeshModify.IsMergeable(e))
                {
                    TriMeshModify.MergeEdge(e, position);
                    n = 0;
                }
                else
                {
                    connectedEdge.Enqueue(e);
                    n++;
                    if (n > connectedEdge.Count)
                    {
                        break;
                    }
                    ;
                }
            }
        }
示例#23
0
 public static TriMesh.Vertex MergeTriangle(TriMesh.Face face, Vector3D pos)
 {
     TriMesh.Edge   edge1 = face.HalfEdge.Edge;
     TriMesh.Edge   edge2 = face.HalfEdge.Next.Edge;
     TriMesh.Vertex v     = MergeEdge(edge1, pos);
     if (IsMergeable(edge2))
     {
         v = MergeEdge(edge2, pos);
     }
     else
     {
         edge2.Traits.SelectedFlag = 1;
     }
     return(v);
 }
示例#24
0
        /// <summary>
        /// Adds a face to the mesh with the specified face traits.
        /// </summary>
        /// <param name="faceVertices">The vertices of the face in counterclockwise order.</param>
        /// <returns>The face created by this method.</returns>
        /// <exception cref="BadTopologyException">
        /// Thrown when fewer than three vertices are given or the vertices cannot form a valid face.
        /// </exception>
        /// <exception cref="ArgumentNullException">Thrown when a null vertex is given.</exception>
        public TriMesh.Face CreateFace(params TriMesh.Vertex[] faceVertices)
        {
            int n = faceVertices.Length;

            // Require at least 3 vertices
            if (n < 3)
            {
                throw new BadTopologyException("Cannot create a polygon with fewer than three vertices.");
            }
            TriMesh mesh = (TriMesh)faceVertices[0].Mesh;

            TriMesh.HalfEdge[] faceHalfedges = new TriMesh.HalfEdge[n];
            bool[]             isUsedVertex  = new bool[n];

            // Make sure input is (mostly) acceptable before making any changes to the mesh
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;
                faceHalfedges[i] = this.Validate(faceVertices[i], faceVertices[j]);
                isUsedVertex[i]  = (faceVertices[i].HalfEdge != null);
            }
            // Create face
            TriMesh.Face f = new TriMesh.Face(default(FaceTraits));
            mesh.AppendToFaceList(f);
            // Create new edges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;
                if (faceHalfedges[i] == null)
                {
                    TriMesh.Edge newEdge = this.CreateNewEdge(faceVertices[i], faceVertices[j]);
                    faceHalfedges[i] = newEdge.HalfEdge0;
                }
                faceHalfedges[i].Face = f;
            }

            // Connect next/previous halfedges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;
                this.ConnectHalfedge(faceHalfedges[i], faceHalfedges[j], isUsedVertex[j]);
            }
            // Connect face to an inner halfedge
            f.HalfEdge = faceHalfedges[0];
            return(f);
        }
示例#25
0
        public static double ComputeDihedralAngle(TriMesh.Edge edge)
        {
            TriMesh.Face f1 = edge.Face0;
            TriMesh.Face f2 = edge.Face1;
            if (f1 != null && f2 != null)
            {
                Vector3D f1Normal = ComputeNormalFace(f1).Normalize();
                Vector3D f2Normal = ComputeNormalFace(f2).Normalize();
                double   cosa     = f1Normal.Dot(f2Normal)
                                    / (f1Normal.Length() * f2Normal.Length());

                return(Math.Acos(cosa));
            }
            else
            {
                return(0);
            }
        }
示例#26
0
        public static Nullable <Vector3D> Intersect(Plane plane, TriMesh.Edge edge, double threshold)
        {
            Vector3D p1 = edge.Vertex0.Traits.Position;
            Vector3D p2 = edge.Vertex1.Traits.Position;
            double   d1 = GetDistance(plane, p1);
            double   d2 = GetDistance(plane, p2);

            if (Math.Abs(d1) > threshold && Math.Abs(d2) > threshold && d1 * d2 < 0)
            {
                d1 = Math.Abs(d1);
                d2 = Math.Abs(d2);
                return(p1 + (p2 - p1) * d1 / (d1 + d2));
            }
            else
            {
                return(null);
            }
        }
示例#27
0
        public static EdgeContext Merge(TriMesh.Edge edge, Vector3D position)
        {
            TriMesh mesh = (TriMesh)edge.Mesh;

            EdgeContext context = new EdgeContext()
            {
                LeftPos  = edge.Vertex0.Traits.Position,
                MidPos   = position,
                RightPos = edge.Vertex1.Traits.Position,
                Left     = edge.Vertex0.Index,
                Right    = edge.Vertex1.Index,
                Top      = edge.HalfEdge1.Next.ToVertex.Index,
                Bottom   = edge.HalfEdge0.Next.ToVertex.Index,
            };

            TriMeshModify.MergeEdge(edge);

            return(context);
        }
示例#28
0
 /// <summary>
 /// cut后部分边的index改变,src是按照cut前边的index顺序,dst是按照cut后的
 /// </summary>
 /// <param name="src"></param>
 /// <returns></returns>
 public double[] MapEdgeValue(double[] src)
 {
     double[] dst = new double[this.mesh.Edges.Count];
     for (int i = 0; i < this.EdgeStore.Length; i++)
     {
         VertexPair   pair  = this.EdgeStore[i];
         TriMesh.Edge edge  = pair.Src.FindEdgeTo(pair.Dst);
         TriMesh.Edge other = this.GetOtherEdge(pair.Src, pair.Dst);
         if (edge != null)
         {
             dst[edge.Index] = src[i];
         }
         if (other != null)
         {
             dst[other.Index] = src[i];
         }
     }
     return(dst);
 }
示例#29
0
        private void InsertEdge(TriMesh mesh, TriMesh.Edge edge, TriMesh.HalfEdge inner, TriMesh.HalfEdge outer)
        {
            TriMesh.Edge left = edge;
            left.HalfEdge0  = outer;
            outer.Edge      = left;
            inner.Next.Edge = left;

            TriMesh.Edge right = outer.Edge;
            right.HalfEdge0     = inner;
            inner.Edge          = right;
            outer.Opposite.Edge = right;

            inner.Opposite          = outer.Opposite;
            inner.Next.Opposite     = outer;
            outer.Opposite.Opposite = inner;
            outer.Opposite          = inner.Next;

            mesh.Add(edge);
        }
示例#30
0
 /// <summary>
 /// 这里有时候有bug,原因不明
 /// </summary>
 /// <param name="v1"></param>
 /// <param name="v2"></param>
 /// <returns></returns>
 public TriMesh.Edge GetOtherEdge(TriMesh.Vertex v1, TriMesh.Vertex v2)
 {
     TriMesh.Vertex[] arr1 = this.GetAllCascade(v1);
     TriMesh.Vertex[] arr2 = this.GetAllCascade(v2);
     foreach (var item1 in arr1)
     {
         foreach (var item2 in arr2)
         {
             if (item1 != v1 || item2 != v2)
             {
                 TriMesh.Edge edge = item1.FindEdgeTo(item2);
                 if (edge != null)
                 {
                     return(edge);
                 }
             }
         }
     }
     return(null);
 }
示例#31
0
        public static TriMesh.Vertex VertexSplit(TriMesh.Vertex v1, TriMesh.Vertex share1,
                                                 TriMesh.Vertex share2, Vector3D v1Position, Vector3D v2Position, int fixedIndex)
        {
            TriMesh.HalfEdge[] hfs = FindGroup(v1, share1, share2);

            TriMesh mesh = (TriMesh)v1.Mesh;

            v1.Traits.Position = v1Position;
            v1.HalfEdge        = hfs[0];

            TriMesh.Vertex v2 = new TriMesh.Vertex();
            v2.Traits            = new VertexTraits(v2Position);
            v2.Traits.FixedIndex = fixedIndex;
            v2.HalfEdge          = hfs[1];
            mesh.AppendToVertexList(v2);

            for (int i = 0; i < hfs.Length - 1; i++)
            {
                hfs[i].Opposite.ToVertex = v2;
            }

            TriMesh.HalfEdge[] triangle1 = AddInnerTriangle(mesh, v1, v2, share1);
            InsertEdge(mesh, triangle1[1], hfs[0]);

            TriMesh.HalfEdge[] triangle2 = AddInnerTriangle(mesh, v2, v1, share2);
            InsertEdge(mesh, triangle2[1], hfs[hfs.Length - 1]);

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0        = triangle1[0];
            triangle1[0].Edge     = edge;
            triangle2[0].Edge     = edge;
            triangle1[0].Opposite = triangle2[0];
            triangle2[0].Opposite = triangle1[0];
            mesh.AppendToEdgeList(edge);

            return(v2);
        }
示例#32
0
        public TriMesh.Vertex VertexSplit1(TriMesh.Vertex v, TriMesh.Vertex vshard1, TriMesh.Vertex vshard2, Vector3D v1Position, Vector3D v2Position, int v2FixedIndex)
        {
            //1.Get two group of verties
            TriMesh.HalfEdge[] processGroup = FindGroup(v, vshard1, vshard2);

            TriMesh mesh = (TriMesh)v.Mesh;
            TriMesh.Vertex v1 = null;
            TriMesh.Vertex v2 = null;
            TriMesh.Vertex newVertex = null;

            v1 = v;
            v.Traits.Position = v1Position;
            v2 = new TriMesh.Vertex();
            v2.Traits = new VertexTraits(Vector3D.Zero);
            newVertex = v2;
            newVertex.Traits.FixedIndex = v2FixedIndex;
            v2.Mesh = v.Mesh;
            v2.Traits.Position = v2Position;

            //2.Process the Topology
            TriMesh.HalfEdge hf1Origin = processGroup[0];
            TriMesh.HalfEdge hf2Origin = processGroup[processGroup.Length - 1];

            //Add new edge
            TriMesh.HalfEdge hf3 = new TriMesh.HalfEdge();
            TriMesh.HalfEdge hf3Oppsite = new TriMesh.HalfEdge();
            TriMesh.Edge edge = new TriMesh.Edge();

            hf3.Opposite = hf3Oppsite;
            hf3Oppsite.Opposite = hf3;

            edge.HalfEdge0 = hf3;
            edge.HalfEdge1 = hf3Oppsite;
            hf3.Edge = edge;
            hf3Oppsite.Edge = edge;

            hf3.ToVertex = v2;
            hf3Oppsite.ToVertex = v1;

            //Handle hf1Origin which is outter hafledge [INNER]
            TriMesh.HalfEdge hf1 = new TriMesh.HalfEdge();
            hf1.Opposite = hf1Origin;
            hf1.ToVertex = v1;

            TriMesh.HalfEdge hf1Other = new TriMesh.HalfEdge();
            hf1Other.Opposite = hf1Origin.Opposite;
            hf1Other.ToVertex = hf1Origin.ToVertex;

            hf1.Previous = hf1Other;
            hf1Other.Next = hf1;

            hf1.Next = hf3;
            hf3.Previous = hf1;
            hf1Other.Previous = hf3;
            hf3.Next = hf1Other;

            //Handle hf2Origin which is inner hafledge [INNER]
            TriMesh.HalfEdge hf2 = new TriMesh.HalfEdge();
            hf2.Opposite = hf2Origin;
            hf2.ToVertex = v2;

            TriMesh.HalfEdge hf2Other = new TriMesh.HalfEdge();
            hf2Other.Opposite = hf2Origin.Opposite;
            hf2Other.ToVertex = hf2Origin.ToVertex;

            hf2.Previous = hf2Other;
            hf2Other.Next = hf2;

            hf2.Next = hf3Oppsite;
            hf3Oppsite.Previous = hf2;
            hf2Other.Previous = hf3Oppsite;
            hf3Oppsite.Next = hf2Other;


            TriMesh.Face face1 = new TriMesh.Face();
            TriMesh.Face face2 = new TriMesh.Face();

            face1.HalfEdge = hf3;
            hf3.Face = face1;
            hf1.Face = face1;
            hf1Other.Face = face1;

            face2.HalfEdge = hf3Oppsite;
            hf3Oppsite.Face = face2;
            hf2.Face = face2;
            hf2Other.Face = face2;

            //Process the outside
            TriMesh.Edge edge1 = new TriMesh.Edge();
            TriMesh.HalfEdge hf1OriginOppsite = hf1Origin.Opposite;

            hf1Origin.Opposite = hf1;
            hf1.Edge = hf1Origin.Edge;

            hf1OriginOppsite.Opposite = hf1Other;
            hf1OriginOppsite.ToVertex = v2;
            hf1OriginOppsite.Edge = edge1;
            hf1Other.Edge = edge1;
            edge1.HalfEdge0 = hf1Other;
            edge1.HalfEdge1 = hf1OriginOppsite;

            TriMesh.Edge edge2 = new TriMesh.Edge();
            TriMesh.HalfEdge hf2OriginOppsite = hf2Origin.Opposite;

            hf2Origin.Opposite = hf2;
            hf2.Edge = hf2Origin.Edge;

            hf2OriginOppsite.Opposite = hf2Other;
            hf2OriginOppsite.ToVertex = v1;
            hf2OriginOppsite.Edge = edge2;
            hf2Other.Edge = edge2;
            edge2.HalfEdge0 = hf2Other;
            edge2.HalfEdge1 = hf2OriginOppsite;

            v1.HalfEdge = hf1Origin;
            v2.HalfEdge = hf2Origin;

            mesh.AppendToEdgeList(edge);
            mesh.AppendToEdgeList(edge1);
            mesh.AppendToEdgeList(edge2);
            mesh.AppendToFaceList(face1);
            mesh.AppendToFaceList(face2);
            mesh.AppendToHalfedgeList(hf1);
            mesh.AppendToHalfedgeList(hf1Other);
            mesh.AppendToHalfedgeList(hf2);
            mesh.AppendToHalfedgeList(hf2Other);
            mesh.AppendToHalfedgeList(hf3);
            mesh.AppendToHalfedgeList(hf3Oppsite);
            mesh.AppendToVertexList(newVertex);

            for (int i = 1; i < processGroup.Length - 1; i++)
            {
                processGroup[i].Opposite.ToVertex = newVertex;
            }


            //mesh.FixIndex();

            return newVertex;
        }
示例#33
0
        public static TriMesh.Vertex VertexSplit(TriMesh.Vertex v1, TriMesh.Vertex share1,
            TriMesh.Vertex share2, Vector3D v1Position, Vector3D v2Position, int fixedIndex)
        {
            TriMesh.HalfEdge[] hfs = FindGroup(v1, share1, share2);

            TriMesh mesh = (TriMesh)v1.Mesh;

            v1.Traits.Position = v1Position;
            v1.HalfEdge = hfs[0];

            TriMesh.Vertex v2 = new TriMesh.Vertex();
            v2.Traits = new VertexTraits(v2Position);
            v2.Traits.FixedIndex = fixedIndex;
            v2.HalfEdge = hfs[1];
            mesh.AppendToVertexList(v2);

            for (int i = 0; i < hfs.Length - 1; i++)
            {
                hfs[i].Opposite.ToVertex = v2;
            }

            TriMesh.HalfEdge[] triangle1 = AddInnerTriangle(mesh, v1, v2, share1);
            InsertEdge(mesh, triangle1[1], hfs[0]);

            TriMesh.HalfEdge[] triangle2 = AddInnerTriangle(mesh, v2, v1, share2);
            InsertEdge(mesh, triangle2[1], hfs[hfs.Length - 1]);

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0 = triangle1[0];
            triangle1[0].Edge = edge;
            triangle2[0].Edge = edge;
            triangle1[0].Opposite = triangle2[0];
            triangle2[0].Opposite = triangle1[0];
            mesh.AppendToEdgeList(edge);

            return v2;
        }
示例#34
0
        public void MakeTheTopologyB(PolygonMesh dualMesh)
        {
            List<TriMesh.Vertex> boundaryVertices = new List<TriMesh.Vertex>();

            boundaryVertices = TriMeshUtil.RetrieveAllBoundaryVertex(mesh);
            foreach (TriMesh.Face face in mesh.Faces)
            {
                int index = face.Index;
                int baryVertexIndex = mesh.Edges.Count;
                int edgeVertexIndex;
                int faceIndex = index;
                TriMesh.Edge boundaryEdge = new TriMesh.Edge();
                TriMesh.HalfEdge tempHalfedge = new TriMesh.HalfEdge();
                TriMesh.Vertex vertex0 = mesh.Faces[index].GetVertex(0);
                TriMesh.Vertex vertex1 = mesh.Faces[index].GetVertex(1);
                TriMesh.Vertex vertex2 = mesh.Faces[index].GetVertex(2);
                TriMesh.HalfEdge halfedge0 = vertex0.FindHalfedgeTo(vertex1);
                TriMesh.HalfEdge halfedge1 = vertex1.FindHalfedgeTo(vertex2);
                TriMesh.HalfEdge halfedge2 = vertex2.FindHalfedgeTo(vertex0);
                TriMesh.HalfEdge boundaryHalfedge = new TriMesh.HalfEdge();
                tempHalfedge = halfedge0;
                
                edgeVertexIndex = halfedge0.Edge.Index;
                TriMesh.Vertex firstVertex = dualMesh.Vertices[baryVertexIndex + faceIndex];
                TriMesh.Vertex secondVertex = dualMesh.Vertices[edgeVertexIndex];
                if (firstVertex.FindHalfedgeTo(secondVertex) == null)
                {
                    List<TriMesh.Vertex> ringVertices = new List<TriMesh.Vertex>();
                    do
                    {
                        ringVertices.Add(dualMesh.Vertices[baryVertexIndex + faceIndex]);
                        ringVertices.Add(dualMesh.Vertices[edgeVertexIndex]);
                        if (tempHalfedge.Opposite.Next.Face != null)
                        {
                            tempHalfedge = tempHalfedge.Opposite.Next;
                            faceIndex = tempHalfedge.Face.Index;
                            edgeVertexIndex = tempHalfedge.Edge.Index;
                        }
                        else
                        {
                            tempHalfedge = tempHalfedge.Opposite.Next;
                            edgeVertexIndex = tempHalfedge.Edge.Index;
                            faceIndex = tempHalfedge.Opposite.Next.Face.Index;
                        }
                    } while (tempHalfedge != halfedge0);
                    TriMesh.Vertex[] newFaceVertices = new TriMesh.Vertex[ringVertices.Count];
                    for (int i = 0; i < ringVertices.Count; i++)
                    {
                        newFaceVertices[i] = ringVertices[i];
                    }
                    dualMesh.Faces.Add(newFaceVertices);
                }
                tempHalfedge = halfedge1;
                edgeVertexIndex = halfedge1.Edge.Index;
                firstVertex = dualMesh.Vertices[baryVertexIndex + faceIndex];
                secondVertex = dualMesh.Vertices[edgeVertexIndex];
                if (firstVertex.FindHalfedgeTo(secondVertex) == null)
                {
                    List<TriMesh.Vertex> ringVertices = new List<TriMesh.Vertex>();
                    do
                    {
                        ringVertices.Add(dualMesh.Vertices[baryVertexIndex + faceIndex]);
                        ringVertices.Add(dualMesh.Vertices[edgeVertexIndex]);
                        if (tempHalfedge.Opposite.Next != null)
                        {
                            tempHalfedge = tempHalfedge.Opposite.Next;
                            faceIndex = tempHalfedge.Face.Index;
                            edgeVertexIndex = tempHalfedge.Edge.Index;
                        }
                        else
                        {
                            continue;
                        }
                    } while (tempHalfedge != halfedge1);
                    TriMesh.Vertex[] newFaceVertices = new TriMesh.Vertex[ringVertices.Count];
                    for (int i = 0; i < ringVertices.Count; i++)
                    {
                        newFaceVertices[i] = ringVertices[i];

                    }
                    dualMesh.Faces.Add(newFaceVertices);
                }
                tempHalfedge = halfedge2;
                edgeVertexIndex = halfedge2.Edge.Index;
                firstVertex = dualMesh.Vertices[baryVertexIndex + faceIndex];
                secondVertex = dualMesh.Vertices[edgeVertexIndex];
                if (firstVertex.FindHalfedgeTo(secondVertex) == null)
                {
                    List<TriMesh.Vertex> ringVertices = new List<TriMesh.Vertex>();
                    do
                    {
                        ringVertices.Add(dualMesh.Vertices[baryVertexIndex + faceIndex]);
                        ringVertices.Add(dualMesh.Vertices[edgeVertexIndex]);
                        if (tempHalfedge.Opposite.Next != null)
                        {
                            tempHalfedge = tempHalfedge.Opposite.Next;
                            faceIndex = tempHalfedge.Face.Index;
                            edgeVertexIndex = tempHalfedge.Edge.Index;
                        }
                        else
                        {
                            continue;
                        }
                    } while (tempHalfedge != halfedge2);
                    TriMesh.Vertex[] newFaceVertices = new TriMesh.Vertex[ringVertices.Count];
                    for (int i = 0; i < ringVertices.Count; i++)
                    {
                        newFaceVertices[i] = ringVertices[i];

                    }
                    dualMesh.Faces.Add(newFaceVertices);
                }
            }
        }
示例#35
0
 TriMesh.Edge CreateNewEdge(TriMesh.Vertex from, TriMesh.Vertex to)
 {
     TriMesh mesh = (TriMesh)from.Mesh;
     // Create new edge
     TriMesh.Edge edge = new TriMesh.Edge();
     mesh.AppendToEdgeList(edge);
     // Create new halfedges
     TriMesh.HalfEdge hf0 = new TriMesh.HalfEdge();
     mesh.AppendToHalfedgeList(hf0);
     hf0.Opposite = new TriMesh.HalfEdge();
     mesh.AppendToHalfedgeList(hf0.Opposite);
     // Connect opposite halfedge to inner halfedge
     hf0.Opposite.Opposite = hf0;
     // Connect edge to halfedges
     edge.HalfEdge0 = hf0;
     // Connect halfedges to edge
     hf0.Edge = edge;
     hf0.Opposite.Edge = edge;
     // Connect halfedges to vertices
     hf0.ToVertex = to;
     hf0.Opposite.ToVertex = from;
     // Connect vertex to outgoing halfedge if it doesn't have one yet
     if (from.HalfEdge == null)
     {
         from.HalfEdge = hf0;
     }
     return edge;
 }
示例#36
0
        /// <summary>
        /// Adds a face to the mesh with the specified face traits.
        /// </summary>
        /// <param name="faceTraits">The custom traits for the face to add to the mesh.</param>
        /// <param name="faceVertices">The vertices of the face in counterclockwise order.</param>
        /// <returns>The face created by this method.</returns>
        /// <exception cref="BadTopologyException">
        /// Thrown when fewer than three vertices are given or the vertices cannot form a valid face.
        /// </exception>
        /// <exception cref="ArgumentNullException">Thrown when a null vertex is given.</exception>
        private static TriMesh.Face CreateFace(TriMesh mesh, params TriMesh.Vertex[] faceVertices)
        {
            int n = faceVertices.Length; 
            // Require at least 3 vertices
            if (n < 3)
            {
                throw new BadTopologyException("Cannot create a polygon with fewer than three vertices.");
            }  
            TriMesh.Edge e;
            TriMesh.Face f;
            TriMesh.HalfEdge[] faceHalfedges = new TriMesh.HalfEdge[n];
            bool[] isNewEdge = new bool[n], isUsedVertex = new bool[n]; 
            for (int i = 0; i < n; i++)
            {
                int j = (i + 1) % n; 
                faceHalfedges[i] = faceVertices[i].FindHalfedgeTo(faceVertices[j]);

            }

            // Make sure input is (mostly) acceptable before making any changes to the mesh
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n; 
                if (faceVertices[i] == null)
                {
                    throw new ArgumentNullException("Can't add a null vertex to a face.");
                }
                if (!faceVertices[i].OnBoundary)
                { 
                    throw new BadTopologyException("Can't add an edge to a vertex on the interior of a mesh.");
                } 
                // Find existing halfedges for this face
                faceHalfedges[i] = faceVertices[i].FindHalfedgeTo(faceVertices[j]);
                isNewEdge[i] = (faceHalfedges[i] == null);
                isUsedVertex[i] = (faceVertices[i].HalfEdge != null); 
                if (!isNewEdge[i] && !faceHalfedges[i].OnBoundary)
                { 
                    throw new BadTopologyException("Can't add more than two faces to an edge.");
                }
            } 
            // Create face
            f = new TriMesh.Face(default(FaceTraits));
            mesh.AppendToFaceList(f); 
            // Create new edges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n; 
                if (isNewEdge[i])
                {
                    // Create new edge
                    e = new TriMesh.Edge();
                    mesh.AppendToEdgeList(e); 
                    // Create new halfedges
                    faceHalfedges[i] = new TriMesh.HalfEdge();
                    mesh.AppendToHalfedgeList(faceHalfedges[i]); 
                    faceHalfedges[i].Opposite = new TriMesh.HalfEdge();
                    mesh.AppendToHalfedgeList(faceHalfedges[i].Opposite); 
                    // Connect opposite halfedge to inner halfedge
                    faceHalfedges[i].Opposite.Opposite = faceHalfedges[i]; 
                    // Connect edge to halfedges
                    e.HalfEdge0 = faceHalfedges[i]; 
                    // Connect halfedges to edge
                    faceHalfedges[i].Edge = e;
                    faceHalfedges[i].Opposite.Edge = e; 
                    // Connect halfedges to vertices
                    faceHalfedges[i].ToVertex = faceVertices[j];
                    faceHalfedges[i].Opposite.ToVertex = faceVertices[i]; 
                    // Connect vertex to outgoing halfedge if it doesn't have one yet
                    if (faceVertices[i].HalfEdge == null)
                    {
                        faceVertices[i].HalfEdge = faceHalfedges[i];
                    }
                } 
                if (faceHalfedges[i].Face != null)
                {
                    throw new BadTopologyException("An inner halfedge already has a face assigned to it.");
                } 
                // Connect inner halfedge to face
                faceHalfedges[i].Face = f; 
            }

            // Connect next/previous halfedges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;

                // Outer halfedges
                if (isNewEdge[i] && isNewEdge[j] && isUsedVertex[j])  // Both edges are new and vertex has faces connected already
                {
                    TriMesh.HalfEdge closeHalfedge = null; 
                    // Find the closing halfedge of the first available opening
                    foreach (TriMesh.HalfEdge h in faceVertices[j].HalfEdges)
                    {
                        if (h.Face == null)
                        {
                            closeHalfedge = h;
                            break;
                        }
                    } 
                    TriMesh.HalfEdge openHalfedge = closeHalfedge.Previous; 
                    // Link new outer halfedges into this opening
                    faceHalfedges[i].Opposite.Previous = openHalfedge;
                    openHalfedge.Next = faceHalfedges[i].Opposite;
                    faceHalfedges[j].Opposite.Next = closeHalfedge;
                    closeHalfedge.Previous = faceHalfedges[j].Opposite;
                }
                else if (isNewEdge[i] && isNewEdge[j])  // Both edges are new
                {
                    faceHalfedges[i].Opposite.Previous = faceHalfedges[j].Opposite;
                    faceHalfedges[j].Opposite.Next = faceHalfedges[i].Opposite;
                }
                else if (isNewEdge[i] && !isNewEdge[j])  // This is new, next is old
                {
                    faceHalfedges[i].Opposite.Previous = faceHalfedges[j].Previous;
                    faceHalfedges[j].Previous.Next = faceHalfedges[i].Opposite;
                }
                else if (!isNewEdge[i] && isNewEdge[j])  // This is old, next is new
                {
                    faceHalfedges[i].Next.Previous = faceHalfedges[j].Opposite;
                    faceHalfedges[j].Opposite.Next = faceHalfedges[i].Next;
                }
                // Relink faces before adding new edges if they are in the way of a new face
                else if (!isNewEdge[i] && !isNewEdge[j] && faceHalfedges[i].Next != faceHalfedges[j]) 
                {
                    TriMesh.HalfEdge closeHalfedge = faceHalfedges[i].Opposite; 
                    // Find the closing halfedge of the opening opposite the opening halfedge i is on
                    do
                    {
                        closeHalfedge = closeHalfedge.Previous.Opposite;
                    } while (closeHalfedge.Face != null && closeHalfedge != faceHalfedges[j] 
                             && closeHalfedge != faceHalfedges[i].Opposite); 
                    if (closeHalfedge == faceHalfedges[j] || closeHalfedge == faceHalfedges[i].Opposite)
                    {
                        throw new BadTopologyException("Unable to find an opening to relink an existing face.");
                    } 
                    TriMesh.HalfEdge openHalfedge = closeHalfedge.Previous; 
                    // Remove group of faces between two openings, close up gap to form one opening
                    openHalfedge.Next = faceHalfedges[i].Next;
                    faceHalfedges[i].Next.Previous = openHalfedge; 
                    // Insert group of faces into target opening
                    faceHalfedges[j].Previous.Next = closeHalfedge;
                    closeHalfedge.Previous = faceHalfedges[j].Previous;
                } 
                // Inner halfedges
                faceHalfedges[i].Next = faceHalfedges[j];
                faceHalfedges[j].Previous = faceHalfedges[i];
            }
            // Connect face to an inner halfedge
            f.HalfEdge = faceHalfedges[0];
            return f;
        }