/// <summary> /// Checks if a given graph is planar and provides a planar embedding if so. /// </summary> /// <param name="graph">Graph</param> /// <param name="embedding">Planar embedding if a given graph is planar, null otherwise</param> /// <returns>True if planar, false otherwise</returns> public bool IsPlanar(IGraph <T> graph, out PlanarEmbedding <T> embedding) { // Transforms input graph TransformGraph(graph); // Init helper collections selfLoopsNew = new List <IEdge <Vertex <T> > >(); mergeStackNew = new Stack <MergeInfo>(); // Use DFS traversal to add basic information to each of the vertices var visitor = new DFSTraversalVisitor <T>(); DFSTraversal.Traverse(transformedGraph, visitor); // Sort vertices by dfs number ASC verticesByDFSNumberNew = BucketSort.Sort(transformedGraph.Vertices, x => x.DFSNumber, transformedGraph.VerticesCount); // Sort vertices by low point ASC verticesByLowPointNew = BucketSort.Sort(transformedGraph.Vertices, x => x.LowPoint, transformedGraph.VerticesCount); // Init vertex fields foreach (var vertex in transformedGraph.Vertices) { vertex.BackEdges = new List <IEdge <Vertex <T> > >(); vertex.Visited = int.MaxValue; vertex.BackedgeFlag = transformedGraph.VerticesCount + 1; vertex.Flipped = false; var dfsParent = vertex.Parent; if (vertex != dfsParent) { var parentEdge = vertex.DFSEdge; vertex.FaceHandle = new FaceHandle <Vertex <T> >(vertex, parentEdge); vertex.DFSChildHandle = new FaceHandle <Vertex <T> >(dfsParent, parentEdge); } else { vertex.FaceHandle = new FaceHandle <Vertex <T> >(vertex, (Vertex <T>)null); // TODO: change vertex.DFSChildHandle = new FaceHandle <Vertex <T> >(dfsParent, (Vertex <T>)null); } vertex.CanonicalDFSChild = vertex; vertex.PertinentRoots = new LinkedList <FaceHandle <Vertex <T> > >(); vertex.SeparatedDFSChildList = new LinkedList <Vertex <T> >(); } // Init separated dfs child lists // // Original Boost comment: // We need to create a list of not-yet-merged depth-first children for // each vertex that will be updated as bicomps get merged. We sort each // list by ascending lowpoint, which allows the externally_active // function to run in constant time, and we keep a pointer to each // vertex's representation in its parent's list, which allows merging //in constant time. foreach (var vertex in verticesByLowPointNew) { var dfsParent = vertex.Parent; if (vertex != dfsParent) { var node = dfsParent.SeparatedDFSChildList.AddLast(vertex); vertex.SeparatedNodeInParentList = node; } } // Call the main algorithm var isPlanar = IsPlanar(); if (!isPlanar) { embedding = null; return(false); } embedding = GetPlanarEmbedding(); return(true); }
/// <summary> /// Traverses planar face of a given embedding. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="graph"></param> /// <param name="embedding"></param> /// <param name="visitor"></param> public static void Traverse <T>(IGraph <T> graph, PlanarEmbedding <T> embedding, IPlanarFaceTraversalVisitor <T> visitor) { var nextEdge = new Dictionary <IEdge <T>, Dictionary <T, IEdge <T> > >(); var visited = new Dictionary <IEdge <T>, HashSet <T> >(); foreach (var edge in graph.Edges) { nextEdge[edge] = new Dictionary <T, IEdge <T> >(); visited[edge] = new HashSet <T>(); } visitor.BeginTraversal(); foreach (var vertex in graph.Vertices) { var vertexEmbedding = embedding.GetEdgesAroundVertex(vertex); for (var i = 0; i < vertexEmbedding.Count; i++) { var edge = vertexEmbedding[i]; var followingEdge = i != vertexEmbedding.Count - 1 ? vertexEmbedding[i + 1] : vertexEmbedding[0]; if (nextEdge.ContainsKey(edge)) { nextEdge[edge][vertex] = followingEdge; } else { nextEdge[SwitchEdge(edge)][vertex] = followingEdge; } } } var selfLoops = new List <IEdge <T> >(); var edgesCache = new List <IEdge <T> >(); var verticesInEdge = new List <T>(); foreach (var edge in graph.Edges) { edgesCache.Add(edge); if (edge.Source.Equals(edge.Target)) { selfLoops.Add(edge); } } foreach (var edge in edgesCache) { var e = edge; verticesInEdge.Clear(); verticesInEdge.Add(e.Source); verticesInEdge.Add(e.Target); foreach (var vertex in verticesInEdge) { var v = vertex; var edgeVisited = GetCorrectEdgeValue(visited, e); var beginFace = false; if (!edgeVisited.Contains(v)) { visitor.BeginFace(); beginFace = true; } while (!edgeVisited.Contains(v)) { visitor.NextVertex(v); visitor.NextEdge(e); edgeVisited.Add(v); v = e.Source.Equals(v) ? e.Target : e.Source; e = GetCorrectEdgeValue(nextEdge, e)[v]; edgeVisited = GetCorrectEdgeValue(visited, e); } if (beginFace) { visitor.EndFace(); } } } // Iterate over all self-loops, visiting them once separately // (they've already been visited once, this visitation is for // the "inside" of the self-loop) foreach (var edge in selfLoops) { visitor.BeginFace(); visitor.NextEdge(edge); visitor.NextVertex(edge.Source); visitor.EndFace(); } visitor.EndTraversal(); }