/// <summary>
        /// Iterates through face edges and builds a list using the opposite edge, iteratively.
        /// </summary>
        /// <param name="pb">The probuilder mesh</param>
        /// <param name="edges">The edges already selected</param>
        /// <returns>The new selected edges</returns>
        internal static IEnumerable <Edge> GetEdgeRingIterative(ProBuilderMesh pb, IEnumerable <Edge> edges)
        {
            List <WingedEdge> wings      = WingedEdge.GetWingedEdges(pb);
            List <EdgeLookup> edgeLookup = EdgeLookup.GetEdgeLookup(edges, pb.sharedVertexLookup).ToList();

            edgeLookup = edgeLookup.Distinct().ToList();

            Dictionary <Edge, WingedEdge> wings_dic = new Dictionary <Edge, WingedEdge>();

            for (int i = 0; i < wings.Count; i++)
            {
                if (!wings_dic.ContainsKey(wings[i].edge.common))
                {
                    wings_dic.Add(wings[i].edge.common, wings[i]);
                }
            }

            HashSet <EdgeLookup> used = new HashSet <EdgeLookup>();

            for (int i = 0, c = edgeLookup.Count; i < c; i++)
            {
                WingedEdge we;

                if (!wings_dic.TryGetValue(edgeLookup[i].common, out we))
                {
                    continue;
                }

                WingedEdge cur = we;

                if (!used.Contains(cur.edge))
                {
                    used.Add(cur.edge);
                }
                var next = EdgeRingNext(cur);
                if (next != null && next.opposite != null && !used.Contains(next.edge))
                {
                    used.Add(next.edge);
                }
                var prev = EdgeRingNext(cur.opposite);
                if (prev != null && prev.opposite != null && !used.Contains(prev.edge))
                {
                    used.Add(prev.edge);
                }
            }

            return(used.Select(x => x.local));
        }
Esempio n. 2
0
        //private void RemoveParallelIncomingEdges(Node<T, TActivity> node)
        //{
        //    if (node is null)
        //    {
        //        throw new ArgumentNullException(nameof(node));
        //    }
        //    // Clean up any dummy edges that are parallel coming into the head node.
        //    if (node.NodeType == NodeType.Start || node.NodeType == NodeType.Isolated)
        //    {
        //        return;
        //    }
        //    // First, find the tail nodes that connect to this node via ALL edges.
        //    // In a vertex graph, all edges should be removable.
        //    var tailNodeParallelEdgesLookup = new Dictionary<T, HashSet<T>>();
        //    IEnumerable<T> removableIncomingEdgeIds =
        //        node.IncomingEdges.Select(x => EdgeLookup[x])
        //        .Where(x => x.Content.CanBeRemoved)
        //        .Select(x => x.Id);

        //    foreach (T incomingEdgeId in removableIncomingEdgeIds)
        //    {
        //        T tailNodeId = EdgeTailNodeLookup[incomingEdgeId].Id;
        //        if (!tailNodeParallelEdgesLookup.TryGetValue(tailNodeId, out HashSet<T> edgeIds))
        //        {
        //            edgeIds = new HashSet<T>();
        //            tailNodeParallelEdgesLookup.Add(tailNodeId, edgeIds);
        //        }
        //        if (!edgeIds.Contains(incomingEdgeId))
        //        {
        //            edgeIds.Add(incomingEdgeId);
        //        }
        //    }

        //    // Now find the tail nodes that connect to this node via multiple edges.
        //    IList<T> setsOfMoreThanOneEdge =
        //        tailNodeParallelEdgesLookup
        //        .Where(x => x.Value.Count > 1)
        //        .Select(x => x.Key)
        //        .ToList();

        //    foreach (T tailNodeId in setsOfMoreThanOneEdge)
        //    {
        //        Node<T, TActivity> tailNode = EdgeTailNodeLookup[tailNodeId];
        //        IList<T> edgeIds = tailNodeParallelEdgesLookup[tailNodeId].ToList();
        //        int length = edgeIds.Count;
        //        // Leave one edge behind.
        //        for (int i = 1; i < length; i++)
        //        {
        //            T edgeId = edgeIds[i];

        //            // Remove the edge from the tail node.
        //            tailNode.OutgoingEdges.Remove(edgeId);
        //            EdgeTailNodeLookup.Remove(edgeId);

        //            // Remove the edge from the head node.
        //            node.IncomingEdges.Remove(edgeId);
        //            EdgeHeadNodeLookup.Remove(edgeId);

        //            // Remove the edge completely.
        //            EdgeLookup.Remove(edgeId);
        //        }
        //    }
        //}

        private void RemoveRedundantIncomingEdges(T nodeId, IDictionary <T, HashSet <T> > nodeIdAncestorLookup)
        {
            if (nodeIdAncestorLookup is null)
            {
                throw new ArgumentNullException(nameof(nodeIdAncestorLookup));
            }
            Node <T, TActivity> node = NodeLookup[nodeId];

            if (node.NodeType == NodeType.Start || node.NodeType == NodeType.Isolated)
            {
                return;
            }

            // Go through all the incoming edges and collate the
            // ancestors of their tail nodes.
            var tailNodeAncestors = new HashSet <T>(node.IncomingEdges
                                                    .Select(x => EdgeTailNodeLookup[x].Id)
                                                    .SelectMany(x => nodeIdAncestorLookup[x]));

            // Go through the incoming edges and remove any that connect
            // directly to any ancestors of the edges' tail nodes.
            // In a vertex graph, all edges should be removable.
            foreach (T edgeId in node.IncomingEdges.Select(x => EdgeLookup[x]).Where(x => x.Content.CanBeRemoved).Select(x => x.Id).ToList())
            {
                Node <T, TActivity> tailNode = EdgeTailNodeLookup[edgeId];
                T edgeTailNodeId             = tailNode.Id;
                if (tailNodeAncestors.Contains(edgeTailNodeId))
                {
                    // Remove the edge from the tail node.
                    tailNode.OutgoingEdges.Remove(edgeId);
                    EdgeTailNodeLookup.Remove(edgeId);

                    // Remove the edge from the node itself.
                    node.IncomingEdges.Remove(edgeId);
                    EdgeHeadNodeLookup.Remove(edgeId);

                    // Remove the edge completely.
                    EdgeLookup.Remove(edgeId);
                }
            }

            // Go through all the remaining incoming edges and repeat.
            foreach (T tailNodeId in node.IncomingEdges.Select(x => EdgeTailNodeLookup[x].Id).ToList())
            {
                RemoveRedundantIncomingEdges(tailNodeId, nodeIdAncestorLookup);
            }
        }
Esempio n. 3
0
        public void Analyze(MinutiaPairing pairing, EdgeLookup lookup, Template probe, Template candidate)
        {
            MaxDistanceError = lookup.MaxDistanceError;
            MaxAngleError    = lookup.MaxAngleError;
            var innerDistanceRadius = Convert.ToInt32(DistanceErrorFlatness * MaxDistanceError);
            var innerAngleRadius    = Convert.ToInt32(AngleErrorFlatness * MaxAngleError);

            PairCount = pairing.Count;

            EdgeCount        = 0;
            SupportedCount   = 0;
            CorrectTypeCount = 0;
            DistanceErrorSum = 0;
            AngleErrorSum    = 0;

            for (int i = 0; i < PairCount; ++i)
            {
                PairInfo pair = pairing.GetPair(i);
                if (pair.SupportingEdges >= MinSupportingEdges)
                {
                    ++SupportedCount;
                }
                EdgeCount += pair.SupportingEdges + 1;
                if (probe.Minutiae[pair.Pair.Probe].Type == candidate.Minutiae[pair.Pair.Candidate].Type)
                {
                    ++CorrectTypeCount;
                }
                if (i > 0)
                {
                    var probeEdge     = EdgeConstructor.Construct(probe, pair.Reference.Probe, pair.Pair.Probe);
                    var candidateEdge = EdgeConstructor.Construct(candidate, pair.Reference.Candidate, pair.Pair.Candidate);
                    DistanceErrorSum += Math.Abs(probeEdge.Length - candidateEdge.Length);
                    AngleErrorSum    += Math.Max(innerDistanceRadius, Angle.Distance(probeEdge.ReferenceAngle, candidateEdge.ReferenceAngle));
                    AngleErrorSum    += Math.Max(innerAngleRadius, Angle.Distance(probeEdge.NeighborAngle, candidateEdge.NeighborAngle));
                }
            }

            float probeFraction     = PairCount / (float)probe.Minutiae.Length;
            float candidateFraction = PairCount / (float)candidate.Minutiae.Length;

            PairFraction = (probeFraction + candidateFraction) / 2;
        }
        /// <summary>
        /// Attempts to find edges along an Edge loop in an iterative way
        ///
        /// Adds two edges to the selection, one at each extremity
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="lastEdgesAdded"></param>
        /// <param name="loop"></param>
        /// <returns></returns>
        internal static bool GetEdgeLoopIterative(ProBuilderMesh mesh, IEnumerable <Edge> edges, out Edge[] loop)
        {
            List <WingedEdge>        wings        = WingedEdge.GetWingedEdges(mesh);
            IEnumerable <EdgeLookup> m_edgeLookup = EdgeLookup.GetEdgeLookup(edges, mesh.sharedVertexLookup);
            HashSet <EdgeLookup>     sources      = new HashSet <EdgeLookup>(m_edgeLookup);
            HashSet <EdgeLookup>     used         = new HashSet <EdgeLookup>();

            for (int i = 0; i < wings.Count; i++)
            {
                if (!sources.Contains(wings[i].edge))
                {
                    continue;
                }

                GetEdgeLoopInternalIterative(wings[i], wings[i].edge.common, used);
            }

            loop = used.Select(x => x.local).ToArray();

            return(true);
        }
Esempio n. 5
0
        protected override void AnythingChanged()
        {
            if (mesh == null)
            {
                return;
            }

            var edges  = viewState == MeshViewState.All ? mesh.faces.SelectMany(x => x.edgesInternal) : mesh.selectedEdgesInternal;
            var common = EdgeLookup.GetEdgeLookup(edges, mesh.sharedVertexLookup);

            foreach (var edge in common)
            {
                if (m_Content.ContainsKey(edge))
                {
                    m_Content[edge] += ", " + edge.local;
                }
                else
                {
                    m_Content.Add(edge, string.Format("<b>{0}</b>: {1}", edge.common, edge.local));
                }
            }
        }
    public void PickEdges_DepthTestOff_RectSelectComplete()
    {
        Setup();
        var edges = TestEdgePick(new PickerOptions()
        {
            depthTest = false, rectSelectMode = RectSelectMode.Complete
        });

        Assert.IsNotNull(edges, "Selection is null");
        var selection = edges.FirstOrDefault();

        Assert.IsNotNull(selection, "Selection is null");
        HashSet <Edge> selectedElements = selection.Value;

        Assert.Greater(selectedElements.Count, 0);

        Dictionary <int, int> commonLookup = selection.Key.sharedVertexLookup;
        var allEdges      = EdgeLookup.GetEdgeLookupHashSet(selection.Key.facesInternal.SelectMany(x => x.edgesInternal), commonLookup);
        var selectedEdges = EdgeLookup.GetEdgeLookupHashSet(selectedElements, commonLookup);

        Assert.AreEqual(allEdges.Count, selectedEdges.Count);

        Cleanup();
    }
Esempio n. 7
0
        public static void DoMouseDrag(Rect mouseDragRect, SelectMode selectionMode, ScenePickerPreferences scenePickerPreferences)
        {
            var pickingOptions = new PickerOptions()
            {
                depthTest      = scenePickerPreferences.cullMode == CullingMode.Back,
                rectSelectMode = scenePickerPreferences.rectSelectMode
            };

            UndoUtility.RecordSelection("Drag Select");
            bool isAppendModifier = EditorHandleUtility.IsAppendModifier(Event.current.modifiers);

            if (!isAppendModifier)
            {
                MeshSelection.ClearElementSelection();
            }

            bool elementsInDragRect = false;

            switch (selectionMode)
            {
            case SelectMode.Vertex:
            case SelectMode.TextureVertex:
            {
                Dictionary <ProBuilderMesh, HashSet <int> > selected = SelectionPicker.PickVerticesInRect(
                    SceneView.lastActiveSceneView.camera,
                    mouseDragRect,
                    MeshSelection.topInternal,
                    pickingOptions,
                    EditorGUIUtility.pixelsPerPoint);

                foreach (var kvp in selected)
                {
                    var            mesh          = kvp.Key;
                    SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
                    HashSet <int>  common;

                    if (isAppendModifier)
                    {
                        common = mesh.GetSharedVertexHandles(mesh.selectedIndexesInternal);

                        if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Add)
                        {
                            common.UnionWith(kvp.Value);
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Subtract)
                        {
                            common.RemoveWhere(x => kvp.Value.Contains(x));
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Difference)
                        {
                            common.SymmetricExceptWith(kvp.Value);
                        }
                    }
                    else
                    {
                        common = kvp.Value;
                    }

                    elementsInDragRect |= kvp.Value.Any();
                    mesh.SetSelectedVertices(common.SelectMany(x => sharedIndexes[x]));
                }

                break;
            }

            case SelectMode.Face:
            case SelectMode.TextureFace:
            {
                Dictionary <ProBuilderMesh, HashSet <Face> > selected = SelectionPicker.PickFacesInRect(
                    SceneView.lastActiveSceneView.camera,
                    mouseDragRect,
                    MeshSelection.topInternal,
                    pickingOptions,
                    EditorGUIUtility.pixelsPerPoint);

                foreach (var kvp in selected)
                {
                    HashSet <Face> current;

                    if (isAppendModifier)
                    {
                        current = new HashSet <Face>(kvp.Key.selectedFacesInternal);

                        if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Add)
                        {
                            current.UnionWith(kvp.Value);
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Subtract)
                        {
                            current.RemoveWhere(x => kvp.Value.Contains(x));
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Difference)
                        {
                            current.SymmetricExceptWith(kvp.Value);
                        }
                    }
                    else
                    {
                        current = kvp.Value;
                    }

                    elementsInDragRect |= kvp.Value.Any();
                    kvp.Key.SetSelectedFaces(current);
                }

                break;
            }

            case SelectMode.Edge:
            case SelectMode.TextureEdge:
            {
                var selected = SelectionPicker.PickEdgesInRect(
                    SceneView.lastActiveSceneView.camera,
                    mouseDragRect,
                    MeshSelection.topInternal,
                    pickingOptions,
                    EditorGUIUtility.pixelsPerPoint);

                foreach (var kvp in selected)
                {
                    ProBuilderMesh        mesh          = kvp.Key;
                    Dictionary <int, int> common        = mesh.sharedVertexLookup;
                    HashSet <EdgeLookup>  selectedEdges = EdgeLookup.GetEdgeLookupHashSet(kvp.Value, common);
                    HashSet <EdgeLookup>  current;

                    if (isAppendModifier)
                    {
                        current = EdgeLookup.GetEdgeLookupHashSet(mesh.selectedEdges, common);

                        if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Add)
                        {
                            current.UnionWith(selectedEdges);
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Subtract)
                        {
                            current.RemoveWhere(x => selectedEdges.Contains(x));
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Difference)
                        {
                            current.SymmetricExceptWith(selectedEdges);
                        }
                    }
                    else
                    {
                        current = selectedEdges;
                    }

                    elementsInDragRect |= kvp.Value.Any();
                    mesh.SetSelectedEdges(current.Select(x => x.local));
                }

                break;
            }
            }

            // if nothing was selected in the drag rect, clear the object selection too
            if (!elementsInDragRect && !isAppendModifier)
            {
                MeshSelection.ClearElementAndObjectSelection();
            }

            ProBuilderEditor.Refresh();
            SceneView.RepaintAll();
        }
Esempio n. 8
0
        /// <summary>
        /// Inserts new edges connecting the passed edges, optionally restricting new edge insertion to faces in faceMask.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="edges"></param>
        /// <param name="addedFaces"></param>
        /// <param name="connections"></param>
        /// <param name="returnFaces"></param>
        /// <param name="returnEdges"></param>
        /// <param name="faceMask"></param>
        /// <returns></returns>
        internal static ActionResult Connect(
            this ProBuilderMesh mesh,
            IEnumerable <Edge> edges,
            out Face[] addedFaces,
            out Edge[] connections,
            bool returnFaces        = false,
            bool returnEdges        = false,
            HashSet <Face> faceMask = null)
        {
            Dictionary <int, int> lookup        = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV      = mesh.sharedTextureLookup;
            HashSet <EdgeLookup>  distinctEdges = new HashSet <EdgeLookup>(EdgeLookup.GetEdgeLookup(edges, lookup));
            List <WingedEdge>     wings         = WingedEdge.GetWingedEdges(mesh);

            // map each edge to a face so that we have a list of all touched faces with their to-be-subdivided edges
            Dictionary <Face, List <WingedEdge> > touched = new Dictionary <Face, List <WingedEdge> >();

            foreach (WingedEdge wing in wings)
            {
                if (distinctEdges.Contains(wing.edge))
                {
                    List <WingedEdge> faceEdges;
                    if (touched.TryGetValue(wing.face, out faceEdges))
                    {
                        faceEdges.Add(wing);
                    }
                    else
                    {
                        touched.Add(wing.face, new List <WingedEdge>()
                        {
                            wing
                        });
                    }
                }
            }

            Dictionary <Face, List <WingedEdge> > affected = new Dictionary <Face, List <WingedEdge> >();

            // weed out edges that won't actually connect to other edges (if you don't play ya' can't stay)
            foreach (KeyValuePair <Face, List <WingedEdge> > kvp in touched)
            {
                if (kvp.Value.Count <= 1)
                {
                    WingedEdge opp = kvp.Value[0].opposite;

                    if (opp == null)
                    {
                        continue;
                    }

                    List <WingedEdge> opp_list;

                    if (!touched.TryGetValue(opp.face, out opp_list))
                    {
                        continue;
                    }

                    if (opp_list.Count <= 1)
                    {
                        continue;
                    }
                }

                affected.Add(kvp.Key, kvp.Value);
            }

            List <Vertex> vertices = new List <Vertex>(mesh.GetVertices());
            List <ConnectFaceRebuildData> results = new List <ConnectFaceRebuildData>();
            // just the faces that where connected with > 1 edge
            List <Face> connectedFaces = new List <Face>();

            HashSet <int> usedTextureGroups    = new HashSet <int>(mesh.facesInternal.Select(x => x.textureGroup));
            int           newTextureGroupIndex = 1;

            // do the splits
            foreach (KeyValuePair <Face, List <WingedEdge> > split in affected)
            {
                Face face = split.Key;
                List <WingedEdge> targetEdges = split.Value;
                int     inserts = targetEdges.Count;
                Vector3 nrm     = Math.Normal(vertices, face.indexesInternal);

                if (inserts == 1 || (faceMask != null && !faceMask.Contains(face)))
                {
                    ConnectFaceRebuildData c;

                    if (InsertVertices(face, targetEdges, vertices, out c))
                    {
                        Vector3 fn = Math.Normal(c.faceRebuildData.vertices, c.faceRebuildData.face.indexesInternal);

                        if (Vector3.Dot(nrm, fn) < 0)
                        {
                            c.faceRebuildData.face.Reverse();
                        }

                        results.Add(c);
                    }
                }
                else if (inserts > 1)
                {
                    List <ConnectFaceRebuildData> res = inserts == 2 ?
                                                        ConnectEdgesInFace(face, targetEdges[0], targetEdges[1], vertices) :
                                                        ConnectEdgesInFace(face, targetEdges, vertices);

                    if (face.textureGroup < 0)
                    {
                        while (usedTextureGroups.Contains(newTextureGroupIndex))
                        {
                            newTextureGroupIndex++;
                        }

                        usedTextureGroups.Add(newTextureGroupIndex);
                    }


                    if (res == null)
                    {
                        connections = null;
                        addedFaces  = null;
                        return(new ActionResult(ActionResult.Status.Failure, "Unable to connect faces"));
                    }
                    else
                    {
                        foreach (ConnectFaceRebuildData c in res)
                        {
                            connectedFaces.Add(c.faceRebuildData.face);

                            Vector3 fn = Math.Normal(c.faceRebuildData.vertices,
                                                     c.faceRebuildData.face.indexesInternal);

                            if (Vector3.Dot(nrm, fn) < 0)
                            {
                                c.faceRebuildData.face.Reverse();
                            }

                            c.faceRebuildData.face.textureGroup =
                                face.textureGroup < 0 ? newTextureGroupIndex : face.textureGroup;
                            c.faceRebuildData.face.uv             = new AutoUnwrapSettings(face.uv);
                            c.faceRebuildData.face.submeshIndex   = face.submeshIndex;
                            c.faceRebuildData.face.smoothingGroup = face.smoothingGroup;
                            c.faceRebuildData.face.manualUV       = face.manualUV;
                        }

                        results.AddRange(res);
                    }
                }
            }

            FaceRebuildData.Apply(results.Select(x => x.faceRebuildData), mesh, vertices, null);

            mesh.sharedTextures = new SharedVertex[0];
            int removedVertexCount = mesh.DeleteFaces(affected.Keys).Length;

            mesh.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(mesh.positionsInternal);
            mesh.ToMesh();

            // figure out where the new edges where inserted
            if (returnEdges)
            {
                // offset the newVertexIndexes by whatever the FaceRebuildData did so we can search for the new edges by index
                var appended = new HashSet <int>();

                for (int n = 0; n < results.Count; n++)
                {
                    for (int i = 0; i < results[n].newVertexIndexes.Count; i++)
                    {
                        appended.Add((results[n].newVertexIndexes[i] + results[n].faceRebuildData.Offset()) - removedVertexCount);
                    }
                }

                Dictionary <int, int>    lup          = mesh.sharedVertexLookup;
                IEnumerable <Edge>       newEdges     = results.SelectMany(x => x.faceRebuildData.face.edgesInternal).Where(x => appended.Contains(x.a) && appended.Contains(x.b));
                IEnumerable <EdgeLookup> distNewEdges = EdgeLookup.GetEdgeLookup(newEdges, lup);

                connections = distNewEdges.Distinct().Select(x => x.local).ToArray();
            }
            else
            {
                connections = null;
            }

            if (returnFaces)
            {
                addedFaces = connectedFaces.ToArray();
            }
            else
            {
                addedFaces = null;
            }

            return(new ActionResult(ActionResult.Status.Success, string.Format("Connected {0} Edges", results.Count / 2)));
        }
        /// <summary>
        /// Insert a number of new points to each edge. Points are evenly spaced out along the edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="edges">The edges to split with points.</param>
        /// <param name="count">The number of new points to insert. Must be greater than 0.</param>
        /// <returns>The new edges created by inserting points.</returns>
        public static List <Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, IList <Edge> edges, int count)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (edges == null)
            {
                throw new ArgumentNullException("edges");
            }

            if (count < 1 || count > 512)
            {
                Log.Error("New edge vertex count is less than 1 or greater than 512.");
                return(null);
            }

            List <Vertex>         vertices        = new List <Vertex>(mesh.GetVertices());
            Dictionary <int, int> lookup          = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV        = mesh.sharedTextureLookup;
            List <int>            indexesToDelete = new List <int>();
            IEnumerable <Edge>    commonEdges     = EdgeUtility.GetSharedVertexHandleEdges(mesh, edges);
            List <Edge>           distinctEdges   = commonEdges.Distinct().ToList();

            Dictionary <Face, FaceRebuildData> modifiedFaces = new Dictionary <Face, FaceRebuildData>();

            int originalSharedIndexesCount = lookup.Count();
            int sharedIndexesCount         = originalSharedIndexesCount;

            foreach (Edge edge in distinctEdges)
            {
                Edge localEdge = EdgeUtility.GetEdgeWithSharedVertexHandles(mesh, edge);

                // Generate the new vertices that will be inserted on this edge
                List <Vertex> verticesToAppend = new List <Vertex>(count);

                for (int i = 0; i < count; i++)
                {
                    verticesToAppend.Add(Vertex.Mix(vertices[localEdge.a], vertices[localEdge.b], (i + 1) / ((float)count + 1)));
                }

                List <SimpleTuple <Face, Edge> > adjacentFaces = ElementSelection.GetNeighborFaces(mesh, localEdge);

                // foreach face attached to common edge, append vertices
                foreach (SimpleTuple <Face, Edge> tup in adjacentFaces)
                {
                    Face face = tup.item1;

                    FaceRebuildData data;

                    if (!modifiedFaces.TryGetValue(face, out data))
                    {
                        data                 = new FaceRebuildData();
                        data.face            = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV);
                        data.vertices        = new List <Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
                        data.sharedIndexes   = new List <int>();
                        data.sharedIndexesUV = new List <int>();

                        foreach (int i in face.distinctIndexesInternal)
                        {
                            int shared;

                            if (lookup.TryGetValue(i, out shared))
                            {
                                data.sharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }
                        }

                        indexesToDelete.AddRange(face.distinctIndexesInternal);

                        modifiedFaces.Add(face, data);
                    }

                    data.vertices.AddRange(verticesToAppend);

                    for (int i = 0; i < count; i++)
                    {
                        data.sharedIndexes.Add(sharedIndexesCount + i);
                        data.sharedIndexesUV.Add(-1);
                    }
                }

                sharedIndexesCount += count;
            }

            // now apply the changes
            List <Face>            dic_face      = modifiedFaces.Keys.ToList();
            List <FaceRebuildData> dic_data      = modifiedFaces.Values.ToList();
            List <EdgeLookup>      appendedEdges = new List <EdgeLookup>();

            for (int i = 0; i < dic_face.Count; i++)
            {
                Face            face = dic_face[i];
                FaceRebuildData data = dic_data[i];

                Vector3   nrm        = Math.Normal(mesh, face);
                Vector2[] projection = Projection.PlanarProject(data.vertices.Select(x => x.position).ToArray(), null, nrm);

                int vertexCount = vertices.Count;

                // triangulate and set new face indexes to end of current vertex list
                List <int> indexes;

                if (Triangulation.SortAndTriangulate(projection, out indexes))
                {
                    data.face.indexesInternal = indexes.ToArray();
                }
                else
                {
                    continue;
                }

                data.face.ShiftIndexes(vertexCount);
                face.CopyFrom(data.face);

                for (int n = 0; n < data.vertices.Count; n++)
                {
                    lookup.Add(vertexCount + n, data.sharedIndexes[n]);
                }

                if (data.sharedIndexesUV.Count == data.vertices.Count)
                {
                    for (int n = 0; n < data.vertices.Count; n++)
                    {
                        lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
                    }
                }

                vertices.AddRange(data.vertices);

                foreach (Edge e in face.edgesInternal)
                {
                    EdgeLookup el = new EdgeLookup(new Edge(lookup[e.a], lookup[e.b]), e);

                    if (el.common.a >= originalSharedIndexesCount || el.common.b >= originalSharedIndexesCount)
                    {
                        appendedEdges.Add(el);
                    }
                }
            }

            indexesToDelete = indexesToDelete.Distinct().ToList();
            int delCount = indexesToDelete.Count;

            var newEdges = appendedEdges.Distinct().Select(x => x.local - delCount).ToList();

            mesh.SetVertices(vertices);
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);
            mesh.DeleteVertices(indexesToDelete);

            return(newEdges);
        }
Esempio n. 10
0
        /// <summary>
        /// Extrude faces as groups.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="faces"></param>
        /// <param name="compensateAngleVertexDistance"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        static Face[] ExtrudeAsGroups(ProBuilderMesh mesh, IEnumerable <Face> faces, bool compensateAngleVertexDistance, float distance)
        {
            if (faces == null || !faces.Any())
            {
                return(null);
            }

            List <Vertex>         vertices          = new List <Vertex>(mesh.GetVertices());
            int                   sharedIndexMax    = mesh.sharedVerticesInternal.Length;
            int                   sharedIndexOffset = 0;
            Dictionary <int, int> lookup            = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV          = mesh.sharedTextureLookup;

            List <Face> newFaces = new List <Face>();
            // old triangle index -> old shared index
            Dictionary <int, int> oldSharedMap = new Dictionary <int, int>();
            // old shared index -> new shared index
            Dictionary <int, int> newSharedMap = new Dictionary <int, int>();
            // bridge face extruded edges, maps vertex index to new extruded vertex position
            Dictionary <int, int> delayPosition = new Dictionary <int, int>();
            // used to average the direction of vertices shared by perimeter edges
            // key[shared index], value[normal count, normal sum]
            Dictionary <int, SimpleTuple <Vector3, Vector3, List <int> > > extrudeMap = new Dictionary <int, SimpleTuple <Vector3, Vector3, List <int> > >();

            List <WingedEdge>      wings  = WingedEdge.GetWingedEdges(mesh, faces, true);
            List <HashSet <Face> > groups = GetFaceGroups(wings);

            foreach (HashSet <Face> group in groups)
            {
                Dictionary <EdgeLookup, Face> perimeter = GetPerimeterEdges(group, lookup);

                newSharedMap.Clear();
                oldSharedMap.Clear();

                foreach (var edgeAndFace in perimeter)
                {
                    EdgeLookup edge = edgeAndFace.Key;
                    Face       face = edgeAndFace.Value;

                    int vc = vertices.Count;
                    int x = edge.local.a, y = edge.local.b;

                    if (!oldSharedMap.ContainsKey(x))
                    {
                        oldSharedMap.Add(x, lookup[x]);
                        int newSharedIndex = -1;

                        if (newSharedMap.TryGetValue(lookup[x], out newSharedIndex))
                        {
                            lookup[x] = newSharedIndex;
                        }
                        else
                        {
                            newSharedIndex = sharedIndexMax + (sharedIndexOffset++);
                            newSharedMap.Add(lookup[x], newSharedIndex);
                            lookup[x] = newSharedIndex;
                        }
                    }

                    if (!oldSharedMap.ContainsKey(y))
                    {
                        oldSharedMap.Add(y, lookup[y]);
                        int newSharedIndex = -1;

                        if (newSharedMap.TryGetValue(lookup[y], out newSharedIndex))
                        {
                            lookup[y] = newSharedIndex;
                        }
                        else
                        {
                            newSharedIndex = sharedIndexMax + (sharedIndexOffset++);
                            newSharedMap.Add(lookup[y], newSharedIndex);
                            lookup[y] = newSharedIndex;
                        }
                    }

                    lookup.Add(vc + 0, oldSharedMap[x]);
                    lookup.Add(vc + 1, oldSharedMap[y]);
                    lookup.Add(vc + 2, lookup[x]);
                    lookup.Add(vc + 3, lookup[y]);

                    delayPosition.Add(vc + 2, x);
                    delayPosition.Add(vc + 3, y);

                    vertices.Add(new Vertex(vertices[x]));
                    vertices.Add(new Vertex(vertices[y]));

                    // extruded edge will be positioned later
                    vertices.Add(null);
                    vertices.Add(null);

                    Face bridge = new Face(
                        new int[6] {
                        vc + 0, vc + 1, vc + 2, vc + 1, vc + 3, vc + 2
                    },
                        face.submeshIndex,
                        new AutoUnwrapSettings(face.uv),
                        Smoothing.smoothingGroupNone,
                        -1,
                        -1,
                        false
                        );

                    newFaces.Add(bridge);
                }

                foreach (Face face in group)
                {
                    // @todo keep together if possible
                    face.textureGroup = -1;

                    Vector3 normal = Math.Normal(mesh, face);

                    for (int i = 0; i < face.distinctIndexesInternal.Length; i++)
                    {
                        int idx = face.distinctIndexesInternal[i];

                        // If this vertex is on the perimeter but not part of a perimeter edge
                        // move the sharedIndex to match it's new value.
                        if (!oldSharedMap.ContainsKey(idx) && newSharedMap.ContainsKey(lookup[idx]))
                        {
                            lookup[idx] = newSharedMap[lookup[idx]];
                        }

                        int com = lookup[idx];

                        // Break any UV shared connections
                        if (lookupUV != null && lookupUV.ContainsKey(face.distinctIndexesInternal[i]))
                        {
                            lookupUV.Remove(face.distinctIndexesInternal[i]);
                        }

                        // add the normal to the list of normals for this shared vertex
                        SimpleTuple <Vector3, Vector3, List <int> > dir;

                        if (extrudeMap.TryGetValue(com, out dir))
                        {
                            dir.item1 += normal;
                            dir.item3.Add(idx);
                            extrudeMap[com] = dir;
                        }
                        else
                        {
                            extrudeMap.Add(com, new SimpleTuple <Vector3, Vector3, List <int> >(normal, normal, new List <int>()
                            {
                                idx
                            }));
                        }
                    }
                }
            }

            foreach (var kvp in extrudeMap)
            {
                Vector3 direction = (kvp.Value.item1 / kvp.Value.item3.Count);
                direction.Normalize();

                // If extruding by face normal extend vertices on seams by the hypotenuse
                float modifier = compensateAngleVertexDistance ? Math.Secant(Vector3.Angle(direction, kvp.Value.item2) * Mathf.Deg2Rad) : 1f;

                direction.x *= distance * modifier;
                direction.y *= distance * modifier;
                direction.z *= distance * modifier;

                foreach (int i in kvp.Value.item3)
                {
                    vertices[i].position += direction;
                }
            }

            foreach (var kvp in delayPosition)
            {
                vertices[kvp.Key] = new Vertex(vertices[kvp.Value]);
            }

            mesh.SetVertices(vertices);

            var fc       = mesh.faceCount;
            var nc       = newFaces.Count;
            var appended = new Face[fc + nc];

            Array.Copy(mesh.facesInternal, 0, appended, 0, fc);
            for (int i = fc, c = fc + nc; i < c; i++)
            {
                appended[i] = newFaces[i - fc];
            }
            mesh.faces = appended;
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);

            return(newFaces.ToArray());
        }
        static IEnumerable <List <Edge> > GetEdgeSelectionGroups(ProBuilderMesh mesh)
        {
            var edges  = EdgeLookup.GetEdgeLookup(mesh.selectedEdgesInternal, mesh.sharedVertexLookup);
            var groups = new List <SimpleTuple <HashSet <int>, List <Edge> > >();

            foreach (var edge in edges)
            {
                var foundMatch = false;

                foreach (var kvp in groups)
                {
                    if (kvp.item1.Contains(edge.common.a) || kvp.item1.Contains(edge.common.b))
                    {
                        kvp.item1.Add(edge.common.a);
                        kvp.item1.Add(edge.common.b);
                        kvp.item2.Add(edge.local);
                        foundMatch = true;
                        break;
                    }
                }

                if (!foundMatch)
                {
                    groups.Add(new SimpleTuple <HashSet <int>, List <Edge> >(
                                   new HashSet <int>()
                    {
                        edge.common.a, edge.common.b
                    },
                                   new List <Edge>()
                    {
                        edge.local
                    }));
                }
            }

            // collect overlapping groups (happens in cases where selection order begins as two separate groups but
            // becomes one)
            var res     = new List <List <Edge> >();
            var overlap = new HashSet <int>();

            for (int i = 0, c = groups.Count; i < c; i++)
            {
                if (overlap.Contains(i))
                {
                    continue;
                }

                List <Edge> grp = groups[i].item2;

                for (int n = i + 1; n < c; n++)
                {
                    if (groups[i].item1.Overlaps(groups[n].item1))
                    {
                        overlap.Add(n);
                        grp.AddRange(groups[n].item2);
                    }
                }

                res.Add(grp);
            }

            return(res);
        }
Esempio n. 12
0
        public override bool RemoveActivityDependencies(T activityId, HashSet <T> dependencies)
        {
            if (dependencies is null)
            {
                throw new ArgumentNullException(nameof(dependencies));
            }
            if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node))
            {
                return(false);
            }
            if (!dependencies.Any())
            {
                return(true);
            }

            RemoveUnsatisfiedSuccessorActivityDependencies(activityId, dependencies);

            if (node.NodeType == NodeType.Start ||
                node.NodeType == NodeType.Isolated)
            {
                return(true);
            }

            // If any dependencies currently exist, remove them.
            var       existingDependencyLookup = new HashSet <T>(NodeLookup.Keys.Intersect(dependencies));
            IList <T> incomingEdgeIds          = node.IncomingEdges.ToList();
            int       length = incomingEdgeIds.Count;

            for (int i = 0; i < length; i++)
            {
                T edgeId = incomingEdgeIds[i];
                Node <T, TActivity> tailNode = EdgeTailNodeLookup[edgeId];

                if (!existingDependencyLookup.Contains(tailNode.Id))
                {
                    continue;
                }

                // Remove the edge from the tail node.
                tailNode.OutgoingEdges.Remove(edgeId);
                EdgeTailNodeLookup.Remove(edgeId);

                if (!tailNode.OutgoingEdges.Any())
                {
                    if (tailNode.NodeType == NodeType.Normal)
                    {
                        tailNode.SetNodeType(NodeType.End);
                    }
                    else if (tailNode.NodeType == NodeType.Start)
                    {
                        tailNode.SetNodeType(NodeType.Isolated);
                    }
                }

                // Remove the edge from the head node.
                node.IncomingEdges.Remove(edgeId);
                EdgeHeadNodeLookup.Remove(edgeId);

                // Remove the edge completely.
                EdgeLookup.Remove(edgeId);
            }

            if (!node.IncomingEdges.Any())
            {
                if (node.NodeType == NodeType.Normal)
                {
                    node.SetNodeType(NodeType.Start);
                }
                else if (node.NodeType == NodeType.End)
                {
                    node.SetNodeType(NodeType.Isolated);
                }
            }

            return(true);
        }
Esempio n. 13
0
        public override bool AddActivityDependencies(T activityId, HashSet <T> dependencies)
        {
            if (dependencies is null)
            {
                throw new ArgumentNullException(nameof(dependencies));
            }

            if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node))
            {
                return(false);
            }
            if (!dependencies.Any())
            {
                return(true);
            }
            if (dependencies.Contains(activityId))
            {
                return(false);
            }

            // If the node is an Start or Isolated node, then convert it.
            if (node.NodeType == NodeType.Start)
            {
                node.SetNodeType(NodeType.Normal);
            }
            else if (node.NodeType == NodeType.Isolated)
            {
                node.SetNodeType(NodeType.End);
            }

            // Check which of the expected dependencies currently exist.
            IList <T> existingDependencies    = NodeLookup.Keys.Intersect(dependencies).ToList();
            IList <T> nonExistingDependencies = dependencies.Except(existingDependencies).ToList();

            // If any expected dependencies currently exist, generate an edge to connect them.
            foreach (T dependencyId in existingDependencies)
            {
                Node <T, TActivity> dependencyNode = NodeLookup[dependencyId];
                T   edgeId = EdgeIdGenerator();
                var edge   = new Edge <T, TEvent>(EventGenerator(edgeId));
                node.IncomingEdges.Add(edgeId);
                EdgeHeadNodeLookup.Add(edgeId, node);

                // If the dependency node is an End or Isolated node, then convert it.
                if (dependencyNode.NodeType == NodeType.End)
                {
                    dependencyNode.SetNodeType(NodeType.Normal);
                }
                else if (dependencyNode.NodeType == NodeType.Isolated)
                {
                    dependencyNode.SetNodeType(NodeType.Start);
                }

                dependencyNode.OutgoingEdges.Add(edgeId);
                EdgeTailNodeLookup.Add(edgeId, dependencyNode);
                EdgeLookup.Add(edgeId, edge);
            }

            // If any expected dependencies currently do not exist, then record their
            // IDs and add this node as an unsatisfied successor.
            foreach (T dependencyId in nonExistingDependencies)
            {
                if (!UnsatisfiedSuccessorsLookup.TryGetValue(dependencyId, out HashSet <Node <T, TActivity> > successorNodes))
                {
                    successorNodes = new HashSet <Node <T, TActivity> >();
                    UnsatisfiedSuccessorsLookup.Add(dependencyId, successorNodes);
                }
                successorNodes.Add(node);
            }
            return(true);
        }
        /// <summary>
        /// Iterates through face edges and builds a list using the opposite edge.
        /// </summary>
        /// <param name="pb"></param>
        /// <param name="edges"></param>
        /// <returns></returns>
        internal static IEnumerable <Edge> GetEdgeRing(ProBuilderMesh pb, IEnumerable <Edge> edges)
        {
            List <WingedEdge> wings      = WingedEdge.GetWingedEdges(pb);
            List <EdgeLookup> edgeLookup = EdgeLookup.GetEdgeLookup(edges, pb.sharedVertexLookup).ToList();

            edgeLookup = edgeLookup.Distinct().ToList();

            Dictionary <Edge, WingedEdge> wings_dic = new Dictionary <Edge, WingedEdge>();

            for (int i = 0; i < wings.Count; i++)
            {
                if (!wings_dic.ContainsKey(wings[i].edge.common))
                {
                    wings_dic.Add(wings[i].edge.common, wings[i]);
                }
            }

            HashSet <EdgeLookup> used = new HashSet <EdgeLookup>();

            for (int i = 0, c = edgeLookup.Count; i < c; i++)
            {
                WingedEdge we;

                if (!wings_dic.TryGetValue(edgeLookup[i].common, out we) || used.Contains(we.edge))
                {
                    continue;
                }

                WingedEdge cur = we;

                while (cur != null)
                {
                    if (!used.Add(cur.edge))
                    {
                        break;
                    }
                    cur = EdgeRingNext(cur);
                    if (cur != null && cur.opposite != null)
                    {
                        cur = cur.opposite;
                    }
                }

                cur = EdgeRingNext(we.opposite);
                if (cur != null && cur.opposite != null)
                {
                    cur = cur.opposite;
                }

                // run in both directions
                while (cur != null)
                {
                    if (!used.Add(cur.edge))
                    {
                        break;
                    }
                    cur = EdgeRingNext(cur);
                    if (cur != null && cur.opposite != null)
                    {
                        cur = cur.opposite;
                    }
                }
            }

            return(used.Select(x => x.local));
        }
        static IEnumerable <List <Edge> > GetEdgeSelectionGroups(ProBuilderMesh mesh)
        {
            var edges  = EdgeLookup.GetEdgeLookup(mesh.selectedEdgesInternal, mesh.sharedVertexLookup);
            var groups = new List <SimpleTuple <HashSet <int>, List <Edge> > >();

            foreach (var edge in edges)
            {
                var foundMatch = false;

                foreach (var kvp in groups)
                {
                    if (kvp.item1.Contains(edge.common.a) || kvp.item1.Contains(edge.common.b))
                    {
                        kvp.item1.Add(edge.common.a);
                        kvp.item1.Add(edge.common.b);
                        kvp.item2.Add(edge.local);
                        foundMatch = true;
                        break;
                    }
                }

                if (!foundMatch)
                {
                    groups.Add(new SimpleTuple <HashSet <int>, List <Edge> >(
                                   new HashSet <int>()
                    {
                        edge.common.a, edge.common.b
                    },
                                   new List <Edge>()
                    {
                        edge.local
                    }));
                }
            }

            // collect overlapping groups (happens in cases where selection order begins as two separate groups but
            // becomes one)
            var res      = new List <List <Edge> >();
            var overlap  = new HashSet <int>();
            var j        = -1;
            var groupIdx = -1;

            for (int i = 0, c = groups.Count; i < c; i++)
            {
                if (overlap.Contains(i))
                {
                    continue;
                }

                List <Edge> grp = groups[i].item2;

                for (int n = i + 1; n < c; n++)
                {
                    if (groups[i].item1.Overlaps(groups[n].item1))
                    {
                        overlap.Add(n);
                        grp.AddRange(groups[n].item2);
                    }
                }
                j++;
                // Make sure the last selected edge is the last in the group
                var idx = grp.IndexOf(mesh.selectedEdgesInternal[mesh.selectedEdgesInternal.Length - 1]);
                if (idx != -1 && idx < grp.Count)
                {
                    var item = grp[idx];
                    groupIdx           = j;
                    grp[idx]           = grp[grp.Count - 1];
                    grp[grp.Count - 1] = item;
                }
                res.Add(grp);
            }

            // Make sure the last selected edge's group is the last in the groups
            if (groupIdx != -1 && groupIdx < res.Count)
            {
                var item = res[groupIdx];
                res[groupIdx]      = res[res.Count - 1];
                res[res.Count - 1] = item;
            }

            return(res);
        }
Esempio n. 16
0
        public bool RemoveDummyActivity(T activityId)
        {
            // Retrieve the activity's edge.
            if (!EdgeLookup.TryGetValue(activityId, out Edge <T, TActivity> edge))
            {
                return(false);
            }
            if (!edge.Content.IsDummy)
            {
                return(false);
            }
            if (!edge.Content.CanBeRemoved)
            {
                return(false);
            }

            Node <T, TEvent> tailNode = EdgeTailNodeLookup[activityId];
            Node <T, TEvent> headNode = EdgeHeadNodeLookup[activityId];

            // Check to make sure that no other edges will be made parallel
            // by removing this edge.
            if (HaveDecendantOrAncestorOverlap(tailNode, headNode) &&
                !ShareMoreThanOneEdge(tailNode, headNode))
            {
                return(false);
            }

            // Remove the edge from the tail node.
            tailNode.OutgoingEdges.Remove(activityId);
            EdgeTailNodeLookup.Remove(activityId);

            // Remove the edge from the head node.
            headNode.IncomingEdges.Remove(activityId);
            EdgeHeadNodeLookup.Remove(activityId);

            // Remove the edge completely.
            EdgeLookup.Remove(activityId);

            // If the head node is not the End node, and it has no more incoming
            // edges, then transfer the head node's outgoing edges to the tail node.
            if (headNode.NodeType != NodeType.End &&
                headNode.NodeType != NodeType.Isolated &&
                !headNode.IncomingEdges.Any())
            {
                IList <T> headNodeOutgoingEdgeIds = headNode.OutgoingEdges.ToList();
                foreach (T headNodeOutgoingEdgeId in headNodeOutgoingEdgeIds)
                {
                    bool changeTailSuccess = ChangeEdgeTailNode(headNodeOutgoingEdgeId, tailNode.Id);
                    if (!changeTailSuccess)
                    {
                        throw new InvalidOperationException($@"Unable to change tail node of edge {headNodeOutgoingEdgeId} to node {tailNode.Id} when removing dummy activity {activityId}");
                    }
                }
            }
            else if (tailNode.NodeType != NodeType.Start &&
                     tailNode.NodeType != NodeType.Isolated &&
                     !tailNode.OutgoingEdges.Any())
            {
                // If the tail node is not the Start node, and it has no more outgoing
                // edges, then transfer the tail node's incoming edges to the head node.
                IList <T> tailNodeIncomingEdgeIds = tailNode.IncomingEdges.ToList();
                foreach (T tailNodeIncomingEdgeId in tailNodeIncomingEdgeIds)
                {
                    bool changeHeadSuccess = ChangeEdgeHeadNode(tailNodeIncomingEdgeId, headNode.Id);
                    if (!changeHeadSuccess)
                    {
                        throw new InvalidOperationException($@"Unable to change head node of edge {tailNodeIncomingEdgeId} to node {headNode.Id} when removing dummy activity {activityId}");
                    }
                }
            }
            return(true);
        }
Esempio n. 17
0
        public override bool AddActivity(TActivity activity, HashSet <T> dependencies)
        {
            if (activity == null)
            {
                throw new ArgumentNullException(nameof(activity));
            }
            if (dependencies is null)
            {
                throw new ArgumentNullException(nameof(dependencies));
            }
            if (EdgeLookup.ContainsKey(activity.Id))
            {
                return(false);
            }
            if (dependencies.Contains(activity.Id))
            {
                return(false);
            }
            // Create a new edge for the activity.
            var edge = new Edge <T, TActivity>(activity);

            EdgeLookup.Add(edge.Id, edge);

            // We expect dependencies at some point.
            if (dependencies.Any())
            {
                // Since we use dummy edges to connect all tail nodes, we can create
                // a new tail node for this edge.
                T   tailEventId = NodeIdGenerator();
                var tailNode    = new Node <T, TEvent>(EventGenerator(tailEventId));
                tailNode.OutgoingEdges.Add(edge.Id);
                EdgeTailNodeLookup.Add(edge.Id, tailNode);
                NodeLookup.Add(tailNode.Id, tailNode);

                // Check which of the expected dependencies currently exist.
                IList <T> existingDependencies    = EdgeLookup.Keys.Intersect(dependencies).ToList();
                IList <T> nonExistingDependencies = dependencies.Except(existingDependencies).ToList();

                // If any expected dependencies currently exist, then hook up their head
                // node to this edge's tail node with dummy edges.
                foreach (T dependencyId in existingDependencies)
                {
                    Node <T, TEvent> dependencyHeadNode = EdgeHeadNodeLookup[dependencyId];
                    T   dummyEdgeId = EdgeIdGenerator();
                    var dummyEdge   = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId));
                    tailNode.IncomingEdges.Add(dummyEdgeId);
                    EdgeHeadNodeLookup.Add(dummyEdgeId, tailNode);

                    // If the head node of the dependency is the End node, then convert it.
                    if (dependencyHeadNode.NodeType == NodeType.End)
                    {
                        dependencyHeadNode.SetNodeType(NodeType.Normal);
                    }

                    dependencyHeadNode.OutgoingEdges.Add(dummyEdgeId);
                    EdgeTailNodeLookup.Add(dummyEdgeId, dependencyHeadNode);
                    EdgeLookup.Add(dummyEdgeId, dummyEdge);
                }

                // If any expected dependencies currently do not exist, then record their
                // IDs and add this edge's tail node as an unsatisfied successor.
                foreach (T dependencyId in nonExistingDependencies)
                {
                    if (!UnsatisfiedSuccessorsLookup.TryGetValue(dependencyId, out HashSet <Node <T, TEvent> > tailNodes))
                    {
                        tailNodes = new HashSet <Node <T, TEvent> >();
                        UnsatisfiedSuccessorsLookup.Add(dependencyId, tailNodes);
                    }
                    tailNodes.Add(tailNode);
                }
            }
            else
            {
                // No dependencies, so attach it directly to the start node.
                StartNode.OutgoingEdges.Add(edge.Id);
                EdgeTailNodeLookup.Add(edge.Id, StartNode);
            }
            ResolveUnsatisfiedSuccessorActivities(edge.Id);
            return(true);
        }
        /// <summary>
        /// Insert a number of new points to each edge. Points are evenly spaced out along the edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="edges">The edges to split with points.</param>
        /// <param name="count">The number of new points to insert. Must be greater than 0.</param>
        /// <returns>The new edges created by inserting points.</returns>
        public static List <Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, IList <Edge> edges, int count)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (edges == null)
            {
                throw new ArgumentNullException("edges");
            }

            if (count < 1 || count > 512)
            {
                Log.Error("New edge vertex count is less than 1 or greater than 512.");
                return(null);
            }

            List <Vertex>         vertices        = new List <Vertex>(mesh.GetVertices());
            Dictionary <int, int> lookup          = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV        = mesh.sharedTextureLookup;
            List <int>            indexesToDelete = new List <int>();
            IEnumerable <Edge>    commonEdges     = EdgeUtility.GetSharedVertexHandleEdges(mesh, edges);
            List <Edge>           distinctEdges   = commonEdges.Distinct().ToList();

            Dictionary <Face, FaceRebuildData> modifiedFaces = new Dictionary <Face, FaceRebuildData>();

            int originalSharedIndexesCount = lookup.Count();
            int sharedIndexesCount         = originalSharedIndexesCount;

            foreach (Edge edge in distinctEdges)
            {
                Edge localEdge = EdgeUtility.GetEdgeWithSharedVertexHandles(mesh, edge);

                // Generate the new vertices that will be inserted on this edge
                List <Vertex> verticesToAppend = new List <Vertex>(count);

                for (int i = 0; i < count; i++)
                {
                    verticesToAppend.Add(Vertex.Mix(vertices[localEdge.a], vertices[localEdge.b],
                                                    (i + 1) / ((float)count + 1)));
                }

                List <SimpleTuple <Face, Edge> > adjacentFaces = ElementSelection.GetNeighborFaces(mesh, localEdge);
                Edge edgeLookUp = new Edge(lookup[localEdge.a], lookup[localEdge.b]);
                Edge e          = new Edge();

                // foreach face attached to common edge, append vertices
                foreach (SimpleTuple <Face, Edge> tup in adjacentFaces)
                {
                    Face face = tup.item1;

                    FaceRebuildData data;

                    if (!modifiedFaces.TryGetValue(face, out data))
                    {
                        data      = new FaceRebuildData();
                        data.face = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv),
                                             face.smoothingGroup, face.textureGroup, -1, face.manualUV);
                        data.vertices =
                            new List <Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
                        data.sharedIndexes   = new List <int>();
                        data.sharedIndexesUV = new List <int>();

                        foreach (int i in face.distinctIndexesInternal)
                        {
                            int shared;

                            if (lookup.TryGetValue(i, out shared))
                            {
                                data.sharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }
                        }

                        indexesToDelete.AddRange(face.distinctIndexesInternal);

                        modifiedFaces.Add(face, data);

                        //Ordering vertices in the new face
                        List <Vertex> orderedVertices        = new List <Vertex>();
                        List <int>    orderedSharedIndexes   = new List <int>();
                        List <int>    orderedSharedUVIndexes = new List <int>();
                        List <Edge>   peripheralEdges        = WingedEdge.SortEdgesByAdjacency(face);

                        for (int i = 0; i < peripheralEdges.Count; i++)
                        {
                            e.a = peripheralEdges[i].a;
                            e.b = peripheralEdges[i].b;

                            orderedVertices.Add(vertices[e.a]);

                            int shared;
                            if (lookup.TryGetValue(e.a, out shared))
                            {
                                orderedSharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }

                            if (edgeLookUp.a == lookup[e.a] && edgeLookUp.b == lookup[e.b])
                            {
                                for (int j = 0; j < count; j++)
                                {
                                    orderedVertices.Add(verticesToAppend[j]);
                                    orderedSharedIndexes.Add(sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                            else if (edgeLookUp.a == lookup[e.b] && edgeLookUp.b == lookup[e.a])
                            {
                                for (int j = count - 1; j >= 0; j--)
                                {
                                    orderedVertices.Add(verticesToAppend[j]);
                                    orderedSharedIndexes.Add(sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                        }

                        data.vertices        = orderedVertices;
                        data.sharedIndexes   = orderedSharedIndexes;
                        data.sharedIndexesUV = orderedSharedUVIndexes;
                    }
                    else
                    {
                        //Get ordered vertices in the existing face and add new ones
                        List <Vertex> orderedVertices        = data.vertices;
                        List <int>    orderedSharedIndexes   = data.sharedIndexes;
                        List <int>    orderedSharedUVIndexes = data.sharedIndexesUV;

                        for (int i = 0; i < orderedVertices.Count; i++)
                        {
                            Vertex edgeStart      = orderedVertices[i];
                            int    edgeStartIndex = vertices.IndexOf(edgeStart);

                            Vertex edgeEnd      = orderedVertices[(i + 1) % orderedVertices.Count];
                            int    edgeEndIndex = vertices.IndexOf(edgeEnd);

                            if (edgeStartIndex == -1 || edgeEndIndex == -1)
                            {
                                continue;
                            }

                            if (lookup[edgeStartIndex] == lookup[localEdge.a] &&
                                lookup[edgeEndIndex] == lookup[localEdge.b])
                            {
                                orderedVertices.InsertRange(i + 1, verticesToAppend);
                                for (int j = 0; j < count; j++)
                                {
                                    orderedSharedIndexes.Insert(i + j + 1, sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                            else if (lookup[edgeStartIndex] == lookup[localEdge.b] &&
                                     lookup[edgeEndIndex] == lookup[localEdge.a])
                            {
                                verticesToAppend.Reverse();
                                orderedVertices.InsertRange(i + 1, verticesToAppend);
                                for (int j = count - 1; j >= 0; j--)
                                {
                                    orderedSharedIndexes.Insert(i + 1, sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                        }

                        data.vertices        = orderedVertices;
                        data.sharedIndexes   = orderedSharedIndexes;
                        data.sharedIndexesUV = orderedSharedUVIndexes;
                    }
                }

                sharedIndexesCount += count;
            }

            // now apply the changes
            List <Face>            dic_face      = modifiedFaces.Keys.ToList();
            List <FaceRebuildData> dic_data      = modifiedFaces.Values.ToList();
            List <EdgeLookup>      appendedEdges = new List <EdgeLookup>();

            for (int i = 0; i < dic_face.Count; i++)
            {
                Face            face = dic_face[i];
                FaceRebuildData data = dic_data[i];

                int vertexCount = vertices.Count;
                // triangulate and set new face indexes to end of current vertex list
                List <int> triangles;

                if (Triangulation.TriangulateVertices(data.vertices, out triangles, false))
                {
                    data.face = new Face(triangles);
                }
                else
                {
                    continue;
                }

                data.face.ShiftIndexes(vertexCount);
                face.CopyFrom(data.face);

                for (int n = 0; n < data.vertices.Count; n++)
                {
                    lookup.Add(vertexCount + n, data.sharedIndexes[n]);
                }

                if (data.sharedIndexesUV.Count == data.vertices.Count)
                {
                    for (int n = 0; n < data.vertices.Count; n++)
                    {
                        lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
                    }
                }

                vertices.AddRange(data.vertices);

                foreach (Edge e in face.edgesInternal)
                {
                    EdgeLookup el = new EdgeLookup(new Edge(lookup[e.a], lookup[e.b]), e);

                    if (el.common.a >= originalSharedIndexesCount || el.common.b >= originalSharedIndexesCount)
                    {
                        appendedEdges.Add(el);
                    }
                }
            }

            indexesToDelete = indexesToDelete.Distinct().ToList();
            int delCount = indexesToDelete.Count;

            var newEdges = appendedEdges.Distinct().Select(x => x.local - delCount).ToList();

            mesh.SetVertices(vertices);
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);
            mesh.DeleteVertices(indexesToDelete);

            return(newEdges);
        }
Esempio n. 19
0
        /// <summary>
        /// Apply a bevel to a set of edges.
        /// </summary>
        /// <param name="mesh">Target mesh.</param>
        /// <param name="edges">A set of edges to apply bevelling to.</param>
        /// <param name="amount">A value from 0 (bevel not at all) to 1 (bevel entire face).</param>
        /// <returns>The new faces created to form the bevel.</returns>
        public static List <Face> BevelEdges(ProBuilderMesh mesh, IList <Edge> edges, float amount)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            Dictionary <int, int>  lookup      = mesh.sharedVertexLookup;
            List <Vertex>          vertices    = new List <Vertex>(mesh.GetVertices());
            List <EdgeLookup>      m_edges     = EdgeLookup.GetEdgeLookup(edges, lookup).Distinct().ToList();
            List <WingedEdge>      wings       = WingedEdge.GetWingedEdges(mesh);
            List <FaceRebuildData> appendFaces = new List <FaceRebuildData>();

            Dictionary <Face, List <int> > ignore = new Dictionary <Face, List <int> >();
            HashSet <int> slide   = new HashSet <int>();
            int           beveled = 0;

            Dictionary <int, List <SimpleTuple <FaceRebuildData, List <int> > > > holes = new Dictionary <int, List <SimpleTuple <FaceRebuildData, List <int> > > >();

            // test every edge that will be moved along to make sure the bevel distance is appropriate.  if it's not, adjust the max bevel amount
            // to suit.
            Dictionary <int, List <WingedEdge> > spokes = WingedEdge.GetSpokes(wings);
            HashSet <int> tested_common = new HashSet <int>();

            foreach (EdgeLookup e in m_edges)
            {
                if (tested_common.Add(e.common.a))
                {
                    foreach (WingedEdge w in spokes[e.common.a])
                    {
                        Edge le = w.edge.local;
                        amount = Mathf.Min(Vector3.Distance(vertices[le.a].position, vertices[le.b].position) - .001f, amount);
                    }
                }

                if (tested_common.Add(e.common.b))
                {
                    foreach (WingedEdge w in spokes[e.common.b])
                    {
                        Edge le = w.edge.local;
                        amount = Mathf.Min(Vector3.Distance(vertices[le.a].position, vertices[le.b].position) - .001f, amount);
                    }
                }
            }

            if (amount < .001f)
            {
                Log.Info("Bevel Distance > Available Surface");
                return(null);
            }

            // iterate selected edges and move each leading edge back along it's direction
            // storing information about adjacent faces in the process
            foreach (EdgeLookup lup in m_edges)
            {
                WingedEdge we = wings.FirstOrDefault(x => x.edge.Equals(lup));

                if (we == null || we.opposite == null)
                {
                    continue;
                }

                beveled++;

                ignore.AddOrAppend(we.face, we.edge.common.a);
                ignore.AddOrAppend(we.face, we.edge.common.b);
                ignore.AddOrAppend(we.opposite.face, we.edge.common.a);
                ignore.AddOrAppend(we.opposite.face, we.edge.common.b);

                // after initial slides go back and split indirect triangles at the intersecting index into two vertices
                slide.Add(we.edge.common.a);
                slide.Add(we.edge.common.b);

                SlideEdge(vertices, we, amount);
                SlideEdge(vertices, we.opposite, amount);

                appendFaces.AddRange(GetBridgeFaces(vertices, we, we.opposite, holes));
            }

            if (beveled < 1)
            {
                Log.Info("Cannot Bevel Open Edges");
                return(null);
            }

            // grab the "createdFaces" array now so that the selection returned is just the bridged faces
            // then add holes later
            var createdFaces = new List <Face>(appendFaces.Select(x => x.face));

            Dictionary <Face, List <SimpleTuple <WingedEdge, int> > > sorted = new Dictionary <Face, List <SimpleTuple <WingedEdge, int> > >();

            // sort the adjacent but affected faces into winged edge groups where each group contains a set of
            // unique winged edges pointing to the same face
            foreach (int c in slide)
            {
                IEnumerable <WingedEdge> matches = wings.Where(x => x.edge.common.Contains(c) && !(ignore.ContainsKey(x.face) && ignore[x.face].Contains(c)));

                HashSet <Face> used = new HashSet <Face>();

                foreach (WingedEdge match in matches)
                {
                    if (!used.Add(match.face))
                    {
                        continue;
                    }

                    sorted.AddOrAppend(match.face, new SimpleTuple <WingedEdge, int>(match, c));
                }
            }

            // now go through those sorted faces and apply the vertex exploding, keeping track of any holes created
            foreach (KeyValuePair <Face, List <SimpleTuple <WingedEdge, int> > > kvp in sorted)
            {
                // common index & list of vertices it was split into
                Dictionary <int, List <int> > appended;

                FaceRebuildData f = VertexEditing.ExplodeVertex(vertices, kvp.Value, amount, out appended);

                if (f == null)
                {
                    continue;
                }

                appendFaces.Add(f);

                foreach (var apv in appended)
                {
                    // organize holes by new face so that later we can compare the winding of the new face to the hole face
                    // holes are sorted by key: common index value: face, vertex list
                    holes.AddOrAppend(apv.Key, new SimpleTuple <FaceRebuildData, List <int> >(f, apv.Value));
                }
            }

            FaceRebuildData.Apply(appendFaces, mesh, vertices);
            int removed = mesh.DeleteFaces(sorted.Keys).Length;

            mesh.sharedTextures = new SharedVertex[0];
            mesh.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(mesh.positionsInternal);

            // @todo don't rebuild indexes, keep 'em cached
            SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
            lookup = mesh.sharedVertexLookup;
            List <HashSet <int> > holesCommonIndexes = new List <HashSet <int> >();

            // offset the indexes of holes and cull any potential holes that are less than 3 indexes (not a hole :)
            foreach (KeyValuePair <int, List <SimpleTuple <FaceRebuildData, List <int> > > > hole in holes)
            {
                // less than 3 indexes in hole path; ain't a hole
                if (hole.Value.Sum(x => x.item2.Count) < 3)
                {
                    continue;
                }

                HashSet <int> holeCommon = new HashSet <int>();

                foreach (SimpleTuple <FaceRebuildData, List <int> > path in hole.Value)
                {
                    int offset = path.item1.Offset() - removed;

                    for (int i = 0; i < path.item2.Count; i++)
                    {
                        holeCommon.Add(lookup[path.item2[i] + offset]);
                    }
                }

                holesCommonIndexes.Add(holeCommon);
            }

            List <WingedEdge> modified = WingedEdge.GetWingedEdges(mesh, appendFaces.Select(x => x.face));

            // now go through the holes and create faces for them
            vertices = new List <Vertex>(mesh.GetVertices());

            List <FaceRebuildData> holeFaces = new List <FaceRebuildData>();

            foreach (HashSet <int> h in holesCommonIndexes)
            {
                // even if a set of hole indexes made it past the initial culling, the distinct part
                // may have reduced the index count
                if (h.Count < 3)
                {
                    continue;
                }
                // skip sorting the path if it's just a triangle
                if (h.Count < 4)
                {
                    List <Vertex> v = new List <Vertex>(mesh.GetVertices(h.Select(x => sharedIndexes[x][0]).ToList()));
                    holeFaces.Add(AppendElements.FaceWithVertices(v));
                }
                // if this hole has > 3 indexes, it needs a tent pole triangulation, which requires sorting into the perimeter order
                else
                {
                    List <int>    holePath = WingedEdge.SortCommonIndexesByAdjacency(modified, h);
                    List <Vertex> v        = new List <Vertex>(mesh.GetVertices(holePath.Select(x => sharedIndexes[x][0]).ToList()));
                    holeFaces.AddRange(AppendElements.TentCapWithVertices(v));
                }
            }

            FaceRebuildData.Apply(holeFaces, mesh, vertices);
            mesh.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(mesh.positionsInternal);

            // go through new faces and conform hole normals
            // get a hash of just the adjacent and bridge faces
            // HashSet<pb_Face> adjacent = new HashSet<pb_Face>(appendFaces.Select(x => x.face));
            // and also just the filled holes
            HashSet <Face> newHoles = new HashSet <Face>(holeFaces.Select(x => x.face));

            // now append filled holes to the full list of added faces
            appendFaces.AddRange(holeFaces);

            List <WingedEdge> allNewFaceEdges = WingedEdge.GetWingedEdges(mesh, appendFaces.Select(x => x.face));

            for (int i = 0; i < allNewFaceEdges.Count && newHoles.Count > 0; i++)
            {
                WingedEdge wing = allNewFaceEdges[i];

                if (newHoles.Contains(wing.face))
                {
                    newHoles.Remove(wing.face);

                    // find first edge whose opposite face isn't a filled hole* then
                    // conform normal by that.
                    // *or is a filled hole but has already been conformed
                    using (var it = new WingedEdgeEnumerator(wing))
                    {
                        while (it.MoveNext())
                        {
                            var w = it.Current;

                            if (!newHoles.Contains(w.opposite.face))
                            {
                                w.face.submeshIndex = w.opposite.face.submeshIndex;
                                w.face.uv           = new AutoUnwrapSettings(w.opposite.face.uv);
                                SurfaceTopology.ConformOppositeNormal(w.opposite);
                                break;
                            }
                        }
                    }
                }
            }

            mesh.ToMesh();

            return(createdFaces);
        }
Esempio n. 20
0
        public override bool RemoveActivity(T activityId)
        {
            // Retrieve the activity's node.
            if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node))
            {
                return(false);
            }
            if (!node.Content.CanBeRemoved)
            {
                return(false);
            }

            RemoveUnsatisfiedSuccessorActivity(activityId);
            NodeLookup.Remove(node.Id);

            if (node.NodeType == NodeType.Isolated)
            {
                return(true);
            }

            if (node.NodeType == NodeType.End ||
                node.NodeType == NodeType.Normal)
            {
                IList <T> incomingEdgeIds = node.IncomingEdges.ToList();
                int       length          = incomingEdgeIds.Count;
                for (int i = 0; i < length; i++)
                {
                    T edgeId = incomingEdgeIds[i];
                    Node <T, TActivity> tailNode = EdgeTailNodeLookup[edgeId];

                    // Remove the edge from the tail node.
                    tailNode.OutgoingEdges.Remove(edgeId);
                    EdgeTailNodeLookup.Remove(edgeId);

                    if (!tailNode.OutgoingEdges.Any())
                    {
                        if (tailNode.NodeType == NodeType.Normal)
                        {
                            tailNode.SetNodeType(NodeType.End);
                        }
                        else if (tailNode.NodeType == NodeType.Start)
                        {
                            tailNode.SetNodeType(NodeType.Isolated);
                        }
                    }

                    // Remove the edge from the head node.
                    node.IncomingEdges.Remove(edgeId);
                    EdgeHeadNodeLookup.Remove(edgeId);

                    if (!node.IncomingEdges.Any())
                    {
                        if (node.NodeType == NodeType.Normal)
                        {
                            node.SetNodeType(NodeType.Start);
                        }
                        else if (node.NodeType == NodeType.End)
                        {
                            node.SetNodeType(NodeType.Isolated);
                        }
                    }

                    // Remove the edge completely.
                    EdgeLookup.Remove(edgeId);
                }
            }

            if (node.NodeType == NodeType.Start ||
                node.NodeType == NodeType.Normal)
            {
                IList <T> outgoingEdgeIds = node.OutgoingEdges.ToList();
                int       length          = outgoingEdgeIds.Count;
                for (int i = 0; i < length; i++)
                {
                    T edgeId = outgoingEdgeIds[i];
                    Node <T, TActivity> headNode = EdgeHeadNodeLookup[edgeId];

                    // Remove the edge from the head node.
                    headNode.IncomingEdges.Remove(edgeId);
                    EdgeHeadNodeLookup.Remove(edgeId);

                    if (!headNode.IncomingEdges.Any())
                    {
                        if (headNode.NodeType == NodeType.Normal)
                        {
                            headNode.SetNodeType(NodeType.Start);
                        }
                        else if (headNode.NodeType == NodeType.End)
                        {
                            headNode.SetNodeType(NodeType.Isolated);
                        }
                    }

                    // Remove the edge from the tail node.
                    node.OutgoingEdges.Remove(edgeId);
                    EdgeTailNodeLookup.Remove(edgeId);

                    if (!node.OutgoingEdges.Any())
                    {
                        if (node.NodeType == NodeType.Normal)
                        {
                            node.SetNodeType(NodeType.End);
                        }
                        else if (node.NodeType == NodeType.Start)
                        {
                            node.SetNodeType(NodeType.Isolated);
                        }
                    }

                    // Remove the edge completely.
                    EdgeLookup.Remove(edgeId);
                }
            }
            return(true);
        }