Ejemplo n.º 1
0
        public void CreateAndWriteGraph()
        {
            //Instantiate property set manager necessary to gather alignment names
            PropertySetManager psmPipeline = new PropertySetManager(
                Application.DocumentManager.MdiActiveDocument.Database,
                PSetDefs.DefinedSets.DriPipelineData);

            PSetDefs.DriPipelineData driPipelineData = new PSetDefs.DriPipelineData();

            //Counter to count all disjoined graphs
            int graphCount = 0;
            //Flag to signal the entry point subgraph
            bool isEntryPoint = false;
            //Stringbuilder to collect all disjoined graphs
            StringBuilder sbAll = new StringBuilder();

            sbAll.AppendLine("digraph G {");

            while (GraphEntities.Count > 0)
            {
                //Increment graph counter
                graphCount++;

                //Collection to keep track of visited nodes
                //To prevent looping for ever
                //And to be able to handle disjoined piping networks
                HashSet <Handle> visitedHandles = new HashSet <Handle>();

                //Determine starting entity
                //Criteria: Only one child -> means an end node AND largest DN of all not visited
                //Currently only for one network
                //Disjoined networks are not handled yet

                GraphEntity ge = GraphEntities.Where(x => x.Cons.Length == 1).MaxBy(x => x.LargestDn()).FirstOrDefault();

                //prdDbg(ge.OwnerHandle.ToString());
                if (ge == null)
                {
                    //throw new System.Exception("No entity found!");
                    prdDbg("ERROR: Graph not complete!!!");
                    foreach (var item in GraphEntities)
                    {
                        Entity owner = item.Owner;
                        Line   line  = new Line();
                        line.Layer      = "0";
                        line.StartPoint = new Point3d();
                        switch (owner)
                        {
                        case Polyline pline:
                            line.EndPoint = pline.GetPointAtDist(pline.Length / 2);
                            break;

                        case BlockReference br:
                            line.EndPoint = br.Position;
                            break;

                        default:
                            break;
                        }
                        line.AddEntityToDbModelSpace(Application.DocumentManager.MdiActiveDocument.Database);
                    }
                    break;
                }

                //Flag the entry point subgraph
                isEntryPoint = true;

                //Variable to cache previous handle to avoid backreferences
                Handle previousHandle = default;

                //Collection to collect the edges
                HashSet <Edge> edges = new HashSet <Edge>();

                //Collection to collect the subgraphs
                Dictionary <string, Subgraph> subgraphs = new Dictionary <string, Subgraph>();

                //Using a stack traversing strategy
                Stack <GraphEntity> stack = new Stack <GraphEntity>();
                //Put the first element on to the stack manually
                stack.Push(ge);
                //Iterate the stack until no connected nodes left
                while (stack.Count > 0)
                {
                    //Fetch the topmost entity on stack
                    GraphEntity current = stack.Pop();

                    //Determine the subgraph it is part of
                    string alName = psmPipeline.ReadPropertyString(current.Owner, driPipelineData.BelongsToAlignment);
                    //Fetch or create new subgraph object
                    Subgraph subgraph;
                    if (subgraphs.ContainsKey(alName))
                    {
                        subgraph = subgraphs[alName];
                    }
                    else
                    {
                        subgraph = new Subgraph(dB, ComponentTable, alName);
                        subgraphs.Add(alName, subgraph);
                    }
                    subgraph.Nodes.Add(current.OwnerHandle);

                    if (isEntryPoint)
                    {
                        subgraph.isEntryPoint = isEntryPoint;
                        isEntryPoint          = false;
                    }

                    //Iterate over current node's children
                    foreach (Con con in current.Cons)
                    {
                        //Find the child the con is referencing to
                        GraphEntity child = GraphEntities.Where(x => x.OwnerHandle == con.ConHandle).FirstOrDefault();
                        //if it is the con refering back to the parent -> skip it
                        if (child == default || child.OwnerHandle == current.OwnerHandle)
                        {
                            continue;
                        }
                        //Also skip if child has already been visited
                        //This prevents from making circular graphs I think
                        //Comment next line out to test circular graphs
                        //if (visitedHandles.Contains(child.OwnerHandle)) continue; <-- First solution
                        //Solution with caching of previous handle, it I don't think it works when backtracking to a branch -> there will be a double arrow
                        if (previousHandle != null && previousHandle == child.OwnerHandle)
                        {
                            continue;
                        }
                        //Try to control which cons get written by their type
                        //Build string
                        string ownEnd = con.OwnEndType.ToString();
                        string conEnd = con.ConEndType.ToString();
                        string key    = ownEnd + "-" + conEnd;
                        if (!allowedCombinations[key])
                        {
                            continue;
                        }

                        //Tries to prevent duplicate Main-Main edges by eliminating upstream Main-Main instance
                        //Doesn't work if recursion just returned from a branch, because previous is set the the
                        //Last node on the branch
                        if (key == "Main-Main" && con.ConHandle == previousHandle)
                        {
                            continue;
                        }

                        //Record the edge between nodes
                        edges.Add(new Edge(current.OwnerHandle, child.OwnerHandle));
                        //edges.Add(new Edge(current.OwnerHandle, child.OwnerHandle, key));
                        //If this child node is in visited collection -> skip, so we don't ger circular referencing
                        if (visitedHandles.Contains(child.OwnerHandle))
                        {
                            continue;
                        }
                        //If the node has not been visited yet, then put it on the stack
                        stack.Push(child);
                    }
                    //When current iteration completes, put the current node handle in the visited collection
                    visitedHandles.Add(current.OwnerHandle);
                    //Cache current node handle to avoid backreference
                    previousHandle = current.OwnerHandle;
                }

                //Write collected data
                //Stringbuilder to contain the overall file
                //This must be refactored when working with disjoined networks
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"subgraph G_{graphCount} {{"); //First line of file stating a graph
                                                              //Set the shape of the nodes for whole graph
                sb.AppendLine("node [shape=record];");

                //Write edges
                foreach (Edge edge in edges)
                {
                    sb.AppendLine(edge.ToString("->"));
                }

                //Write subgraphs
                int i = 0;
                foreach (var sg in subgraphs)
                {
                    i++;
                    sb.Append(sg.Value.WriteSubgraph(i));
                }

                //Add closing curly brace correspoinding to the first line
                sb.AppendLine("}");
                //Append current disjoined graph to all collector
                sbAll.Append(sb.ToString());

                //using (System.IO.StreamWriter file = new System.IO.StreamWriter($"C:\\Temp\\MyGraph_{graphCount}.dot"))
                //{
                //    file.WriteLine(sb.ToString()); // "sb" is the StringBuilder
                //}

                //Modify the GraphEntities to remove visited entities
                GraphEntities = GraphEntities.ExceptWhere(x => visitedHandles.Contains(x.OwnerHandle)).ToHashSet();
            }

            //Closing brace of the main graph
            sbAll.AppendLine("}");

            //Check or create directory
            if (!Directory.Exists(@"C:\Temp\"))
            {
                Directory.CreateDirectory(@"C:\Temp\");
            }

            //Write the collected graphs to one file
            using (System.IO.StreamWriter file = new System.IO.StreamWriter($"C:\\Temp\\MyGraph.dot"))
            {
                file.WriteLine(sbAll.ToString()); // "sb" is the StringBuilder
            }
        }