Skip to content

Commit d7a45fd

Browse files
claudebertyhell
authored andcommitted
enhance: only visit visible links and dots when rendering the commit graph
`Views.CommitGraph.Render` iterated `Links` and `Dots` from the first item on every frame, skipping everything above the viewport, which is O(commits) per frame when scrolled to the bottom of a long history. Both lists are ordered by Y (one dot per commit row, each link ends half a row below its commit), so binary search the first visible item instead. `Paths` keep the linear scan since a path that starts far above the viewport can still cross it. The grayed pens are now created once in `Models.CommitGraph.SetPens` (and the dot fill pen is cached until `DotBrush` changes) instead of being allocated on every frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCxH1zwDXLd9kfAAkcf6PZ
1 parent 3e65576 commit d7a45fd

2 files changed

Lines changed: 39 additions & 6 deletions

File tree

‎src/Models/CommitGraph.cs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public enum CommitGraphHighlighting
2020
public class CommitGraph
2121
{
2222
public static List<Pen> Pens { get; } = [];
23+
public static Pen GrayedCurvePen { get; private set; } = null;
24+
public static Pen GrayedDotPen { get; private set; } = null;
2325

2426
public static void SetDefaultPens(double thickness = 2)
2527
{
@@ -33,6 +35,8 @@ public static void SetPens(List<Color> colors, double thickness)
3335
foreach (var c in colors)
3436
Pens.Add(new Pen(c.ToUInt32(), thickness));
3537

38+
GrayedCurvePen = new Pen(new SolidColorBrush(Colors.Gray, 0.4), thickness);
39+
GrayedDotPen = new Pen(Brushes.Gray, thickness);
3640
s_penCount = colors.Count;
3741
}
3842

‎src/Views/CommitGraph.cs‎

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using Avalonia;
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using Avalonia;
25
using Avalonia.Controls;
36
using Avalonia.Media;
47

@@ -64,6 +67,9 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
6467
{
6568
base.OnPropertyChanged(change);
6669

70+
if (change.Property == DotBrushProperty)
71+
_dotFillPen = null;
72+
6773
if (change.Property == GraphProperty ||
6874
change.Property == LayoutProperty ||
6975
change.Property == DotBrushProperty)
@@ -72,10 +78,13 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
7278

7379
private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom, double rowHeight)
7480
{
75-
var grayedPen = new Pen(new SolidColorBrush(Colors.Gray, 0.4), Models.CommitGraph.Pens[0].Thickness);
81+
var grayedPen = Models.CommitGraph.GrayedCurvePen;
7682

77-
foreach (var link in graph.Links)
83+
// Links are ordered by their end (each link ends half a row below the commit it starts from).
84+
var links = graph.Links;
85+
for (var idx = FindFirstVisible(links, static l => l.End.Y, top, rowHeight); idx < links.Count; idx++)
7886
{
87+
var link = links[idx];
7988
var startY = link.Start.Y * rowHeight;
8089
var endY = link.End.Y * rowHeight;
8190

@@ -168,11 +177,14 @@ private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double
168177
private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, double top, double bottom, double rowHeight)
169178
{
170179
var dotFill = DotBrush;
171-
var dotFillPen = new Pen(dotFill, 2);
172-
var grayedPen = new Pen(Brushes.Gray, Models.CommitGraph.Pens[0].Thickness);
180+
var dotFillPen = _dotFillPen ??= new Pen(dotFill, 2);
181+
var grayedPen = Models.CommitGraph.GrayedDotPen;
173182

174-
foreach (var dot in graph.Dots)
183+
// Dots are ordered by their center (one dot per commit row).
184+
var dots = graph.Dots;
185+
for (var idx = FindFirstVisible(dots, static d => d.Center.Y, top, rowHeight); idx < dots.Count; idx++)
175186
{
187+
var dot = dots[idx];
176188
var center = new Point(dot.Center.X, dot.Center.Y * rowHeight);
177189

178190
if (center.Y < top)
@@ -199,7 +211,24 @@ private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, doubl
199211
}
200212
}
201213

214+
private static int FindFirstVisible<T>(List<T> items, Func<T, double> getBottom, double top, double rowHeight)
215+
{
216+
var lo = 0;
217+
var hi = items.Count;
218+
while (lo < hi)
219+
{
220+
var mid = lo + (hi - lo) / 2;
221+
if (getBottom(items[mid]) * rowHeight < top)
222+
lo = mid + 1;
223+
else
224+
hi = mid;
225+
}
226+
227+
return lo;
228+
}
229+
202230
private Models.CommitGraph _graph = null;
203231
private Models.CommitGraphLayout _layout = null;
232+
private Pen _dotFillPen = null;
204233
}
205234
}

0 commit comments

Comments
 (0)