Ejemplo n.º 1
0
        /// <summary>
        /// Try to merge the two given faces.
        /// For this to work, the edges belonging to both faces must be able
        /// to form a closed loop.
        /// The first face will be grown and the second face removed on success
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns>True on success, False on failure</returns>
        internal static bool MergeFaces(this Kernel kernel, Face a, Face b)
        {
            // try to find the shared edge
            if (!ConnectivityQuery.TryFindSharedEdge(a, b, out var shared))
            {
                return(false);
            }

            // get edge iterator for face a, starting at shared edge
            var firstEdges = new EdgeIterator(shared).ToArray();

            // get edge iterator for face b starting at shared edge
            var otherEdges = new EdgeIterator(shared.Pair).ToArray();

            // remove the shared edge from the kernel
            kernel.Remove(shared);

            // establish circular link between both face halfes
            EdgeLinker.LinkOrderedEdgeCollection(
                firstEdges
                .Skip(1)
                .Concat(otherEdges.Skip(1))
                .ToList());

            // update face references
            otherEdges.Skip(1).Select(e => e.Face = a);

            // remove face b
            b.Start = null;
            kernel.Remove(b);

            return(true);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Insert a new face from a counter-clockwise ordered list of halfedges
        /// </summary>
        /// <param name="edges"></param>
        /// <returns></returns>
        public bool Insert(IReadOnlyList <HalfEdge> edges)
        {
            if (edges.Count < 3)
            {
                return(false);
            }

            // TODO: Check if ccw - ordered
            // establish circular link between edges
            EdgeLinker.LinkOrderedEdgeCollection(edges);

            // assign face starting edge
            var face = new Face {
                Start = edges[0]
            };

            // assign face to edges
            foreach (var edge in edges)
            {
                edge.Face = face;
            }

            // insert the new face
            return(Insert(face));
        }
Ejemplo n.º 3
0
        public override bool Insert(HalfEdge edge)
        {
            // try to link up the edge
            if (!EdgeLinker.TryLinkEdge(edge))
            {
                return(false);
            }

            Add(edge);

            return(true);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Create a new kernel from a list of positions and a nested list of indices representing faces
        /// This has no checks, the given input needs to represent a valid mesh to produce a meaningful output
        /// </summary>
        /// <param name="positions"></param>
        /// <param name="faces"></param>
        /// <returns></returns>
        internal static Kernel CreateFromPositionsUnchecked(IReadOnlyList <Vec3d> positions, IEnumerable <IEnumerable <int> > faces)
        {
            // create new kernel
            var kernel = new Kernel();

            // insert a vertex into the kernel for each position
            var vertices = (from position in positions select kernel.GetVertexForPosition(position)).ToArray();
            var edges    = new List <HalfEdge>();

            // iterate over number of faces
            foreach (var face in faces)
            {
                // empty list to hold halfedges of new face
                var faceEdges = new List <HalfEdge>();

                // get vertex indices and iterate over them
                var indices = face;
                foreach (var vertexIndex in indices)
                {
                    // create a new halfedge originating from the current vertexindex
                    var edge = new HalfEdge
                    {
                        Origin = vertices[vertexIndex],
                    };

                    // assure outgoing edge on current vertex
                    if (vertices[vertexIndex].Outgoing is null)
                    {
                        vertices[vertexIndex].Outgoing = edge;
                    }

                    faceEdges.Add(edge);
                }

                // insert a new face from the face edges
                kernel.Insert(faceEdges);

                edges.AddRange(faceEdges);
            }

            // link all edges to their pairs
            EdgeLinker.LinkEdgePairs(ref edges);

            // iterate over all edges and add to kernel
            foreach (var edge in edges)
            {
                kernel.Insert(edge);
            }

            // return the kernel
            return(kernel);
        }
Ejemplo n.º 5
0
        internal static void CollapseEdge(this Kernel kernel, HalfEdge edge)
        {
            if (EdgeLinker.IsDummyPairEdge(edge))
            {
                edge = edge.Pair;
            }

            // if the edge is naked, we don't have to change the other face
            if (EdgeLinker.IsDummyPairEdge(edge.Pair))
            {
                CollapseNakedEdge(edge, kernel);
                return;
            }

            CollapseEdgeBetweenFaces(edge, kernel);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Removes a face from the kernel, by cutting a hole in the mesh
        /// All Edges belonging to this face will become unlinked, naked edges
        /// </summary>
        /// <param name="face"></param>
        /// <returns></returns>
        public override bool Remove(Face face)
        {
            if (face.Start is null)
            {
                return(_elements.Remove(face));
            }

            // iterate over face edges
            foreach (var edge in new EdgeIterator(face.Start))
            {
                // unlink the current edge
                EdgeLinker.UnlinkEdge(edge);
            }

            // remove face from inner collection
            return(_elements.Remove(face));
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Removes all unused Edges from the kernel
        /// Unused edges are edge-pairs, where both pairs are naked
        /// </summary>
        /// <param name="kernel"></param>
        internal static void RemoveUnusedEdges(this Kernel kernel)
        {
            foreach (var edge in kernel.Edges)
            {
                if (!EdgeLinker.IsDummyPairEdge(edge))
                {
                    continue;
                }

                if (!EdgeLinker.IsDummyPairEdge(edge.Pair))
                {
                    continue;
                }

                kernel.Remove(edge);
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Removes a <see cref="HalfEdge"/>. This also removes its pair,
        /// as HalfEdges are not allowed to be single ;)
        /// </summary>
        /// <param name="edge"></param>
        /// <returns></returns>
        public override bool Remove(HalfEdge edge)
        {
            // handle active references to edge
            if (!EdgeLinker.IsDummyPairEdge(edge))
            {
                RemoveReferences(edge);
            }

            // handle active references to its pair
            if (!EdgeLinker.IsDummyPairEdge(edge.Pair))
            {
                RemoveReferences(edge.Pair);
            }

            // unlink the edges
            EdgeLinker.UnlinkEdge(edge);
            EdgeLinker.UnlinkEdge(edge.Pair);

            // remove edges from inner collection
            _elements.Remove(edge);
            _elements.Remove(edge.Pair);

            return(true);
        }
Ejemplo n.º 9
0
        internal static bool TrySplitEdge(this Kernel kernel, HalfEdge edge, double t, out (HalfEdge, HalfEdge) parts)
        {
            parts = (null, null);

            // TODO: Error checks
            if (EdgeLinker.IsDummyPairEdge(edge))
            {
                edge = edge.Pair;
            }

            // test valid param
            if (0 > t || t > 1)
            {
                return(false);
            }

            // calculate vec at param
            var vec = edge.Origin.Position.VecAtParameter(edge.Target.Position, t);

            // convert to a vertex
            var vertex = kernel.GetVertexForPosition(vec);

            // store edge pair, and conditionals
            var pair    = edge.Pair;
            var isDummy = EdgeLinker.IsDummyPairEdge(pair);
            var isStart = edge.Face.Start == edge;

            // create new edges
            var firstHalf = new HalfEdge
            {
                Face     = edge.Face,
                Previous = edge.Previous,
                Origin   = edge.Origin
            };
            var secondHalf = new HalfEdge
            {
                Face   = edge.Face,
                Next   = edge.Next,
                Origin = vertex
            };
            var pairFirstHalf = new HalfEdge
            {
                Face     = pair.Face,
                Previous = pair.Previous,
                Origin   = pair.Origin,
                Pair     = secondHalf,
            };
            var pairSecondHalf = new HalfEdge
            {
                Face   = pair.Face,
                Next   = pair.Next,
                Origin = vertex,
                Pair   = firstHalf
            };

            // link halves
            firstHalf.Next     = secondHalf;
            pairFirstHalf.Next = pairSecondHalf;

            // remove original edges
            kernel.Remove(edge);

            // add new halves
            if (!isDummy)
            {
                kernel.Insert(firstHalf);
                kernel.Insert(secondHalf);
                kernel.Insert(pairFirstHalf);
                kernel.Insert(pairSecondHalf);
            }
            else
            {
                firstHalf.Pair  = null;
                secondHalf.Pair = null;
                kernel.Insert(firstHalf);
                kernel.Insert(secondHalf);
            }

            // set start to first half
            if (isStart)
            {
                firstHalf.Face.Start = firstHalf;
            }

            parts = (firstHalf, secondHalf);

            return(true);
        }
Ejemplo n.º 10
0
        internal static bool TrySplitFace(this Kernel kernel, HalfEdge start, HalfEdge end, out (Face, Face) parts)
        {
            parts = (null, null);
            // TODO: Error checks

            // get edges for face of both halfedges
            var edges = new EdgeIterator(start).ToArray();

            // can't split triangles
            if (edges.Length <= 3)
            {
                return(false);
            }

            // get the index of the end edge inside the edges array
            var endIndex = Array.IndexOf(edges, end);

            // create the new ending halfedge for the start half
            var newEnd = new HalfEdge
            {
                Face     = start.Face,
                Next     = start,
                Previous = edges[endIndex - 1],
                Origin   = end.Origin
            };

            // create the new Face for the end half
            var newFace = new Face {
                Start = end
            };
            // create a new starting edge for the end half
            var newStart = new HalfEdge
            {
                Face     = newFace,
                Next     = end,
                Previous = edges.Last(),
                Origin   = start.Origin,
                Pair     = newEnd
            };

            // pair up the new end
            newEnd.Pair = newStart;

            // establish circular link for first face
            var firstFaceEdges = edges.Take(endIndex).ToList();

            firstFaceEdges.Add(newEnd);
            EdgeLinker.LinkOrderedEdgeCollection(firstFaceEdges);

            // hacky re-assignment
            newEnd.Face.Start = newEnd.Next;

            // establish circular link for second face
            var secondFaceEdges = edges.Skip(endIndex).ToList();

            secondFaceEdges.Insert(0, newStart);
            EdgeLinker.LinkOrderedEdgeCollection(secondFaceEdges);
            secondFaceEdges.ForEach(e => e.Face = newFace);

            // Add new edges to kernel
            kernel.Add(newEnd);
            kernel.Add(newStart);

            // add new face
            kernel.Insert(newFace);

            // prepare output
            parts = (start.Face, newFace);
            return(true);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Adds a new face to the kernel
        /// </summary>
        /// <param name="positions"></param>
        public bool AddFace(IEnumerable <Vec3d> positions)
        {
            // No bad faces please :(
            if (positions.Count() < 3)
            {
                return(false);
            }

            // convert positions to vertices inside the mesh
            var vertices = VertexQuery.GetVerticesForPositions(_vertices, positions);

            // if the positions had stacked vertices, we might not be able to continue
            if (vertices.Count() < 3)
            {
                // iterate over position vertices
                foreach (var vertex in vertices)
                {
                    // if the vertex was old, it will have an outgoing
                    if (vertex.Outgoing != null)
                    {
                        continue;
                    }

                    // if it was new we need to remove it to restore state
                    Remove(vertex);
                }

                return(false);
            }

            var edges = new List <HalfEdge>();

            // iterate over all positions
            foreach (var vertex in vertices)
            {
                // create a new halfedge originating from the current vertex and linked to the new face
                var halfEdge = new HalfEdge
                {
                    Origin = vertex,
                };

                // test if the vertex already has an outgoing edge assigned
                if (vertex.Outgoing is null)
                {
                    vertex.Outgoing = halfEdge;
                }

                edges.Add(halfEdge);
            }

            // Insert a face from the edges
            Insert(edges);

            // iterate over all edges and insert them
            foreach (var edge in edges)
            {
                // TODO: Maybe we should NOT skip linking checks here
                _halfEdges.Add(edge);
            }

            // test if first face
            if (this.FaceCount == 1)
            {
                var outerRing = new EdgeIterator(
                    _halfEdges[0])
                                .Select(h => h.Pair)
                                .Reverse()
                                .ToList();

                // establish circular link between outside half-edges
                EdgeLinker.LinkOrderedEdgeCollection(outerRing);
            }

            // TODO: Should be method of edgelinker instead
            // Make sure edge pairs either have a starting face or the ghost face
            // Iterators are lazy, calling `Count()` will execute the select body
            _ = edges.Where(e => e.Pair.Face == null).Select(e => e.Pair.Face = Kernel.Outside).Count();

            return(true);

            // TODO: Make sure outer halfEdges are linked,too
            // TODO: Right now only inner halfEdges are linked circularly
            // TODO: This leads to crashes on faces with too many naked edges when creating vertexringiterators
            // TODO: For this we will need a 'ghost' face that encompasses all the space outside of the mesh faces
            // TODO: CLosed meshes will have an empty ghost face
        }
Ejemplo n.º 12
0
        internal static void Kis(this Kernel kernel)
        {
            var initialFaceCount = kernel.FaceCount;

            for (int i = 0; i < initialFaceCount; i++)
            {
                var face = kernel.Faces[i];

                // get face center
                // TODO: We could move the center vertex by face normal direction to build a n - pyramid
                var center = face.GetFaceCenter();

                // get vertex for center
                var centerVertex = kernel.GetVertexForPosition(center);

                var newCenter = true;

                // iterate over face edges
                foreach (var edge in new EdgeIterator(face.Start))
                {
                    // new face that will be created
                    var newFace = new Face {
                        Start = edge
                    };

                    // link up current edge
                    edge.Face = newFace;

                    // create an edge from center to origin of current edge
                    if (!kernel.TryGetHalfEdgeBetweenVertices(centerVertex, edge.Origin, out var incoming))
                    {
                        incoming = new HalfEdge
                        {
                            Face   = newFace,
                            Origin = centerVertex
                        };
                    }

                    // create an edge from current edge target to center
                    if (!kernel.TryGetHalfEdgeBetweenVertices(edge.Target, centerVertex, out var outgoing))
                    {
                        outgoing = new HalfEdge
                        {
                            Face   = newFace,
                            Origin = edge.Target,
                        };
                    }

                    // link up center vertex on first iteration
                    if (newCenter)
                    {
                        centerVertex.Outgoing = incoming;
                        newCenter             = false;
                    }

                    // link up edges
                    EdgeLinker.LinkOrderedEdgeCollection(new[] { incoming, edge, outgoing });

                    // add new edges to kernel
                    kernel.Add(incoming);
                    kernel.Add(outgoing);

                    // insert new face into kernel
                    kernel.Insert(newFace);
                }

                // unlink face
                face.Start = null;
            }

            for (int i = initialFaceCount - 1; i >= 0; i--)
            {
                kernel.Remove(kernel.Faces[i]);
            }
        }