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 } }
public void AddEntityToPOIs(Entity ent) { switch (ent) { case Polyline pline: switch (GetPipeSystem(pline)) { case PipeSystemEnum.Ukendt: prdDbg($"Wrong type of pline supplied: {pline.Handle}"); return; case PipeSystemEnum.Stål: POIs.Add(new POI(pline, pline.StartPoint.To2D(), EndType.Start, PSM, DriGraph)); POIs.Add(new POI(pline, pline.EndPoint.To2D(), EndType.End, PSM, DriGraph)); break; case PipeSystemEnum.Kobberflex: case PipeSystemEnum.AluPex: #region STIK //Find forbindelse til forsyningsrøret Point3d pt = pline.StartPoint; var query = allPipes.Where(x => pt.IsOnCurve(x, 0.025) && pline.Handle != x.Handle && GetPipeSystem(x) == PipeSystemEnum.Stål); if (query.FirstOrDefault() != default) { Polyline parent = query.FirstOrDefault(); POIs.Add(new POI(parent, parent.GetClosestPointTo(pt, false).To2D(), EndType.StikAfgrening, PSM, DriGraph)); } pt = pline.EndPoint; if (query.FirstOrDefault() != default) { //This shouldn't happen now, because AssignPlinesAndBlocksToAlignments //guarantees that the end point is never on a supply pipe Polyline parent = query.FirstOrDefault(); POIs.Add(new POI(parent, parent.GetClosestPointTo(pt, false).To2D(), EndType.StikAfgrening, PSM, DriGraph)); } #endregion //Tilføj almindelige ender til POIs POIs.Add(new POI(pline, pline.StartPoint.To2D(), EndType.StikStart, PSM, DriGraph)); POIs.Add(new POI(pline, pline.EndPoint.To2D(), EndType.StikEnd, PSM, DriGraph)); break; default: throw new System.Exception("Supplied a new PipeSystemEnum! Add to code kthxbai."); } break; case BlockReference br: Transaction tx = br.Database.TransactionManager.TopTransaction; BlockTableRecord btr = br.BlockTableRecord.Go <BlockTableRecord>(tx); foreach (Oid oid in btr) { if (!oid.IsDerivedFrom <BlockReference>()) { continue; } BlockReference nestedBr = oid.Go <BlockReference>(tx); if (!nestedBr.Name.Contains("MuffeIntern")) { continue; } Point3d wPt = nestedBr.Position; wPt = wPt.TransformBy(br.BlockTransform); EndType endType; if (nestedBr.Name.Contains("BRANCH")) { endType = EndType.Branch; } else { endType = EndType.Main; //Handle special case of AFGRSTUDS //which does not coincide with an end on polyline //but is situated somewhere along the polyline if (br.RealName() == "AFGRSTUDS") { PropertySetManager psmPipeline = new PropertySetManager(dB, PSetDefs.DefinedSets.DriPipelineData); PSetDefs.DriPipelineData driPipelineData = new PSetDefs.DriPipelineData(); string branchAlName = psmPipeline.ReadPropertyString(br, driPipelineData.BranchesOffToAlignment); if (branchAlName.IsNoE()) { prdDbg( $"WARNING! Afgrstuds {br.Handle} has no BranchesOffToAlignment value.\n" + $"This happens if there are objects with no alignment assigned.\n" + $"To fix enter main alignment name in BranchesOffToAlignment field."); } var polylines = allPipes //.GetFjvPipes(tx, true) //.HashSetOfType<Polyline>(tx, true) .Where(x => psmPipeline.FilterPropetyString (x, driPipelineData.BelongsToAlignment, branchAlName)); //.ToHashSet(); foreach (Polyline polyline in polylines) { Point3d nearest = polyline.GetClosestPointTo(wPt, false); if (nearest.DistanceHorizontalTo(wPt) < 0.01) { POIs.Add(new POI(polyline, nearest.To2D(), EndType.Branch, PSM, DriGraph)); break; } } } } POIs.Add(new POI(br, wPt.To2D(), endType, PSM, DriGraph)); } break; default: throw new System.Exception("Wrong type of object supplied!"); } }