protected override void OptimizeAfterSequencing(IComparer <object> inEdgeOrder, IComparer <object> outEdgeOrder, LayoutGraph graph, ILayers layers, ILayoutDataProvider ldp, IItemFactory itemFactory) { edge2LaneCrossing = Maps.CreateHashedEdgeMap(); node2LaneAlignment = Maps.CreateHashedNodeMap(); var criticalEdges = Maps.CreateHashedEdgeMap(); // determine whether an edge crosses a swim lane border and if so in which direction foreach (var edge in graph.Edges) { var originalEdge = GetOriginalEdge(edge, ldp); // now we have a 'real' edge with valid valid source and target nodes var originalSourceId = GetLaneId(originalEdge.Source, ldp); var originalTargetId = GetLaneId(originalEdge.Target, ldp); LaneCrossing crossing = LaneCrossing.None; if (originalSourceId != originalTargetId) { // check if we need to flip the sides because edge and original edge have different directions var flipSides = edge.Source != originalEdge.Source; var sourceId = flipSides ? originalTargetId : originalSourceId; var targetId = flipSides ? originalSourceId : originalTargetId; crossing = sourceId > targetId ? LaneCrossing.ToWest : LaneCrossing.ToEast; } edge2LaneCrossing.Set(edge, crossing); } // determine basic node alignment foreach (var n in graph.Nodes) { LaneAlignment alignment = CalculateLaneAlignment(n); node2LaneAlignment.Set(n, alignment); } foreach (var n in graph.Nodes) { // sort the edges with the provided comparer n.SortInEdges(inEdgeOrder); n.SortOutEdges(outEdgeOrder); // calculate 'critical' in and out-edges whose nodes should be aligned in flow var bestInEdge = n.InDegree > 0 ? GetBestFlowEdge(n.InEdges, ldp, graph) : null; var bestOutEdge = n.OutDegree > 0 ? GetBestFlowEdge(n.OutEdges, ldp, graph) : null; if (bestInEdge != null) { criticalEdges.SetDouble(bestInEdge, criticalEdges.GetDouble(bestInEdge) + 0.5); } if (bestOutEdge != null) { criticalEdges.SetDouble(bestOutEdge, criticalEdges.GetDouble(bestOutEdge) + 0.5); } if (n.Degree <= 4) { // should usually be the case and we can distribute each edge to its own side // remember which node side is already taken by an in- or out-edge bool westTakenByInEdge = false; bool eastTakenByInEdge = false; bool westTakenByOutEdge = false; bool eastTakenByOutEdge = false; if (n.InDegree > 0 && n.OutDegree < 3) { // if there are at least three out-edges, we distribute those first, otherwise we start with the in-edges var firstInEdge = n.FirstInEdge; var lastInEdge = n.LastInEdge; if (GetLaneCrossing(firstInEdge) == LaneCrossing.ToEast && (n.InDegree > 1 || IsSameLayerEdge(firstInEdge, ldp))) { // the first in-edge comes from west and is either a same layer edge or there are other in-edges ConstrainWest(firstInEdge, false, itemFactory); westTakenByInEdge = true; } if (!westTakenByInEdge || n.OutDegree < 2) { // don't use west and east side for in-edges if there are at least 2 out-edges if (GetLaneCrossing(lastInEdge) == LaneCrossing.ToWest && (n.InDegree > 1 || IsSameLayerEdge(lastInEdge, ldp))) { // the last in-edge comes from east and is either // a same-layer edge or there are other in-edges ConstrainEast(lastInEdge, false, itemFactory); eastTakenByInEdge = true; } } } if (n.OutDegree > 0) { var firstOutEdge = n.FirstOutEdge; var lastOutEdge = n.LastOutEdge; if (!westTakenByInEdge) { // the west side is still free if (BpmnLayout.IsBoundaryInterrupting(firstOutEdge, graph) || (GetLaneCrossing(firstOutEdge) == LaneCrossing.ToWest) && (n.OutDegree > 1 || IsSameLayerEdge(firstOutEdge, ldp))) { // the first out-edge is either boundary interrupting or goes to west and // is either a same layer edge or there are other out-edges ConstrainWest(firstOutEdge, true, itemFactory); westTakenByOutEdge = true; } else if (eastTakenByInEdge && n.OutDegree >= 2 && !IsSameLayerEdge(firstOutEdge.NextOutEdge, ldp)) { // the east side is already taken but we have more then one out edge. // if the second out edge is a same layer edge, constraining the firstOutEdge could lead to // no in-flow edge ConstrainWest(firstOutEdge, true, itemFactory); westTakenByOutEdge = true; } } if (!eastTakenByInEdge) { // the east side is still free if (GetLaneCrossing(lastOutEdge) == LaneCrossing.ToEast && (n.OutDegree > 1 || IsSameLayerEdge(lastOutEdge, ldp))) { // the last out-edge goes to east and // is either a same layer edge or there are other out-edges ConstrainEast(lastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } else if (westTakenByInEdge && n.OutDegree >= 2 && !IsSameLayerEdge(lastOutEdge.PrevOutEdge, ldp)) { // the west side is already taken but we have more then one out edge. // if the second last out edge is a same layer edge, constraining the lastOutEdge could lead to // no in-flow edge ConstrainEast(lastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } } } // distribute remaining in-edges if (n.InDegree == 2 && !(eastTakenByInEdge || westTakenByInEdge)) { // two in-edges but none distributed, yet if (bestInEdge == n.FirstInEdge && !eastTakenByOutEdge) { // first in-edge is in-flow edge and east side is still free ConstrainEast(n.LastInEdge, false, itemFactory); eastTakenByInEdge = true; } else if (bestInEdge == n.LastInEdge && !westTakenByOutEdge) { // last in-edge is in-flow edge and west side is still free ConstrainWest(n.FirstInEdge, false, itemFactory); westTakenByInEdge = true; } } else if (n.InDegree == 3 && !(eastTakenByInEdge && westTakenByInEdge) && !(IsSameLayerEdge(n.FirstInEdge.NextInEdge, ldp))) { // three in-edges but not both sides taken, yet and the middle edge is no same layer edge if (!eastTakenByOutEdge) { // if not already taken, constraint the last in-edge to east ConstrainEast(n.LastInEdge, false, itemFactory); eastTakenByInEdge = true; } if (!westTakenByOutEdge) { // if not already taken, constraint the first in-edge to west ConstrainWest(n.FirstInEdge, false, itemFactory); westTakenByInEdge = true; } } // distribute remaining out-edges if (n.OutDegree == 2 && !(eastTakenByOutEdge || westTakenByOutEdge)) { // two out-edges but none distributed, yet if (bestOutEdge == n.FirstOutEdge && !eastTakenByInEdge) { // first out-edge is in-flow edge and east side is still free ConstrainEast(n.LastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } else if (bestOutEdge == n.LastOutEdge && !westTakenByInEdge) { // last out-edge is in-flow edge and west side is still free ConstrainWest(n.FirstOutEdge, true, itemFactory); westTakenByOutEdge = true; } } else if (n.OutDegree == 3 && !(eastTakenByOutEdge && westTakenByOutEdge) && !(IsSameLayerEdge(n.FirstOutEdge.NextOutEdge, ldp))) { // three out-edges but not both sides taken, yet and the middle edge is no same layer edge if (!eastTakenByInEdge) { // if not already taken, constraint the last out-edge to east ConstrainEast(n.LastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } if (!westTakenByInEdge) { // if not already taken, constraint the first out-edge to west ConstrainWest(n.FirstOutEdge, true, itemFactory); westTakenByOutEdge = true; } } } } // register the data provider for critical edge paths. It is deregistered again by BpmnLayout itself graph.AddDataProvider(HierarchicLayout.CriticalEdgePriorityDpKey, criticalEdges); sameLayerData = null; edge2LaneCrossing = null; node2LaneAlignment = null; }
public static void Main() { DefaultLayoutGraph graph = new DefaultLayoutGraph(); //construct graph. assign sizes to nodes Node v1 = graph.CreateNode(); graph.SetSize(v1, 30, 30); Node v2 = graph.CreateNode(); graph.SetSize(v2, 30, 30); Node v3 = graph.CreateNode(); graph.SetSize(v3, 30, 30); // add a label to one node var nodeLabelLayoutModel = new DiscreteNodeLabelLayoutModel(DiscreteNodeLabelPositions.InternalMask, 4); var labelLayoutFactory = LayoutGraphUtilities.GetLabelFactory(graph); labelLayoutFactory.AddLabelLayout(v1, labelLayoutFactory.CreateLabelLayout(v1, new YOrientedRectangle(0, 0, 80, 20), nodeLabelLayoutModel)); Edge e1 = graph.CreateEdge(v1, v2); Edge e2 = graph.CreateEdge(v1, v3); // add a label to an edge var edgeLabelLayoutModel = new SliderEdgeLabelLayoutModel(SliderMode.Side); labelLayoutFactory.AddLabelLayout(e1, labelLayoutFactory.CreateLabelLayout(e1, new YOrientedRectangle(0, 0, 80, 20), edgeLabelLayoutModel, PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.LeftOfEdge))); //optionally setup some port constraints for HierarchicLayout IEdgeMap spc = graph.CreateEdgeMap(); IEdgeMap tpc = graph.CreateEdgeMap(); //e1 shall leave and enter the node on the right side spc.Set(e1, PortConstraint.Create(PortSide.East, false)); //additionally set a strong port constraint on the target side. tpc.Set(e1, PortConstraint.Create(PortSide.East, true)); //ports with strong port constraints will not be reset by the //layout algorithm. So we specify the target port right now to connect //to the upper left corner of the node graph.SetTargetPointRel(e1, new YPoint(15, -15)); //e2 shall leave and enter the node on the top side spc.Set(e2, PortConstraint.Create(PortSide.North, false)); tpc.Set(e2, PortConstraint.Create(PortSide.North, false)); graph.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, spc); graph.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, tpc); HierarchicLayout layout = new HierarchicLayout(); layout.IntegratedEdgeLabeling = true; layout.ConsiderNodeLabels = true; layout.LayoutMode = LayoutMode.FromScratch; new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT HIERARCHICALLY FROM SCRATCH"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); //display the graph in a simple viewer GraphViewer gv = new GraphViewer(); gv.AddLayoutGraph(new CopiedLayoutGraph(graph), "Before Addition"); // now add a node and two edges incrementally... Node v4 = graph.CreateNode(); graph.SetSize(v4, 30, 30); Edge e4 = graph.CreateEdge(v4, v2); Edge e3 = graph.CreateEdge(v1, v4); //mark elements as newly added so that the layout algorithm can place //them nicely. IIncrementalHintsFactory ihf = layout.CreateIncrementalHintsFactory(); IDataMap map = Maps.CreateHashedDataMap(); map.Set(v4, ihf.CreateLayerIncrementallyHint(v4)); map.Set(e3, ihf.CreateSequenceIncrementallyHint(e3)); map.Set(e4, ihf.CreateSequenceIncrementallyHint(e4)); graph.AddDataProvider(HierarchicLayout.IncrementalHintsDpKey, map); layout.LayoutMode = LayoutMode.Incremental; new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH AFTER ELEMENTS HAVE BEEN ADDED INCREMENTALLY"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("v4 center position = " + graph.GetCenter(v4)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("e4 path = " + graph.GetPath(e4)); //clean up data maps graph.RemoveDataProvider(HierarchicLayout.IncrementalHintsDpKey); //display the graph in a simple viewer gv.AddLayoutGraph(new CopiedLayoutGraph(graph), "After Addition"); Application.Run(gv); }
public static void Main() { DefaultLayoutGraph graph = new DefaultLayoutGraph(); //construct graph. assign sizes to nodes Node v1 = graph.CreateNode(); graph.SetSize(v1, 30, 30); Node v2 = graph.CreateNode(); graph.SetSize(v2, 30, 30); Node v3 = graph.CreateNode(); graph.SetSize(v3, 30, 30); Edge e1 = graph.CreateEdge(v1, v2); Edge e2 = graph.CreateEdge(v2, v3); Edge e3 = graph.CreateEdge(v1, v3); //optionally setup some port constraints for HierarchicLayout IEdgeMap spc = graph.CreateEdgeMap(); IEdgeMap tpc = graph.CreateEdgeMap(); //e1 shall leave and enter the node on the right side spc.Set(e1, PortConstraint.Create(PortSide.East)); //additionally set a strong port constraint on the target side. tpc.Set(e1, PortConstraint.Create(PortSide.East, true)); //ports with strong port constraints will not be reset by the //layout algorithm. So we specify the target port right now to connect //to the upper left corner of the node graph.SetTargetPointRel(e1, new YPoint(15, -15)); //e2 shall leave and enter the node on the top side spc.Set(e2, PortConstraint.Create(PortSide.North)); tpc.Set(e2, PortConstraint.Create(PortSide.North)); //e3 uses no port constraints, i.e. layout will choose best side graph.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, spc); graph.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, tpc); //setup two edge labels for edge e1. The size of the edge labels will be set to //80x20. Usually the size of the labels will be determined by //calculaing the bounding box of a piece text that is displayed //with a specific font. var labelFactory = LayoutGraphUtilities.GetLabelFactory(graph); graph.SetLabelLayout(e1, new[] { CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Center), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)), CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Side), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.LeftOfEdge)) }); var layout = new HierarchicLayout(); layout.LabelingEnabled = true; layout.Labeling = new GenericLabeling(); new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT USING GENERIC EDGE LABELING"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("ell1 upper left location = " + GetEdgeLabelLocation(graph, e1, CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Center), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)))); Console.WriteLine("ell2 upper left location = " + GetEdgeLabelLocation(graph, e1, graph.GetLabelLayout(e1)[1])); GraphViewer gv = new GraphViewer(); gv.AddLayoutGraph(new CopiedLayoutGraph(graph), "Layout with Generic Labeling"); var freeModel = new FreeEdgeLabelLayoutModel(); graph.SetLabelLayout(e1, new[] { CreateEdgeLabelLayout(labelFactory, e1, freeModel, PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)), CreateEdgeLabelLayout(labelFactory, e1, freeModel, PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.LeftOfEdge)) }); layout.LabelingEnabled = true; layout.Labeling = new LabelLayoutTranslator(); new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT USING HIERACHIC LAYOUT SPECIFIC EDGE LABELING"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("ell1 upper left location = " + GetEdgeLabelLocation(graph, e1, CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Center), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)))); Console.WriteLine("ell2 upper left location = " + GetEdgeLabelLocation(graph, e1, graph.GetLabelLayout(e1)[1])); //display the result in a simple viewer gv.AddLayoutGraph(graph, "Layout with Integrated Labeling"); var application = new System.Windows.Application(); application.Run(gv); }
/// <summary> /// Creates a small graph and applies a hierarchic group layout to it. /// The output of the calculated coordinates will be displayed in the /// console. /// </summary> public void Run() { DefaultLayoutGraph graph = new DefaultLayoutGraph(); //construct graph. assign sizes to nodes Node v1 = graph.CreateNode(); graph.SetSize(v1, 30, 30); Node v2 = graph.CreateNode(); graph.SetSize(v2, 30, 30); Node v3 = graph.CreateNode(); graph.SetSize(v3, 30, 30); Node v4 = graph.CreateNode(); graph.SetSize(v4, 30, 30); Node groupNode = graph.CreateNode(); graph.SetSize(groupNode, 100, 100); Edge e1 = graph.CreateEdge(v1, v2); Edge e2 = graph.CreateEdge(v4, groupNode); Edge e3 = graph.CreateEdge(v1, v3); Edge e4 = graph.CreateEdge(v1, v1); Edge e5 = graph.CreateEdge(v2, groupNode); Edge e6 = graph.CreateEdge(groupNode, v2); //optionally setup some edge groups IEdgeMap spg = graph.CreateEdgeMap(); IEdgeMap tpg = graph.CreateEdgeMap(); graph.AddDataProvider(PortConstraintKeys.SourceGroupIdDpKey, spg); graph.AddDataProvider(PortConstraintKeys.TargetGroupIdDpKey, tpg); spg.Set(e1, "SGroup1"); spg.Set(e3, "SGroup1"); tpg.Set(e1, "TGroup1"); tpg.Set(e3, "TGroup1"); //optionally setup the node grouping INodeMap nodeId = graph.CreateNodeMap(); INodeMap parentNodeId = graph.CreateNodeMap(); INodeMap groupKey = graph.CreateNodeMap(); graph.AddDataProvider(GroupingKeys.NodeIdDpKey, nodeId); graph.AddDataProvider(GroupingKeys.ParentNodeIdDpKey, parentNodeId); graph.AddDataProvider(GroupingKeys.GroupDpKey, groupKey); //mark a node as a group node groupKey.SetBool(groupNode, true); // add ids for each node nodeId.Set(v1, "v1"); nodeId.Set(v2, "v2"); nodeId.Set(v3, "v3"); nodeId.Set(v4, "v4"); nodeId.Set(groupNode, "groupNode"); // set the parent for each grouped node parentNodeId.Set(v2, "groupNode"); parentNodeId.Set(v3, "groupNode"); HierarchicLayout layout = new HierarchicLayout(); layout.MinimumLayerDistance = 0; layout.EdgeLayoutDescriptor.MinimumDistance = 10; new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT USING HIERARCHICLAYOUT"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("v4 center position = " + graph.GetCenter(v4)); Console.WriteLine("group center position = " + graph.GetCenter(groupNode)); Console.WriteLine("group size = " + graph.GetSize(groupNode)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("e4 path = " + graph.GetPath(e4)); Console.WriteLine("e5 path = " + graph.GetPath(e5)); Console.WriteLine("e6 path = " + graph.GetPath(e4)); //display the result in a simple viewer Application.Run(new Demo.yWorks.LayoutGraphViewer.GraphViewer(graph, "Hierarchical Group Layout")); }
public static void Main() { //instantiates an empty graph yWorks.Algorithms.Graph graph = new yWorks.Algorithms.Graph(); //create a temporary node array for fast lookup Node[] tmpNodes = new Node[5]; //create some nodes in the graph and store references in the array for (int i = 0; i < 5; i++) { tmpNodes[i] = graph.CreateNode(); } //create some edges in the graph for (int i = 0; i < 5; i++) { for (int j = i + 1; j < 5; j++) { //create an edge from node at index i to node at index j graph.CreateEdge(tmpNodes[i], tmpNodes[j]); } } //output the nodes of the graph Console.WriteLine("The nodes of the graph"); for (INodeCursor nc = graph.GetNodeCursor(); nc.Ok; nc.Next()) { Node node = nc.Node; Console.WriteLine(node); Console.WriteLine("in edges #" + node.InDegree); for (IEdgeCursor ec = node.GetInEdgeCursor(); ec.Ok; ec.Next()) { Console.WriteLine(ec.Edge); } Console.WriteLine("out edges #" + node.OutDegree); for (IEdgeCursor ec = node.GetOutEdgeCursor(); ec.Ok; ec.Next()) { Console.WriteLine(ec.Edge); } } //output the edges of the graph Console.WriteLine("\nThe edges of the graph"); for (IEdgeCursor ec = graph.GetEdgeCursor(); ec.Ok; ec.Next()) { Console.WriteLine(ec.Edge); } //reverse edges that have consecutive neighbors in graph //reversing means switching source and target node for (IEdgeCursor ec = graph.GetEdgeCursor(); ec.Ok; ec.Next()) { if (Math.Abs(ec.Edge.Source.Index - ec.Edge.Target.Index) == 1) { graph.ReverseEdge(ec.Edge); } } Console.WriteLine("\nthe edges of the graph after some edge reversal"); for (IEdgeCursor ec = graph.GetEdgeCursor(); ec.Ok; ec.Next()) { Console.WriteLine(ec.Edge); } /////////////////////////////////////////////////////////////////////////// // Node- and EdgeMap handling /////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// //create a nodemap for the graph INodeMap nodeMap = graph.CreateNodeMap(); foreach (var node in graph.Nodes) { //associate descriptive String to the node via a nodemap nodeMap.Set(node, "this is node " + node.Index); } //create an edgemap for the graph IEdgeMap edgeMap = graph.CreateEdgeMap(); foreach (var edge in graph.Edges) { //associate descriptive String to the edge via an edgemap edgeMap.Set(edge, "this is edge [" + nodeMap.Get(edge.Source) + "," + nodeMap.Get(edge.Target) + "]"); } //output the nodemap values of the nodes Console.WriteLine("\nThe node map values of the graph"); foreach (var node in graph.Nodes) { Console.WriteLine(nodeMap.Get(node)); } //output the edges of the graph Console.WriteLine("\nThe edge map values of the graph"); foreach (var edge in graph.Edges) { Console.WriteLine(edgeMap.Get(edge)); } //cleanup unneeded node and edge maps again (free resources) graph.DisposeNodeMap(nodeMap); graph.DisposeEdgeMap(edgeMap); /////////////////////////////////////////////////////////////////////////// // removing elements from the graph ////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// for (INodeCursor nc = graph.GetNodeCursor(); nc.Ok; nc.Next()) { //remove node that has a edge degree > 2 if (nc.Node.Degree > 2) { //removed the node and all of its adjacent edges from the graph graph.RemoveNode(nc.Node); } } Console.WriteLine("\ngraph after some node removal"); Console.WriteLine(graph); Console.WriteLine("\nPress key to end demo."); Console.ReadKey(); }