Source: arcdiagram.js

/** 
 * @brief Module for rendering an arc diagram to a svg canvas with d3
 *
 * @file suffixtree.cpp
 * @author David Pfahler
 * @author Matthias Gusenbauer
 */ 

var width  = $('.starter-template').width();     //> width of svg image
var height = 400;                           //> height of svg image       

var data = [];
var LoD  = 1;
var transparency = 0.3;
var hoverTransparency = 1;
var isMirrored = false;
var isSmaller = false;
var fC = "lightsteelblue"
var sC = "darkslateblue"

/** Method to parse the input of the textfield or the file
 * @param input The string of data to be parsed and visualized as arcs 
 * @return Struct of characters and essential matching pair arcs to draw
 */
function parseInput(input)
{

  var characters = input.split('');
  var tree = new SuffixTree(input);
    
  // definition 1 - maximal matching pairs
  var mmPairs = tree.getMaximalMatchingPairs();
    
  // defintion 2 - get repetition regions
  var regions = tree.getRepRegion(input);

  // definition 3 - get essential matching pairs
  var emPairs = tree.getEssentialMatchinPairs(mmPairs, regions);
    
  return {
    characters: characters,
    arcs: emPairs
  };
}

/** Method to calculate the correct arc height given the distance of the patterns
 * @param arcs All arcs for a pattern
 * @return The maximum arc height
 */
function getMaxArcHeight(arcs)
{
  maxPairDistance = 0;
  for(i = 0; i < arcs.length; i++)
  {
    currDistance = (arcs[i].targetPos - arcs[i].sourcePos + arcs[i].numberEle)/2
    if(currDistance > maxPairDistance)
    {
      maxPairDistance = currDistance;
    }
  }

  return Math.max(maxPairDistance,getMaxArcWidth(arcs));
}

/** Method to calculate the correct arc width given the distance of the patterns
 * @param arcs All arcs for a pattern
 * @return The maximum arc width
 */
function getMaxArcWidth(arcs)
{
  maxPairDimension = 0;
  for(i = 0; i < arcs.length; i++)
  {
    if(arcs[i].numberEle > maxPairDimension)
    {
      maxPairDimension = arcs[i].numberEle;
    }
  }

  return maxPairDimension;
}
    
/** Draw the arc diagram
 */
function draw(){

  // clear the chart and redraw everything
  $("#chart").empty();

  arcs = [];
  mirroredArcs = [];
  if(!isSmaller)
    mirroredArcs = data.arcs;

  //filter the data for LoD
  for(j = 0; j < data.arcs.length; j++)
  {
    if(data.arcs[j].numberEle >= LoD)
      arcs.push(data.arcs[j]);
    else if(isMirrored && isSmaller)
      mirroredArcs.push(data.arcs[j]);
  }

  var numElements = data.characters.length;
  var fontWidth = width / Math.max(numElements,1);
  var fontHeight = fontWidth*1.2;

  var x = d3.scale.linear()
   .domain([0,numElements])
   .range([0,width]);

  height = (getMaxArcHeight(arcs) + 1)* fontHeight;
  upperArcLine = height - fontHeight;
  if(isMirrored)
  {
    var mirroredArcHeight = getMaxArcHeight(mirroredArcs) * fontHeight;
    height = height + mirroredArcHeight; 
  }

  var colour = d3.scale.linear()
   .domain([0,getMaxArcWidth(arcs)])
   .range([fC,sC]);        // nice coloring

  // create new chart with the specified width and height
  var chart = d3.select("svg")
    .attr("width", width)
    .attr("height", height);

  var textGroup = chart.append("g")
    .attr("id","textGroup");

  var textPlot = textGroup.selectAll("g");
  //plot the characters along the x-axis
  if(fontWidth > 3)
  {
    textPlot = textGroup
    .selectAll("g")
    .data(data.characters)
    .enter()
    .append("g")
    .attr("transform", function(d,i) { 
      x1 = i * fontWidth;
      y1 = upperArcLine;
      return "translate(" + x1 + "," + y1 + ")"; 
    })
    .attr("id",function(d,i){return i;});

  textPlot.append("text")
    .attr("y",fontHeight/2)
    .attr("x",fontWidth/2)
    .attr("style","font-size:"+fontWidth+'px')
    .text(function(d) {return d;});
  }
  

  //plot the arcs like defined in the arcs array of the parsed data
  var arcGroup = chart.append("g")
    .attr("id","arcGroup");

  var mirroredArcGroup = chart.append("g")
    .attr("id","mirroredArcGroup");

  arcGroup.selectAll("path")
    .data(arcs)
    .enter().append("path")
    .style("stroke-opacity",transparency)
    .attr("d",function(d,i){
        patternCenterDistance = (d.numberEle*fontWidth)/2;
        x1 = x(d.sourcePos) + patternCenterDistance;
        x2 = x(d.targetPos) + patternCenterDistance;
        y = upperArcLine-6;
        arcCenter = (x2 - x1)/2;
        return "M" + x1 + ","+y+" A "+ arcCenter +","+ arcCenter +" 0 0 1 " + x2 + ","+y;
      })
    .attr("stroke", function(d,i){return colour(d.numberEle);})
    .attr("stroke-width",function(d,i){return d.numberEle*fontWidth;})
    .on("click",function(d){ 
       arcGroup
        .selectAll("path")
        .style("stroke-opacity",function(x){if(d.text==x.text) return hoverTransparency; else return transparency;});
    })
    .on("mouseover", function(d){
      arcGroup
        .selectAll("path")
        .style("stroke-opacity",function(x){if(d==x) return hoverTransparency; else return transparency;});
      textPlot
        .select("text")
        .style("fill-opacity",function(x,n){
          if( (n >= d.sourcePos && n < d.sourcePos + d.numberEle) ||  
            (n >= d.targetPos && n < d.targetPos + d.numberEle))
            return hoverTransparency; else return transparency;});
      tooltip.style("visibility", "visible").text(d.text);
      return 1;
    })
    .on("mousemove", function(){
      bb = tooltip.node().getBoundingClientRect();
      w = d3.event.pageX-bb.width/2;
      h = d3.event.pageY-bb.height - 13;
      return tooltip
        .style("top", h+"px")
        .style("left",w+"px");})
    .on("mouseout", function(){
      arcGroup
        .selectAll("path")
        .style("stroke-opacity",transparency);
      textPlot
        .select("text")
        .style("fill-opacity",hoverTransparency);
      return tooltip.style("visibility", "hidden");
    });

  if(isMirrored)
  {
    mirroredArcGroup.selectAll("path")
      .data(mirroredArcs)
      .enter().append("path")
      .style("stroke-opacity",transparency)
      .attr("d",function(d,i){
          patternCenterDistance = (d.numberEle*fontWidth)/2;
          x1 = x(d.sourcePos) + patternCenterDistance;
          x2 = x(d.targetPos) + patternCenterDistance;
          y = upperArcLine+fontHeight/2 + 6;
          arcCenter = (x2 - x1)/2;
          return "M" + x1 + ","+y+" A "+ arcCenter +","+ arcCenter +" 0 1 0 " + x2 + ","+y;
        })
      .attr("stroke", function(d,i){return colour(d.numberEle);})
      .attr("stroke-width",function(d,i){return d.numberEle*fontWidth;})
      .on("click",function(d){
        arcGroup
        .selectAll("path")
        .style("stroke-opacity",function(x){if(d.text==x.text) return hoverTransparency; else return transparency;});
        mirroredArcGroup
        .selectAll("path")
        .style("stroke-opacity",function(x){if(d.text==x.text) return hoverTransparency; else return transparency;});
      })
      .on("mouseover", function(d){
        mirroredArcGroup
          .selectAll("path")
          .style("stroke-opacity",function(x){if(d==x) return hoverTransparency; else return transparency;});
        arcGroup
          .selectAll("path")
          .style("stroke-opacity",function(x){if(d==x) return hoverTransparency; else return transparency;});
        textPlot
          .select("text")
          .style("fill-opacity",function(x,n){
            if( (n >= d.sourcePos && n < d.sourcePos + d.numberEle) ||  
              (n >= d.targetPos && n < d.targetPos + d.numberEle))
              return hoverTransparency; else return transparency;});
        tooltip.style("visibility", "visible").text(d.text);
        return 1;
      })
      .on("mousemove", function(){
      bb = tooltip.node().getBoundingClientRect();
      w = d3.event.pageX-bb.width/2;
      h = d3.event.pageY-bb.height - 13;
      return tooltip
        .style("top", h+"px")
        .style("left",w+"px");})
      .on("mouseout", function(){
        mirroredArcGroup
          .selectAll("path")
          .style("stroke-opacity",transparency);
        arcGroup
          .selectAll("path")
          .style("stroke-opacity",transparency);
        textPlot
          .select("text")
          .style("fill-opacity",hoverTransparency);
        return tooltip.style("visibility", "hidden");
      });
  }
}

// Figure 1: 28746391473564827639137
// Figure 2: 1234567abcde1234567fghij1234567
// Figure 3: 1010101010101010
// Figure 4: abcd111110000011111abcd
// Figure 5: 11111000110111001001011110001101110001010