From 59b6de93cebe4aaa8cca121e6147fd7c83786f17 Mon Sep 17 00:00:00 2001
From: Sato Hiroyuki <sathiroyuki@gmail.com>
Date: Wed, 30 Jan 2013 22:20:00 +0900
Subject: [PATCH] Improve overlap of lines in network graph

---
 lib/gitlab/graph/commit.rb                |  4 +-
 lib/gitlab/graph/json_builder.rb          | 88 +++++++++++++++++------
 vendor/assets/javascripts/branch-graph.js | 25 +++++--
 3 files changed, 91 insertions(+), 26 deletions(-)

diff --git a/lib/gitlab/graph/commit.rb b/lib/gitlab/graph/commit.rb
index a6bf23a2381..13c8ebc9952 100644
--- a/lib/gitlab/graph/commit.rb
+++ b/lib/gitlab/graph/commit.rb
@@ -5,12 +5,13 @@ module Gitlab
     class Commit
       include ActionView::Helpers::TagHelper
 
-      attr_accessor :time, :space, :refs
+      attr_accessor :time, :space, :refs, :parent_spaces
 
       def initialize(commit)
         @_commit = commit
         @time = -1
         @space = 0
+        @parent_spaces = []
       end
 
       def method_missing(m, *args, &block)
@@ -28,6 +29,7 @@ module Gitlab
         }
         h[:time]    = time
         h[:space]   = space
+        h[:parent_spaces]   = parent_spaces
         h[:refs]    = refs.collect{|r|r.name}.join(" ") unless refs.nil?
         h[:id]      = sha
         h[:date]    = date
diff --git a/lib/gitlab/graph/json_builder.rb b/lib/gitlab/graph/json_builder.rb
index a3157aa4b4d..b25e313df40 100644
--- a/lib/gitlab/graph/json_builder.rb
+++ b/lib/gitlab/graph/json_builder.rb
@@ -16,7 +16,6 @@ module Gitlab
 
         @commits = collect_commits
         @days = index_commits
-        @space = 0
       end
 
       def to_json(*args)
@@ -53,7 +52,7 @@ module Gitlab
       #
       # @return [Array<TimeDate>] list of commit dates corelated with time on commits
       def index_commits
-        days, heads = [], []
+        days, heads, times = [], [], []
         map = {}
 
         commits.reverse.each_with_index do |c,i|
@@ -61,6 +60,7 @@ module Gitlab
           days[i] = c.committed_date
           map[c.id] = c
           heads += c.refs unless c.refs.nil?
+          times[i] = c
         end
 
         heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
@@ -86,9 +86,62 @@ module Gitlab
           end
         end
 
+        # find parent spaces for not overlap lines
+        times.each do |c|
+          c.parent_spaces.concat(find_free_parent_spaces(c, map, times))
+        end
+
         days
       end
 
+      def find_free_parent_spaces(commit, map, times)
+        spaces = []
+
+        commit.parents.each do |p|
+          if map.include?(p.id) then
+            parent = map[p.id]
+
+            range = if commit.time < parent.time then
+                      commit.time..parent.time
+                    else
+                      parent.time..commit.time
+                    end
+
+            space = if commit.space >= parent.space then
+                      find_free_parent_space(range, map, parent.space, 1, commit.space, times)
+                    else
+                      find_free_parent_space(range, map, parent.space, -1, parent.space, times)
+                    end
+
+            mark_reserved(range, space)
+            spaces << space
+          end
+        end
+
+        spaces
+      end
+
+      def find_free_parent_space(range, map, space_base, space_step, space_default, times)
+        if is_overlap?(range, times, space_default) then
+          find_free_space(range, map, space_base, space_step)
+        else
+          space_default
+        end
+      end
+
+      def is_overlap?(range, times, overlap_space)
+        range.each do |i|
+          if i != range.first &&
+            i != range.last &&
+            times[i].space == overlap_space then
+
+            return true;
+          end
+        end
+
+        false
+      end
+
       # Add space mark on commit and its parents
       #
       # @param [Graph::Commit] the commit object.
@@ -98,8 +151,9 @@ module Gitlab
         if leaves.empty?
           return
         end
-        @space = find_free_space(leaves, map)
-        leaves.each{|l| l.space = @space}
+        time_range = leaves.last.time..leaves.first.time
+        space = find_free_space(time_range, map, 1, 2)
+        leaves.each{|l| l.space = space}
         # and mark it as reserved
         min_time = leaves.last.time
         parents = leaves.last.parents.collect
@@ -116,7 +170,7 @@ module Gitlab
         else
           max_time = parent_time - 1
         end
-        mark_reserved(min_time..max_time, @space)
+        mark_reserved(min_time..max_time, space)
 
         # Visit branching chains
         leaves.each do |l|
@@ -133,28 +187,22 @@ module Gitlab
         end
       end
 
-      def find_free_space(leaves, map)
-        time_range = leaves.last.time..leaves.first.time
+      def find_free_space(time_range, map, space_base, space_step)
         reserved = []
         for day in time_range
           reserved += @_reserved[day]
         end
-        space = base_space(leaves, map)
-        while (reserved.include? space) || (space == @space) do
-          space += 1
-        end
-
-        space
-      end
 
-      def base_space(leaves, map)
-        parents = []
-        leaves.each do |l|
-          parents.concat l.parents.collect.select{|p| map.include? p.id and map[p.id].space.nonzero?}
+        space = space_base
+        while reserved.include?(space) do
+          space += space_step
+          if space <= 0 then
+            space_step *= -1
+            space = space_base + space_step
+          end
         end
 
-        space = parents.map{|p| map[p.id].space}.max || 0
-        space += 1
+        space
       end
 
       # Takes most left subtree branch of commits
diff --git a/vendor/assets/javascripts/branch-graph.js b/vendor/assets/javascripts/branch-graph.js
index 93849c79e80..76494196e43 100644
--- a/vendor/assets/javascripts/branch-graph.js
+++ b/vendor/assets/javascripts/branch-graph.js
@@ -103,8 +103,9 @@
     
     for (i = 0; i < this.commitCount; i++) {
       var x = offsetX + 20 * this.commits[i].time
-        , y = offsetY + 20 * this.commits[i].space
-        , c;
+        , y = offsetY + 10 * this.commits[i].space
+        , c
+        , ps;
       
       // Draw dot
       r.circle(x, y, 3).attr({
@@ -115,9 +116,11 @@
       // Draw lines
       for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
         c = this.preparedCommits[this.commits[i].parents[j][0]];
+        ps = this.commits[i].parent_spaces[j];
         if (c) {
           var cx = offsetX + 20 * c.time
-            , cy = offsetY + 20 * c.space;
+            , cy = offsetY + 10 * c.space
+            , psy = offsetY + 10 * ps;
           if (c.space == this.commits[i].space) {
             r.path([
               "M", x, y,
@@ -128,13 +131,25 @@
             });
 
           } else if (c.space < this.commits[i].space) {
-            r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C", x - 5, y, x - 17, y + 2, x - 20, y - 5, "L", cx, y - 5, cx, cy])
+            r.path([
+                "M", x - 5, y,
+                "l-5-2,0,4,5,-2",
+                "L", x - 10, y,
+                "L", x - 15, psy,
+                "L", cx + 5, psy,
+                "L", cx, cy])
             .attr({
               stroke: this.colors[this.commits[i].space], 
               "stroke-width": 2
             });
           } else {
-            r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5L", x - 10, y + 20, "L", x - 10, cy, cx, cy])
+            r.path([
+                "M", x - 3, y + 6,
+                "l-4,3,4,2,0,-5",
+                "L", x - 5, y + 10,
+                "L", x - 10, psy,
+                "L", cx + 5, psy,
+                "L", cx, cy])
             .attr({
               stroke: this.colors[c.space], 
               "stroke-width": 2
-- 
GitLab