@@ -25,12 +25,12 @@ def __init__(self):
2525 self .trimmed = False
2626 self .prevRef = None
2727 self .topref = None
28- self .i = 0
2928 self .numCommits = settings .commits
3029 self .defaultNumCommits = settings .commits
3130 self .selected_branches = []
3231 self .stop = False
3332 self .zone_title_offset = 2.6 if platform .system () == "Windows" else 2.6
33+ self .arrow_map = []
3434
3535 self .logo = m .ImageMobject (settings .logo )
3636 self .logo .width = 3
@@ -81,31 +81,50 @@ def get_commits(self, start="HEAD"):
8181 self .get_commits (start = start )
8282
8383 def parse_commits (
84- self , commit , prevCircle = None , shift = numpy .array ([0.0 , 0.0 , 0.0 ]), dots = False
84+ self , commit , i , prevCircle = None , shift = numpy .array ([0.0 , 0.0 , 0.0 ])
8585 ):
86- if self .stop :
87- return
88- if self . i < self .numCommits and commit in self .commits :
86+ isNewCommit = commit . hexsha not in self .drawnCommits
87+
88+ if i < self .numCommits and commit in self .commits :
8989 commitId , circle , arrow , hide_refs = self .draw_commit (
90- commit , prevCircle , shift , dots
90+ commit , i , prevCircle , shift
9191 )
9292
9393 if commit != "dark" :
94- if not hide_refs and not self .stop :
95- self .draw_head (commit , commitId )
96- self .draw_branch (commit )
97- self .draw_tag (commit )
98- self .draw_arrow (prevCircle , arrow )
99- if self .stop :
100- return
101- if self .i == 0 and len (self .drawnRefs ) < 2 :
94+ if not hide_refs and isNewCommit :
95+ self .draw_head (commit , i , commitId )
96+ self .draw_branch (commit , i )
97+ self .draw_tag (commit , i )
98+ if (
99+ not isinstance (arrow , m .CurvedArrow )
100+ and [arrow .start .tolist (), arrow .end .tolist ()] not in self .arrow_map
101+ ):
102+ self .draw_arrow (prevCircle , arrow )
103+ self .arrow_map .append ([arrow .start .tolist (), arrow .end .tolist ()])
104+ elif (
105+ isinstance (arrow , m .CurvedArrow )
106+ and [arrow .get_start ().tolist (), arrow .get_end ().tolist ()]
107+ not in self .arrow_map
108+ ):
109+ self .draw_arrow (prevCircle , arrow )
110+ self .arrow_map .append (
111+ [arrow .get_start ().tolist (), arrow .get_end ().tolist ()]
112+ )
113+ if i == 0 and len (self .drawnRefs ) < 2 :
102114 self .draw_dark_ref ()
103115
104- if self .i < len (self .commits ) - 1 :
105- self .i += 1
106- self .parse_commits (self .commits [self .i ], circle , dots = True )
107- else :
108- self .i = 0
116+ if i < self .numCommits : # len(self.commits) - 1:
117+ i += 1
118+ commitParents = list (commit .parents )
119+ if len (commitParents ) > 0 :
120+ if settings .invert_branches :
121+ commitParents .reverse ()
122+
123+ if settings .hide_merged_chains :
124+ self .parse_commits (commitParents [0 ], i , circle )
125+ else :
126+ for p in range (len (commitParents )):
127+ self .parse_commits (commitParents [p ], i , circle )
109128
110129 def show_intro (self ):
111130 if settings .animate and settings .show_intro :
@@ -170,9 +189,7 @@ def get_centers(self):
170189 centers .append (commit .get_center ())
171190 return centers
172191
173- def draw_commit (
174- self , commit , prevCircle , shift = numpy .array ([0.0 , 0.0 , 0.0 ]), dots = False
175- ):
192+ def draw_commit (self , commit , i , prevCircle , shift = numpy .array ([0.0 , 0.0 , 0.0 ])):
176193 if commit == "dark" :
177194 commitFill = m .WHITE if settings .light_mode else m .BLACK
178195 elif len (commit .parents ) <= 1 :
@@ -193,29 +210,54 @@ def draw_commit(
193210 prevCircle , m .RIGHT if settings .reverse else m .LEFT , buff = 1.5
194211 )
195212
196- start = (
197- prevCircle .get_center ()
198- if prevCircle
199- else (m .LEFT if settings .reverse else m .RIGHT )
200- )
201- end = circle .get_center ()
213+ while any ((circle .get_center () == c ).all () for c in self .get_centers ()):
214+ circle .next_to (circle , m .DOWN , buff = 3.5 )
215+
216+ isNewCommit = commit .hexsha not in self .drawnCommits
217+
218+ if isNewCommit :
219+ start = (
220+ prevCircle .get_center ()
221+ if prevCircle
222+ else (m .LEFT if settings .reverse else m .RIGHT )
223+ )
224+ end = circle .get_center ()
225+ else :
226+ circle .move_to (self .drawnCommits [commit .hexsha ].get_center ())
227+ start = (
228+ prevCircle .get_center ()
229+ if prevCircle
230+ else (m .LEFT if settings .reverse else m .RIGHT )
231+ )
232+ end = self .drawnCommits [commit .hexsha ].get_center ()
233+
234+ arrow = m .Arrow (start , end , color = self .fontColor )
202235
203236 if commit == "dark" :
204237 arrow = m .Arrow (
205238 start , end , color = m .WHITE if settings .light_mode else m .BLACK
206239 )
207- elif commit .hexsha in self .drawnCommits :
208- end = self .drawnCommits [commit .hexsha ].get_center ()
209- arrow = m .Arrow (start , end , color = self .fontColor )
210- self .stop = True
211- else :
212- arrow = m .Arrow (start , end , color = self .fontColor )
213240
214241 length = numpy .linalg .norm (start - end ) - (1.5 if start [1 ] == end [1 ] else 3 )
215242 arrow .set_length (length )
243+ angle = arrow .get_angle ()
244+ lineRect = (
245+ m .Rectangle (height = 0.1 , width = length , color = "#123456" )
246+ .move_to (arrow .get_center ())
247+ .rotate (angle )
248+ )
249+
250+ for commitCircle in self .drawnCommits .values ():
251+ inter = m .Intersection (lineRect , commitCircle )
252+ if inter .has_points ():
253+ arrow = m .CurvedArrow (start , end , color = self .fontColor )
254+ if start [1 ] == end [1 ]:
255+ arrow .shift (m .UP * 1.25 )
256+ if start [0 ] < end [0 ] and start [1 ] == end [1 ]:
257+ arrow .flip (m .RIGHT ).shift (m .UP )
216258
217259 commitId , commitMessage , commit , hide_refs = self .build_commit_id_and_message (
218- commit , dots
260+ commit , i
219261 )
220262 commitId .next_to (circle , m .UP )
221263
@@ -231,15 +273,15 @@ def draw_commit(
231273 color = self .fontColor ,
232274 ).next_to (circle , m .DOWN )
233275
234- if settings .animate and commit != "dark" and not self . stop :
276+ if settings .animate and commit != "dark" and isNewCommit :
235277 self .play (
236278 self .camera .frame .animate .move_to (circle .get_center ()),
237279 m .Create (circle ),
238280 m .AddTextLetterByLetter (commitId ),
239281 m .AddTextLetterByLetter (message ),
240282 run_time = 1 / settings .speed ,
241283 )
242- elif not self . stop :
284+ elif isNewCommit :
243285 self .add (circle , commitId , message )
244286 else :
245287 return commitId , circle , arrow , hide_refs
@@ -252,7 +294,17 @@ def draw_commit(
252294
253295 return commitId , circle , arrow , hide_refs
254296
255- def build_commit_id_and_message (self , commit , dots = False ):
297+ def get_nonparent_branch_names (self ):
298+ branches = [b for b in self .repo .heads if not b .name .startswith ("remotes/" )]
299+ exclude = []
300+ for b1 in branches :
301+ for b2 in branches :
302+ if b1 .name != b2 .name :
303+ if self .repo .is_ancestor (b1 .commit , b2 .commit ):
304+ exclude .append (b1 .name )
305+ return [b for b in branches if b .name not in exclude ]
306+
307+ def build_commit_id_and_message (self , commit , i , dots = False ):
256308 hide_refs = False
257309 if commit == "dark" :
258310 commitId = m .Text ("" , font = "Monospace" , font_size = 20 , color = self .fontColor )
@@ -276,7 +328,7 @@ def build_commit_id_and_message(self, commit, dots=False):
276328 commitMessage = commit .message .split ("\n " )[0 ][:40 ].replace ("\n " , " " )
277329 return commitId , commitMessage , commit , hide_refs
278330
279- def draw_head (self , commit , commitId ):
331+ def draw_head (self , commit , i , commitId ):
280332 if commit .hexsha == self .repo .head .commit .hexsha :
281333 headbox = m .Rectangle (color = m .BLUE , fill_color = m .BLUE , fill_opacity = 0.25 )
282334 headbox .width = 1
@@ -297,10 +349,10 @@ def draw_head(self, commit, commitId):
297349 self .drawnRefs ["HEAD" ] = head
298350 self .prevRef = head
299351
300- if self . i == 0 :
352+ if i == 0 :
301353 self .topref = self .prevRef
302354
303- def draw_branch (self , commit ):
355+ def draw_branch (self , commit , i ):
304356 x = 0
305357
306358 remote_tracking_branches = self .get_remote_tracking_branches ()
@@ -348,17 +400,17 @@ def draw_branch(self, commit):
348400 self .toFadeOut .add (branchRec , branchText )
349401 self .drawnRefs [branch ] = fullbranch
350402
351- if self . i == 0 :
403+ if i == 0 :
352404 self .topref = self .prevRef
353405
354406 x += 1
355407 if x >= settings .max_branches_per_commit :
356408 return
357409
358- def draw_tag (self , commit ):
410+ def draw_tag (self , commit , i ):
359411 x = 0
360412
361- if settings .hide_first_tag and self . i == 0 :
413+ if settings .hide_first_tag and i == 0 :
362414 return
363415
364416 for tag in self .repo .tags :
@@ -394,7 +446,7 @@ def draw_tag(self, commit):
394446
395447 self .toFadeOut .add (tagRec , tagText )
396448
397- if self . i == 0 :
449+ if i == 0 :
398450 self .topref = self .prevRef
399451
400452 x += 1
@@ -878,7 +930,7 @@ def get_nondark_commits(self):
878930 nondark_commits .append (commit )
879931 return nondark_commits
880932
881- def draw_ref (self , commit , top , text = "HEAD" , color = m .BLUE ):
933+ def draw_ref (self , commit , i , top , text = "HEAD" , color = m .BLUE ):
882934 refText = m .Text (text , font = "Monospace" , font_size = 20 , color = self .fontColor )
883935 refbox = m .Rectangle (
884936 color = color ,
@@ -901,7 +953,7 @@ def draw_ref(self, commit, top, text="HEAD", color=m.BLUE):
901953 self .drawnRefs [text ] = ref
902954 self .prevRef = ref
903955
904- if self . i == 0 :
956+ if i == 0 :
905957 self .topref = self .prevRef
906958
907959 def draw_dark_ref (self ):
0 commit comments