public void Replace(int aOld, int aNew) { for (int j = edges.CountNext(aOld) - 1; j >= 0; --j) { Graph.LaneInfo info = edges.RemoveNext(aOld, j, out var start, out _); info.ConnectLane = aNew; edges.Add(start, info); } }
public void Replace(int old, int @new) { for (int j = _edges.CountNext(old) - 1; j >= 0; --j) { Graph.LaneInfo info = _edges.RemoveNext(old, j, out var start, out _); info.ConnectLane = @new; _edges.Add(start, info); } }
public void Expand(int col) { int edgeCount = Math.Max(_edges.CountCurrent(), _edges.CountNext()); for (int i = edgeCount - 1; i >= col; --i) { while (_edges.CountNext(i) > 0) { Graph.LaneInfo info = _edges.RemoveNext(i, 0, out var start, out _); info.ConnectLane++; _edges.Add(start, info); } } }
public void Collapse(int col) { int edgeCount = Math.Max(_edges.CountCurrent(), _edges.CountNext()); for (int i = col; i < edgeCount; i++) { while (_edges.CountNext(i) > 0) { Graph.LaneInfo info = _edges.RemoveNext(i, 0, out var start, out _); info.ConnectLane--; _edges.Add(start, info); } } }
public void Expand(int col) { int edgeCount = Math.Max(edges.CountCurrent(), edges.CountNext()); for (int i = edgeCount - 1; i >= col; --i) { while (edges.CountNext(i) > 0) { int start, end; Graph.LaneInfo info = edges.RemoveNext(i, 0, out start, out end); info.ConnectLane++; edges.Add(start, info); } } }
public void Collapse(int col) { int edgeCount = Math.Max(edges.CountCurrent(), edges.CountNext()); for (int i = col; i < edgeCount; i++) { while (edges.CountNext(i) > 0) { int start, end; Graph.LaneInfo info = edges.RemoveNext(i, 0, out start, out end); info.ConnectLane--; edges.Add(start, info); } } }
public void Add(int from, Graph.LaneInfo data) { var e = new Edge(data, from); edges.Add(e); while (countStart.Count <= e.Start) { countStart.Add(0); } countStart[e.Start]++; while (countEnd.Count <= e.End) { countEnd.Add(0); } countEnd[e.End]++; }
public Graph.ILaneRow Advance() { var newLaneRow = new SavedLaneRow(this); var newEdges = new Edges(); for (int i = 0; i < edges.CountNext(); i++) { int edgeCount = edges.CountNext(i); if (edgeCount > 0) { Graph.LaneInfo info = edges.Next(i, 0).Clone(); for (int j = 1; j < edgeCount; j++) { Graph.LaneInfo edgeInfo = edges.Next(i, j); info.UnionWith(edgeInfo); } newEdges.Add(i, info); } } edges = newEdges; return(newLaneRow); }
// end drawGraph private bool DrawItem(Graphics wa, Graph.ILaneRow row) { if (row == null || row.NodeLane == -1) { return(false); } // Clip to the area we're drawing in, but draw 1 pixel past so // that the top/bottom of the line segment's anti-aliasing isn't // visible in the final rendering. int top = wa.RenderingOrigin.Y + _rowHeight / 2; var laneRect = new Rectangle(0, top, Width, _rowHeight); Region oldClip = wa.Clip; var newClip = new Region(laneRect); newClip.Intersect(oldClip); wa.Clip = newClip; wa.Clear(Color.Transparent); //for (int r = 0; r < 2; r++) for (int lane = 0; lane < row.Count; lane++) { int mid = wa.RenderingOrigin.X + (int)((lane + 0.5) * _laneWidth); for (int item = 0; item < row.LaneInfoCount(lane); item++) { Graph.LaneInfo laneInfo = row[lane, item]; bool highLight = (RevisionGraphDrawStyle == RevisionGraphDrawStyleEnum.DrawNonRelativesGray && laneInfo.Junctions.Any(j => j.IsRelative)) || (RevisionGraphDrawStyle == RevisionGraphDrawStyleEnum.HighlightSelected && laneInfo.Junctions.Any(j => j.HighLight)) || (RevisionGraphDrawStyle == RevisionGraphDrawStyleEnum.Normal); List <Color> curColors = GetJunctionColors(laneInfo.Junctions); // Create the brush for drawing the line Brush brushLineColor = null; Pen brushLineColorPen = null; try { bool drawBorder = Settings.BranchBorders && highLight; //hide border for "non-relatives" if (curColors.Count == 1 || !Settings.StripedBranchChange) { if (curColors[0] != _nonRelativeColor) { brushLineColor = new SolidBrush(curColors[0]); } else if (curColors.Count > 1 && curColors[1] != _nonRelativeColor) { brushLineColor = new SolidBrush(curColors[1]); } else { drawBorder = false; brushLineColor = new SolidBrush(_nonRelativeColor); } } else { Color lastRealColor = curColors.LastOrDefault(c => c != _nonRelativeColor); if (lastRealColor.IsEmpty) { brushLineColor = new SolidBrush(_nonRelativeColor); drawBorder = false; } else { brushLineColor = new HatchBrush(HatchStyle.DarkDownwardDiagonal, curColors[0], lastRealColor); } } for (int i = drawBorder ? 0 : 2; i < 3; i++) { Pen penLine = null; if (i == 0) { penLine = _whiteBorderPen; } else if (i == 1) { penLine = _blackBorderPen; } else { if (brushLineColorPen == null) { brushLineColorPen = new Pen(brushLineColor, _laneLineWidth); } penLine = brushLineColorPen; } if (laneInfo.ConnectLane == lane) { wa.DrawLine ( penLine, new Point(mid, top - 1), new Point(mid, top + _rowHeight + 2) ); } else { wa.DrawBezier ( penLine, new Point(mid, top - 1), new Point(mid, top + _rowHeight + 2), new Point(mid + (laneInfo.ConnectLane - lane) * _laneWidth, top - 1), new Point(mid + (laneInfo.ConnectLane - lane) * _laneWidth, top + _rowHeight + 2) ); } } } finally { if (brushLineColorPen != null) { ((IDisposable)brushLineColorPen).Dispose(); } if (brushLineColor != null) { ((IDisposable)brushLineColor).Dispose(); } } } } // Reset the clip region wa.Clip = oldClip; { // Draw node var nodeRect = new Rectangle ( wa.RenderingOrigin.X + (_laneWidth - _nodeDimension) / 2 + row.NodeLane * _laneWidth, wa.RenderingOrigin.Y + (_rowHeight - _nodeDimension) / 2, _nodeDimension, _nodeDimension ); Brush nodeBrush; List <Color> nodeColors = GetJunctionColors(row.Node.Ancestors); bool highlight = (RevisionGraphDrawStyle == RevisionGraphDrawStyleEnum.DrawNonRelativesGray && row.Node.Ancestors.Any(j => j.IsRelative)) || (RevisionGraphDrawStyle == RevisionGraphDrawStyleEnum.HighlightSelected && row.Node.Ancestors.Any(j => j.HighLight)) || (RevisionGraphDrawStyle == RevisionGraphDrawStyleEnum.Normal); bool drawBorder = Settings.BranchBorders && highlight; if (nodeColors.Count == 1) { nodeBrush = new SolidBrush(highlight ? nodeColors[0] : _nonRelativeColor); if (nodeColors[0] == _nonRelativeColor) { drawBorder = false; } } else { nodeBrush = new LinearGradientBrush(nodeRect, nodeColors[0], nodeColors[1], LinearGradientMode.Horizontal); if (nodeColors.All(c => c == _nonRelativeColor)) { drawBorder = false; } } if (_filterMode == FilterType.Highlight && row.Node.IsFiltered) { Rectangle highlightRect = nodeRect; highlightRect.Inflate(2, 3); wa.FillRectangle(Brushes.Yellow, highlightRect); wa.DrawRectangle(Pens.Black, highlightRect); } if (row.Node.Data == null) { wa.FillEllipse(Brushes.White, nodeRect); using (var pen = new Pen(Color.Red, 2)) { wa.DrawEllipse(pen, nodeRect); } } else if (row.Node.IsActive) { wa.FillRectangle(nodeBrush, nodeRect); nodeRect.Inflate(1, 1); using (var pen = new Pen(Color.Black, 3)) wa.DrawRectangle(pen, nodeRect); } else if (row.Node.IsSpecial) { wa.FillRectangle(nodeBrush, nodeRect); if (drawBorder) { wa.DrawRectangle(Pens.Black, nodeRect); } } else { wa.FillEllipse(nodeBrush, nodeRect); if (drawBorder) { wa.DrawEllipse(Pens.Black, nodeRect); } } } return(true); }
private bool DrawItem(Graphics wa, Graph.ILaneRow row) { if (row == null || row.NodeLane == -1) { return(false); } // Clip to the area we're drawing in, but draw 1 pixel past so // that the top/bottom of the line segment's anti-aliasing isn't // visible in the final rendering. int top = wa.RenderingOrigin.Y + (_rowHeight / 2); var laneRect = new Rectangle(0, top, Width, _rowHeight); Region oldClip = wa.Clip; var newClip = new Region(laneRect); newClip.Intersect(oldClip); wa.Clip = newClip; wa.Clear(Color.Transparent); // Getting RevisionGraphDrawStyle results in call to AppSettings. This is not very cheap, cache. _revisionGraphDrawStyleCache = RevisionGraphDrawStyle; ////for (int r = 0; r < 2; r++) for (int lane = 0; lane < row.Count; lane++) { int mid = wa.RenderingOrigin.X + (int)((lane + 0.5) * _laneWidth); for (int item = 0; item < row.LaneInfoCount(lane); item++) { Graph.LaneInfo laneInfo = row[lane, item]; bool highLight = (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.DrawNonRelativesGray && laneInfo.Junctions.Any(j => j.IsRelative)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.HighlightSelected && laneInfo.Junctions.Any(j => j.HighLight)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.Normal); List <Color> curColors = GetJunctionColors(laneInfo.Junctions); // Create the brush for drawing the line Brush brushLineColor = null; Pen brushLineColorPen = null; try { bool drawBorder = highLight && AppSettings.BranchBorders; // hide border for "non-relatives" if (curColors.Count == 1 || !AppSettings.StripedBranchChange) { if (curColors[0] != _nonRelativeColor) { brushLineColor = new SolidBrush(curColors[0]); } else if (curColors.Count > 1 && curColors[1] != _nonRelativeColor) { brushLineColor = new SolidBrush(curColors[1]); } else { drawBorder = false; brushLineColor = new SolidBrush(_nonRelativeColor); } } else { Color lastRealColor = curColors.LastOrDefault(c => c != _nonRelativeColor); if (lastRealColor.IsEmpty) { brushLineColor = new SolidBrush(_nonRelativeColor); drawBorder = false; } else { brushLineColor = new HatchBrush(HatchStyle.DarkDownwardDiagonal, curColors[0], lastRealColor); } } // Precalculate line endpoints bool singleLane = laneInfo.ConnectLane == lane; int x0 = mid; int y0 = top - 1; int x1 = singleLane ? x0 : mid + ((laneInfo.ConnectLane - lane) * _laneWidth); int y1 = top + _rowHeight; Point p0 = new Point(x0, y0); Point p1 = new Point(x1, y1); // Precalculate curve control points when needed Point c0, c1; if (singleLane) { c0 = c1 = Point.Empty; } else { // Controls the curvature of cross-lane lines (0 = straight line, 1 = 90 degree turns) const float severity = 0.5f; c0 = new Point(x0, (int)((y0 * (1.0f - severity)) + (y1 * severity))); c1 = new Point(x1, (int)((y1 * (1.0f - severity)) + (y0 * severity))); } for (int i = drawBorder ? 0 : 2; i < 3; i++) { Pen penLine; switch (i) { case 0: penLine = _whiteBorderPen; break; case 1: penLine = _blackBorderPen; break; default: if (brushLineColorPen == null) { brushLineColorPen = new Pen(brushLineColor, _laneLineWidth); } penLine = brushLineColorPen; break; } if (singleLane) { wa.DrawLine(penLine, p0, p1); } else { wa.DrawBezier(penLine, p0, c0, c1, p1); } } } finally { brushLineColorPen?.Dispose(); ((IDisposable)brushLineColor)?.Dispose(); } } } // Reset the clip region wa.Clip = oldClip; { // Draw node var nodeRect = new Rectangle( wa.RenderingOrigin.X + ((_laneWidth - _nodeDimension) / 2) + (row.NodeLane * _laneWidth), wa.RenderingOrigin.Y + ((_rowHeight - _nodeDimension) / 2), _nodeDimension, _nodeDimension); Brush nodeBrush; List <Color> nodeColors = GetJunctionColors(row.Node.Ancestors); bool highlight = (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.DrawNonRelativesGray && row.Node.Ancestors.Any(j => j.IsRelative)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.HighlightSelected && row.Node.Ancestors.Any(j => j.HighLight)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.Normal); bool drawBorder = AppSettings.BranchBorders && highlight; if (nodeColors.Count == 1) { nodeBrush = new SolidBrush(highlight ? nodeColors[0] : _nonRelativeColor); if (nodeColors[0] == _nonRelativeColor) { drawBorder = false; } } else { nodeBrush = new LinearGradientBrush(nodeRect, nodeColors[0], nodeColors[1], LinearGradientMode.Horizontal); if (nodeColors.All(c => c == _nonRelativeColor)) { drawBorder = false; } } if (_filterMode == FilterType.Highlight && row.Node.IsFiltered) { Rectangle highlightRect = nodeRect; highlightRect.Inflate(2, 3); wa.FillRectangle(Brushes.Yellow, highlightRect); wa.DrawRectangle(Pens.Black, highlightRect); } if (row.Node.Data == null) { wa.FillEllipse(Brushes.White, nodeRect); using (var pen = new Pen(Color.Red, 2)) { wa.DrawEllipse(pen, nodeRect); } } else if (row.Node.IsActive) { wa.FillRectangle(nodeBrush, nodeRect); nodeRect.Inflate(1, 1); using (var pen = new Pen(Color.Black, 3)) { wa.DrawRectangle(pen, nodeRect); } } else if (row.Node.IsSpecial) { wa.FillRectangle(nodeBrush, nodeRect); if (drawBorder) { wa.DrawRectangle(Pens.Black, nodeRect); } } else { wa.FillEllipse(nodeBrush, nodeRect); if (drawBorder) { wa.DrawEllipse(Pens.Black, nodeRect); } } } return(true); }
public Edge(Graph.LaneInfo data, int start) { Data = data; Start = start; }
public void Add(int lane, Graph.LaneInfo data) { edges.Add(lane, data); }
private bool MoveNext() { // If there are no lanes, there is nothing more to draw if (laneNodes.Count == 0 || sourceGraph.Count <= laneRows.Count) { return(false); } // Find the new current row's node (newest item in the row) #region Find current node & index currentRow.Node = null; for (int curLane = 0; curLane < laneNodes.Count; curLane++) { LaneJunctionDetail lane = laneNodes[curLane]; if (lane.Count == 0) { continue; } // NOTE: We could also compare with sourceGraph sourceGraph.AddedNodes[sourceGraph.processedNodes], // since it should always be the same value if (currentRow.Node == null || currentRow.Node.Data == null || (lane.Current.Data != null && lane.Current.Index < currentRow.Node.Index)) { currentRow.Node = lane.Current; currentRow.NodeLane = curLane; //break; } } if (currentRow.Node == null) { // DEBUG: The check above didn't find anything, but should have if (Debugger.IsAttached) { Debugger.Break(); } //Node[] topo = this.sourceGraph.TopoSortedNodes(); return(false); } // If this row doesn't contain data, we're to the end of the valid entries. if (currentRow.Node.Data == null) { return(false); } sourceGraph.ProcessNode(currentRow.Node); #endregion // Check for multiple junctions with this node at the top. Remove the // node from that junction as well. This will happen when there is a branch #region Check for branches currentRow.Clear(currentRow.NodeLane); for (int curLane = 0; curLane < laneNodes.Count; curLane++) { LaneJunctionDetail lane = laneNodes[curLane]; if (lane.Count == 0) { continue; } if (currentRow.Node != lane.Current) { // We're only interested in columns that have the same node // at the top of the junction as the current row's node continue; } // Remove the item from the lane, since it is being drawn now. // We need to draw the graph line for this lane. If there are no items // left in the lane we don't draw it. int intoLane = AdvanceLane(curLane); if (intoLane < curLane) { // AdvanceLane could have removed lanes so we need to start from // the merged into lane (otherwise we could skip a lane, causing // us to try to insert a node into the graph twice) curLane = intoLane; } // Re-process the lane to make sure there are no actions left. curLane--; } #endregion // Look for lanes that cross and reorder to straighten them out if possible, // and keep the lanes that merge next to each other. #region Straighten out lanes // Look for crossing lanes // but only when there are not too many lanes taking up too much performance if (currentRow.Count < 10) { for (int lane = 0; lane < currentRow.Count; lane++) { for (int item = 0; item < currentRow.LaneInfoCount(lane); item++) { Graph.LaneInfo laneInfo = currentRow[lane, item]; if (laneInfo.ConnectLane <= lane) { continue; } // Lane is moving to the right, check to see if it intersects // with any lanes moving to the left. for (int otherLane = lane + 1; otherLane <= laneInfo.ConnectLane; otherLane++) { if (currentRow.LaneInfoCount(otherLane) != 1) { continue; } Graph.LaneInfo otherLaneInfo = currentRow[otherLane, 0]; if (otherLaneInfo.ConnectLane < otherLane) { currentRow.Swap(otherLaneInfo.ConnectLane, otherLane); LaneJunctionDetail temp = laneNodes[otherLane]; laneNodes[otherLane] = laneNodes[otherLaneInfo.ConnectLane]; laneNodes[otherLaneInfo.ConnectLane] = temp; } } } } } //// Keep the merge lanes next to each other //int mergeFromCount = currentRow.LaneInfoCount(currentRow.NodeLane); //if (mergeFromCount > 1) //{ // for (int i = 0; i < mergeFromCount; i++) // { // Graph.LaneInfo laneInfo = currentRow[currentRow.NodeLane, i]; // // Check to see if the lane is currently next to us // if (laneInfo.ConnectLane - currentRow.NodeLane > mergeFromCount) // { // // Only move the lane if it isn't already being drawn. // if (currentRow.LaneInfoCount(laneInfo.ConnectLane) == 0) // { // // Remove the row laneInfo.ConnectLane and insert // // it at currentRow.NodeLane+1. // // Then start over searching for others if i != mergeFromCount-1? // int adjacentLane = currentRow.NodeLane + 1; // if (adjacentLane >= laneNodes.Count) Debugger.Break(); // currentRow.Expand(adjacentLane); // currentRow.Replace(laneInfo.ConnectLane + 1, adjacentLane); // LaneJunctionDetail temp = laneNodes[laneInfo.ConnectLane]; // laneNodes.RemoveAt(laneInfo.ConnectLane); // laneNodes.Insert(adjacentLane, temp); // } // } // } //} #endregion if (currentRow.Node != null) { Graph.ILaneRow row = currentRow.Advance(); // This means there is a node that got put in the graph twice... if (row.Node.InLane != int.MaxValue) { if (Debugger.IsAttached) { Debugger.Break(); } } row.Node.InLane = laneRows.Count; laneRows.Add(row); return(true); } // Return that there are more items left return(false); }
// end drawGraph private bool DrawItem(Graphics wa, Graph.ILaneRow row) { if (row == null || row.NodeLane == -1) { return(false); } // Clip to the area we're drawing in, but draw 1 pixel past so // that the top/bottom of the line segment's anti-aliasing isn't // visible in the final rendering. int top = wa.RenderingOrigin.Y + rowHeight / 2; var laneRect = new Rectangle(0, top, Width, rowHeight); Region oldClip = wa.Clip; var newClip = new Region(laneRect); newClip.Intersect(oldClip); wa.Clip = newClip; wa.Clear(Color.Transparent); for (int r = 0; r < 2; r++) { for (int lane = 0; lane < row.Count; lane++) { int mid = wa.RenderingOrigin.X + (int)((lane + 0.5) * LANE_WIDTH); for (int item = 0; item < row.LaneInfoCount(lane); item++) { Graph.LaneInfo laneInfo = row[lane, item]; //Draw all non-relative items first, them draw //all relative items on top if (laneInfo.Junctions.FirstOrDefault() != null) { if (laneInfo.Junctions.First().IsRelative == (r == 0)) { continue; } } List <Color> curColors = GetJunctionColors(laneInfo.Junctions); // Create the brush for drawing the line Brush brushLineColor; bool drawBorder = Settings.BranchBorders; //hide border for "non-relatives" if (curColors.Count == 1 || !Settings.StripedBranchChange) { if (curColors[0] != nonRelativeColor) { brushLineColor = new SolidBrush(curColors[0]); } else if (curColors.Count > 1 && curColors[1] != nonRelativeColor) { brushLineColor = new SolidBrush(curColors[1]); } else { drawBorder = false; brushLineColor = new SolidBrush(nonRelativeColor); } } else { brushLineColor = new HatchBrush(HatchStyle.DarkDownwardDiagonal, curColors[0], curColors[1]); if (curColors[0] == nonRelativeColor && curColors[1] == nonRelativeColor) { drawBorder = false; } } for (int i = drawBorder ? 0 : 2; i < 3; i++) { Pen penLine; if (i == 0) { penLine = new Pen(new SolidBrush(Color.White), LANE_LINE_WIDTH + 2); } else if (i == 1) { penLine = new Pen(new SolidBrush(Color.Black), LANE_LINE_WIDTH + 1); } else { penLine = new Pen(brushLineColor, LANE_LINE_WIDTH); } if (laneInfo.ConnectLane == lane) { wa.DrawLine ( penLine, new Point(mid, top - 1), new Point(mid, top + rowHeight + 2) ); } else { wa.DrawBezier ( penLine, new Point(mid, top - 1), new Point(mid, top + rowHeight + 2), new Point(mid + (laneInfo.ConnectLane - lane) * LANE_WIDTH, top - 1), new Point(mid + (laneInfo.ConnectLane - lane) * LANE_WIDTH, top + rowHeight + 2) ); } } } } } // Reset the clip region wa.Clip = oldClip; { // Draw node var nodeRect = new Rectangle ( wa.RenderingOrigin.X + (LANE_WIDTH - NODE_DIMENSION) / 2 + row.NodeLane * LANE_WIDTH, wa.RenderingOrigin.Y + (rowHeight - NODE_DIMENSION) / 2, NODE_DIMENSION, NODE_DIMENSION ); Brush nodeBrush; bool drawBorder = Settings.BranchBorders; List <Color> nodeColors = GetJunctionColors(row.Node.Ancestors); if (nodeColors.Count == 1) { nodeBrush = new SolidBrush(nodeColors[0]); if (nodeColors[0] == nonRelativeColor) { drawBorder = false; } } else { nodeBrush = new LinearGradientBrush(nodeRect, nodeColors[0], nodeColors[1], LinearGradientMode.Horizontal); if (nodeColors[0] == nonRelativeColor && nodeColors[1] == Color.LightGray) { drawBorder = false; } } if (filterMode == FilterType.Highlight && row.Node.IsFiltered) { Rectangle highlightRect = nodeRect; highlightRect.Inflate(2, 3); wa.FillRectangle(Brushes.Yellow, highlightRect); wa.DrawRectangle(new Pen(Brushes.Black), highlightRect); } if (row.Node.Data == null) { wa.FillEllipse(Brushes.White, nodeRect); wa.DrawEllipse(new Pen(Color.Red, 2), nodeRect); } else if (row.Node.IsActive) { wa.FillRectangle(nodeBrush, nodeRect); nodeRect.Inflate(1, 1); wa.DrawRectangle(new Pen(Color.Black, 3), nodeRect); } else if (row.Node.IsSpecial) { wa.FillRectangle(nodeBrush, nodeRect); if (drawBorder) { wa.DrawRectangle(new Pen(Color.Black, 1), nodeRect); } } else { wa.FillEllipse(nodeBrush, nodeRect); if (drawBorder) { wa.DrawEllipse(new Pen(Color.Black, 1), nodeRect); } } } return(true); }
private bool DrawItem(Graphics wa, Graph.ILaneRow row) { ThreadHelper.AssertOnUIThread(); if (row == null || row.NodeLane == -1) { return(false); } // Clip to the area we're drawing in, but draw 1 pixel past so // that the top/bottom of the line segment's anti-aliasing isn't // visible in the final rendering. int top = wa.RenderingOrigin.Y + (_rowHeight / 2); var laneRect = new Rectangle(0, top, Width, _rowHeight); Region oldClip = wa.Clip; var newClip = new Region(laneRect); newClip.Intersect(oldClip); wa.Clip = newClip; wa.Clear(Color.Transparent); // Getting RevisionGraphDrawStyle results in call to AppSettings. This is not very cheap, cache. _revisionGraphDrawStyleCache = RevisionGraphDrawStyle; ////for (int r = 0; r < 2; r++) for (int lane = 0; lane < row.Count; lane++) { int mid = wa.RenderingOrigin.X + (int)((lane + 0.5) * _laneWidth); for (int item = 0; item < row.LaneInfoCount(lane); item++) { Graph.LaneInfo laneInfo = row[lane, item]; bool highLight = (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.DrawNonRelativesGray && laneInfo.Junctions.Any(j => j.IsRelative)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.HighlightSelected && laneInfo.Junctions.Any(j => j.HighLight)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.Normal); UpdateJunctionColors(laneInfo.Junctions); // Create the brush for drawing the line Brush lineBrush = null; Pen linePen = null; try { bool drawBorder = highLight && AppSettings.BranchBorders; // hide border for "non-relatives" if (_junctionColors.Count == 1 || !AppSettings.StripedBranchChange) { if (_junctionColors[0] != _nonRelativeColor) { lineBrush = new SolidBrush(_junctionColors[0]); } else if (_junctionColors.Count > 1 && _junctionColors[1] != _nonRelativeColor) { lineBrush = new SolidBrush(_junctionColors[1]); } else { drawBorder = false; lineBrush = new SolidBrush(_nonRelativeColor); } } else { Color lastRealColor = _junctionColors.LastOrDefault(c => c != _nonRelativeColor); if (lastRealColor.IsEmpty) { lineBrush = new SolidBrush(_nonRelativeColor); drawBorder = false; } else { lineBrush = new HatchBrush(HatchStyle.DarkDownwardDiagonal, _junctionColors[0], lastRealColor); } } // Precalculate line endpoints bool sameLane = laneInfo.ConnectLane == lane; int x0 = mid; int y0 = top - 1; int x1 = sameLane ? x0 : mid + ((laneInfo.ConnectLane - lane) * _laneWidth); int y1 = top + _rowHeight; Point p0 = new Point(x0, y0); Point p1 = new Point(x1, y1); // Precalculate curve control points when needed Point c0, c1; if (sameLane) { // We are drawing between two points in the same // lane, so there will be no curve c0 = c1 = default; } else { // Left shifting int is fast equivalent of dividing by two, // thus computing the average of y0 and y1. var yMid = (y0 + y1) >> 1; c0 = new Point(x0, yMid); c1 = new Point(x1, yMid); } for (int i = drawBorder ? 0 : 2; i < 3; i++) { Pen pen; switch (i) { case 0: pen = _whiteBorderPen; break; case 1: pen = _blackBorderPen; break; default: if (linePen == null) { linePen = new Pen(lineBrush, _laneLineWidth); } pen = linePen; break; } if (sameLane) { wa.DrawLine(pen, p0, p1); } else { wa.DrawBezier(pen, p0, c0, c1, p1); } } } finally { linePen?.Dispose(); lineBrush?.Dispose(); } } } // Reset the clip region wa.Clip = oldClip; // Draw node var nodeRect = new Rectangle( wa.RenderingOrigin.X + ((_laneWidth - _nodeDimension) / 2) + (row.NodeLane * _laneWidth), wa.RenderingOrigin.Y + ((_rowHeight - _nodeDimension) / 2), _nodeDimension, _nodeDimension); Brush nodeBrush; UpdateJunctionColors(row.Node.Ancestors); bool highlight = (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.DrawNonRelativesGray && row.Node.Ancestors.Any(j => j.IsRelative)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.HighlightSelected && row.Node.Ancestors.Any(j => j.HighLight)) || (_revisionGraphDrawStyleCache == RevisionGraphDrawStyleEnum.Normal); bool drawNodeBorder = AppSettings.BranchBorders && highlight; if (_junctionColors.Count == 1) { nodeBrush = new SolidBrush(highlight ? _junctionColors[0] : _nonRelativeColor); if (_junctionColors[0] == _nonRelativeColor) { drawNodeBorder = false; } } else { nodeBrush = new LinearGradientBrush( nodeRect, _junctionColors[0], _junctionColors[1], LinearGradientMode.Horizontal); if (_junctionColors.All(c => c == _nonRelativeColor)) { drawNodeBorder = false; } } if (row.Node.Data == null) { wa.FillEllipse(Brushes.White, nodeRect); using (var pen = new Pen(Color.Red, 2)) { wa.DrawEllipse(pen, nodeRect); } } else if (row.Node.IsActive) { wa.FillRectangle(nodeBrush, nodeRect); nodeRect.Inflate(1, 1); using (var pen = new Pen(Color.Black, 3)) { wa.DrawRectangle(pen, nodeRect); } } else if (row.Node.IsSpecial) { wa.FillRectangle(nodeBrush, nodeRect); if (drawNodeBorder) { wa.DrawRectangle(Pens.Black, nodeRect); } } else { wa.FillEllipse(nodeBrush, nodeRect); if (drawNodeBorder) { wa.DrawEllipse(Pens.Black, nodeRect); } } nodeBrush.Dispose(); return(true); void UpdateJunctionColors(IEnumerable <Junction> junction) { _junctionColors.Clear(); // Color of non-relative branches. _junctionColors.AddRange(junction.Select(GetJunctionColor)); if (_junctionColors.Count == 0) { _junctionColors.Add(Color.Black); } } }