private static GraphAtom[] BuildGraphLine0Child(IList<T> parents, T[] line, int[] linecolors, IGraphColorProvider cprov, ref int lineCount) { GraphAtom[] res; int lineColor = 0; // find position to insert dot int j = lineCount; while((j != 0) && (line[j - 1] == null)) { --j; } // allocate memory for graph line switch(parents.Count) { case 0: // just a dot, generates no topology res = new GraphAtom[j + 1]; // paint dot res[j].Paint(GraphElement.Dot, 0); break; case 1: // allocate color bool colorFound = false; int spaceId = -1; for(int i = 0; i < lineCount; ++i) { if(line[i] == parents[0] && !colorFound) { lineColor = linecolors[i]; colorFound = true; if(spaceId != -1) break; } if(line[i] == null && spaceId == -1) { spaceId = i; if(colorFound) break; } } if(!colorFound) { lineColor = cprov.AcquireColor(); } if(spaceId == -1) { // generate 1 new line lineCount = j + 1; res = new GraphAtom[lineCount]; } else { j = spaceId; res = new GraphAtom[lineCount]; ContinueVerticalLines(res, line, linecolors, j + 1, lineCount - 1); } // paint dot res[j].Paint(GraphElement.Dot, 0); line[j] = parents[0]; linecolors[j] = lineColor; res[j].Paint(GraphElement.VerticalBottom, lineColor); break; default: var d = new Dictionary<int, int>(parents.Count); for(int p = 0; p < parents.Count; ++p) { for(int i = 0; i < lineCount; ++i) { if(line[i] == parents[p]) { d.Add(p, i); break; } } } // generate parents - 1 new lines lineCount = j + 1 + parents.Count - d.Count - 1; if(d.Count == parents.Count) { res = new GraphAtom[j + 1]; } else { res = new GraphAtom[j + 1 + parents.Count - d.Count - 1]; } // paint dot res[j].Paint(GraphElement.Dot, 0); // paint lines for parents int newLineId = j; for(int i = 0; i < parents.Count; ++i) { int to; if(d.TryGetValue(i, out to)) { lineColor = linecolors[to]; DrawBottomConnection(res, lineColor, j, to); } else { // something like // ... ... ... // .+- --- ... // .|. \.. \.. line[newLineId] = parents[i]; linecolors[newLineId] = lineColor = cprov.AcquireColor(); DrawBottomConnection(res, lineColor, j, newLineId); newLineId++; } } break; } // continue lines from previos graph lines, preserving color ContinueVerticalLines(res, line, linecolors, 0, j - 1); return res; }
private static int ColorLookup(T[] line, int lineCount, T parent, int[] linecolors, IGraphColorProvider cprov) { for(int i = 0; i < lineCount; ++i) { if(line[i] == parent) return linecolors[i]; } return cprov.AcquireColor(); }
private static void BuildUpperConnections(GraphAtom[] res, int[] linecolors, IGraphColorProvider cprov, IList<int> pos, int id) { int j = pos[id]; for(int i = 0; i < pos.Count; ++i) { int cpos = pos[i]; int lineColor = linecolors[i]; if(cpos < j) { res[cpos].Paint(GraphElement.RightTopCorner, lineColor); if(i != pos.Count - 1) { int next = pos[i + 1]; if(next >= j) { next = j - 1; res[j].Paint(GraphElement.HorizontalLeft, lineColor); } for(int k = cpos + 1; k <= next; ++k) { res[k].Paint(GraphElement.Horizontal, lineColor); } } else { int next = j - 1; res[j].Paint(GraphElement.HorizontalLeft, lineColor); for(int k = cpos + 1; k <= next; ++k) { res[k].Paint(GraphElement.Horizontal, lineColor); } } cprov.ReleaseColor(lineColor); } else if(cpos > j) { res[cpos].Paint(GraphElement.LeftTopCorner, lineColor); if(i != 0) { int prev = pos[i - 1]; if(prev <= j) { prev = j + 1; res[j].Paint(GraphElement.HorizontalRight, lineColor); } for(int k = prev; k < cpos; ++k) { res[k].Paint(GraphElement.Horizontal, lineColor); } } else { int prev = j + 1; res[j].Paint(GraphElement.HorizontalRight, lineColor); for(int k = prev; k < cpos; ++k) { res[k].Paint(GraphElement.Horizontal, lineColor); } } cprov.ReleaseColor(lineColor); } else { res[cpos].Paint(GraphElement.VerticalTop, lineColor); } } }
private static GraphAtom[] BuildGraphLine(IList<T> parents, T[] line, int[] linecolors, IGraphColorProvider cprov, IList<int> pos, int id, ref int lineCount) { GraphAtom[] res; int j = pos[id]; switch(parents.Count) { case 0: #region Line stops here { // allocate memory for new line res = new GraphAtom[lineCount]; // paint // ... // .+. // ... res[j].Paint(GraphElement.Dot, 0); for(int i = 0; i < pos.Count; ++i) { line[pos[i]] = null; } cprov.ReleaseColor(linecolors[j]); if(j == lineCount - 1) // reduce graph width if it was last line { while(lineCount != 0 && line[lineCount - 1] == null) { --lineCount; } } // continue lines from previous graph line ContinueVerticalLines(res, line, linecolors, 0, lineCount - 1); } #endregion break; case 1: #region Line continues - most common case { int lineColor; if(pos.Count > 1) { lineColor = cprov.AcquireColor(); cprov.ReleaseColor(linecolors[j]); linecolors[j] = lineColor; } else { lineColor = linecolors[j]; } for(int i = 0; i < pos.Count; ++i) { line[pos[i]] = null; } line[j] = parents[0]; // allocate memory for new graph line res = new GraphAtom[lineCount]; // paint res[j].Paint(GraphElement.Dot, 0); res[j].Paint(GraphElement.VerticalBottom, lineColor); // continue lines from previous graph line ContinueVerticalLines(res, line, linecolors, 0, j - 1); ContinueVerticalLines(res, line, linecolors, j + 1, lineCount - 1); } #endregion break; default: #region Several lines start here (point where 1+ git branches were merged) { line[j] = null; bool lastItem = j == lineCount - 1; var lineColor = 0; Dictionary<T, int> unmergeable = null; var mergeable = new Dictionary<T, List<int>>(parents.Count); // find mergeable parents for(int i = 0; i < lineCount; ++i) { var p = line[i]; if(p != null) { bool found = false; for(int c = 0; c < parents.Count; ++c) { if(p == parents[c]) { found = true; List<int> positions; if(!mergeable.TryGetValue(p, out positions)) { positions = new List<int>(parents.Count); mergeable.Add(p, positions); } positions.Add(i); break; } } if(found) { if(mergeable.Count == parents.Count) break; } } } if(parents.Count == mergeable.Count) { T closest = default(T); int closestPosition = 0; int minD = int.MaxValue; bool found = true; foreach(var kvp in mergeable) { var positions = kvp.Value; for(int i = 0; i < positions.Count; ++i) { if(positions[i] <= j) { found = false; break; } else { var d = positions[i] - j; if(d > 0 && d < minD) { closestPosition = j; minD = d; closest = kvp.Key; } } } } if(found) { var list = mergeable[closest]; if(list.Count == 1) { mergeable.Remove(closest); } else { list.Remove(closestPosition); } unmergeable = new Dictionary<T, int>(); unmergeable.Add(closest, j); } } else { // fill unmergeable int np = j; unmergeable = new Dictionary<T, int>(parents.Count - mergeable.Count); for(int c = 0; c < parents.Count; ++c) { var p = parents[c]; if(!mergeable.ContainsKey(p)) { while(line[np] != null && !pos.Contains(np)) { ++np; } unmergeable.Add(p, np); ++np; } } if(np > lineCount) { lineCount = np; } } // allocate memory for new line res = new GraphAtom[lineCount]; // paint dot res[j].Paint(GraphElement.Dot, 0); // paint mergeable foreach(var c in mergeable) { var positions = c.Value; int mind = int.MaxValue; int pid = 0; for(int p = 0; p < positions.Count; ++p) { int d = Math.Abs(j - positions[p]); if(d < mind) { mind = d; pid = p; } } int i = positions[pid]; lineColor = linecolors[i]; DrawBottomConnection(res, lineColor, j, i); } for(int i = 0; i < pos.Count; ++i) { line[pos[i]] = null; } // continue lines from previous graph line ContinueVerticalLines(res, line, linecolors, 0, lineCount - 1); // paint unmergeable int oldcolor = 0; if(unmergeable != null) { foreach(var c in unmergeable) { int i = c.Value; lineColor = cprov.AcquireColor(); DrawBottomConnection(res, lineColor, j, i); if(i == j) { oldcolor = linecolors[i]; } linecolors[i] = lineColor; line[i] = c.Key; } } cprov.ReleaseColor(oldcolor); } #endregion break; } return res; }