Avoid overlaps in relation visualization

Change-Id: I69b0c297a3160b50f4c4cdf163f0966cdeef539d
diff --git a/dev/js/src/match/relations.js b/dev/js/src/match/relations.js
index 77d05ec..040a674 100644
--- a/dev/js/src/match/relations.js
+++ b/dev/js/src/match/relations.js
@@ -6,11 +6,16 @@
   return {
     create : function (snippet) {
       var obj = Object.create(this)._init(snippet);
-      obj._tokens = ["Das", "ist", "ja", "toll"];
+      obj._tokens = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
       obj._tokenElements = [];
       obj._arcs = [
-        { start: 1, end: 3, label: "small" },
-        { start: 3, end: 0, label: "large" }
+        { start: 0, end: 1, label: "a" },
+        { start: 0, end: 1, label: "b" },
+        { start: 1, end: 2, label: "c" },
+        { start: 0, end: 2, label: "d" },
+        { start: 1, end: 5, label: "e" },
+        { start: 4, end: 8, label: "f" },
+        { start: 6, end: 7, label: "g" },
       ]
       obj.maxArc = 200; // maximum height of the bezier control point
       return obj;
@@ -37,10 +42,10 @@
 
     // Create an arc with a label
     // Potentially needs a height parameter for stacks
-    _createArc : function (start, end, label) {
+    _drawArc : function (arc) {
 
-      var startPos = this._tokenPoint(this._tokenElements[start]);
-      var endPos = this._tokenPoint(this._tokenElements[end]);
+      var startPos = this._tokenPoint(this._tokenElements[arc.first]);
+      var endPos = this._tokenPoint(this._tokenElements[arc.last]);
 
       var y = 0;
       var g = this._c("g");
@@ -49,22 +54,24 @@
       // Create arc
       var middle = Math.abs(endPos - startPos) / 2;
 
-      var cHeight = middle < this.maxArc ? middle : this.maxArc;
+      // var cHeight = middle < this.maxArc ? middle : this.maxArc;
+      // TODO: take the number of tokens into account!
+      var cHeight = 10 + arc.overlaps * 12 + (middle / 2);
 
       var x = Math.min(startPos, endPos);
       
-      var arc = "M "+ startPos + " " + y +
+      var arcE = "M "+ startPos + " " + y +
           " C " + startPos + " " + (y-cHeight) +
           " " + endPos + " " + (y-cHeight) +
           " " + endPos + " " + y;
-      p.setAttribute("d", arc);
+      p.setAttribute("d", arcE);
 
-      if (label !== undefined) {
+      if (arc.label !== undefined) {
         var labelE = g.appendChild(this._c("text"));
         labelE.setAttribute("x", x + middle);
-        labelE.setAttribute("y", -1 * cHeight + 10);
+        labelE.setAttribute("y", -1 * cHeight);
         labelE.setAttribute("text-anchor", "middle");
-        labelE.appendChild(document.createTextNode(label));
+        labelE.appendChild(document.createTextNode(arc.label));
       };
       
       return g;
@@ -84,7 +91,7 @@
 
     // Add a relation with a start, an end,
     // a direction value and a label text
-    addArc : function (start, end, direction, label) {
+    addArc : function (arc) {
     },
 
     /*
@@ -101,6 +108,73 @@
       //       -> found: Add +1 to the level of the (lpos)
       //    c) If the new tag is smaller than the previous element,
       //       reorder
+
+      // Normalize start and end
+      var sortedArcs = this._arcs.map(function (v) {
+        if (v.start < v.end) {
+          v.first = v.start;
+          v.last = v.end;
+          v.length = v.end - v.start;
+        }
+        else {
+          v.first = v.end;
+          v.last = v.start;
+          v.length = v.start - v.end;
+        };
+        return v;
+      });
+
+      // Sort based on length
+      sortedArcs.sort(function (a, b) {
+        if (a.length < b.length)
+          return -1;
+        else
+          return 1;
+      });
+
+      var arcStack = [];
+
+      // Iterate over all arc definitions
+      for (var i = 0; i < sortedArcs.length; i++) {
+        var currentArc = sortedArcs[i];
+
+        // Check the stack order
+        var overlaps = 0;
+
+        for (var j = (arcStack.length - 1); j >= 0; j--) {
+          var checkArc = arcStack[j];
+
+          // (a..(b..b)..a)
+          if (currentArc.first <= checkArc.first && currentArc.last >= checkArc.last) {
+            overlaps = checkArc.overlaps + 1;
+            break;
+          }
+
+          // (a..(b..a)..b)
+          else if (currentArc.first < checkArc.first && currentArc.last > checkArc.first) {
+            overlaps = checkArc.overlaps + (currentArc.length == checkArc.length ? 0 : 1);
+          }
+
+          // (b..(a..b)..a)
+          else if (currentArc.first < checkArc.last && currentArc.last > checkArc.last) {
+            overlaps = checkArc.overlaps + (currentArc.length == checkArc.length ? 0 : 1);
+          };
+        };
+
+        // Set overlaps
+        currentArc.overlaps = overlaps;
+
+        arcStack.push(currentArc);
+
+        // Although it is already sorted,
+        // the new item has to be put at the correct place
+        // TODO: Use something like splice() instead
+        arcStack.sort(function (a,b) {
+          b.overlaps - a.overlaps
+        });
+      };
+
+      return arcStack;
     },
     
     show : function () {
@@ -134,7 +208,7 @@
         "transform",
         "translate(0," + textBox.y +")"
       );
-
+      
       /*
        * TODO:
        * Before creating the arcs, the height of the arc
@@ -142,9 +216,9 @@
        * That means, the arcs need to be presorted, so massively
        * overlapping arcs are taken first.
        */
-      for (var i in this._arcs) {
-        var arc = this._arcs[i];
-        this.arcs.appendChild(this._createArc(arc.start, arc.end, arc.label));
+      var sortedArcs = this._sortArcs();
+      for (var i in sortedArcs) {
+        this.arcs.appendChild(this._drawArc(sortedArcs[i]));
       };
     }
   }