Edit in JSFiddle

/**
 * With this demo, you are able to get the idea of transition and intepolate with D3
 */

// the 3 sets of data we would like to visualize
var data = [1, 1, 1, 6, 7, 8, 5, 5, 20, 25, 9, 8, 2, 2, 2, 20];

var theStatusNameMapping = ['Running', 'Stopping', 'Paused', 'Halt', 'Terminated', 'Stopped', 'Bootstraping', 'InSync', 'Out-Of-Sync', 'Impaired', 'Degraded', 'Unreachabled', 'Destroyed', 'Error1', 'Error2', 'Error3', 'Error4']
var dataScaleName = d3.scale.ordinal()
  .range(theStatusNameMapping)

var arcOuterRadius = 130;
var arcTextOffsetTimes = 1.3;
var textMinSpaceHeight = 13;
var charMinSpaceWidth = 10; // px

var pathStrokeWidth = 5;

// surely, create a svg workspace first
var theSvgG = d3.select('body').append('svg').attr({
  width: 600,
  height: 600
}).append('g').attr('transform', 'translate(300, 300)');

// the color of the 3 annuluses we will assign later
var colorScale = d3.scale.category20();

var pieLayoutData = d3.layout.pie().sort(null);


// the arc Path d generator
var arc = d3.svg.arc()
  .innerRadius(100)
  .outerRadius(arcOuterRadius)

// we will generate startAngle value based on last sector's endAngle
  .startAngle(function (d, index) {
    return d.startAngle
  })

// its startAngle + its radian = its endAngle
  .endAngle(function (d, index) {

    return d.endAngle


  });

// our main utils/data handler class

var DataHandler = function () {

  // let's follow CommonJS a little bit
  var exports = this;
  
  exports.data = [];
  var percentageOfCircle = [];
  var endAngleOf = [];



  var updateDatum = function (datum, attr, newValue) {
    datum[attr] = newValue;
  }

  var pieData = function (i, value) {
    if (typeof value === 'undefined') {
      return exports.data [i];
    }
    exports.data [i] = value;
  }

  // sum the array elements as a single circle radian
  var sumData = function(){
    return exports.data .reduce(function (a, b) {
      return a + b;
    });
  }



  /**
   * percentage of each array element
   * @param i Int this is the index of the given element of the array
   */

  var percentageOfCircle = function (i) {
    return percentageOfCircle[i] || (percentageOfCircle[i] = exports.data [i] / sumData())
  }

  // store end angle for all array elements
  var endAngleOfDataI = function (i, value) {

    if (typeof (value) !== 'undefined') {
      endAngleOf[i] = value;
    } else {
      return endAngleOf[i] || 0
    }

  }

  // get radian of an array element
  var getRadianByDataI = function (index, radian) {

    if (typeof radian === 'undefined') {
      // if param radian is not provided, we assume it's going to get its percentage of whole circle
      return 2 * Math.PI * percentageOfCircle(index)
    }

    return radian * percentageOfCircle(index)

  }

  // init data

  var initData = function (data) {

    exports.data = pieLayoutData(data);
    percentageOfCircle = [];
    endAngleOf = [];


    // calc text x and y attr and store back to data
    var updateTextOffset = function (datum, index) {

      var offset;

      // adjust text anchor
      var textAnchor = function (textObj) {

        textObj.textAnchor = datum.textObj.x > 0 ? 'start' : 'end';

      }

      offset = arc.centroid(datum)

      updateDatum(datum.textObj, 'x', offset[0] * arcTextOffsetTimes);
      updateDatum(datum.textObj, 'y', offset[1] * arcTextOffsetTimes);

      textAnchor(datum.textObj);

    }

    exports.data.forEach(function (e, i) {
      e.textObj = e.textObj || {};
      updateTextOffset(e, i)

    });

    // reposition label to prevent overlapping
    var reduceOverlappingOfTextLabel = function (data) {

      data.forEach(function (datum, index, array) {

        // if current (x, y) is too closed to previous (x, y)
        // then we adjust current x, y
        if (index === 0) {
          return;
        }
        var x1 = datum.textObj.x;
        var y1 = datum.textObj.y;
        var x0 = array[index - 1 ].textObj.x;
        var y0 = array[index - 1 ].textObj.y;

        var previousYChang = array[index - 1].textObj.yChange;

        if (previousYChang) {
          y1 += previousYChang;
//          datum.textObj.yChange = previousYChang;
//          datum.textObj.x *= (1 + datum.textObj.yChange/datum.textObj.y) ;

//          datum.textObj.y = y1;


        }

        var xD = x1 - x0;
        var yD = y1 - y0;


        var textSpace = dataScaleName(index).length * charMinSpaceWidth;

        if (Math.abs(yD) < textMinSpaceHeight) {


          if (Math.abs(xD) < textSpace) {


            y1 = y0 - textMinSpaceHeight;


            datum.textObj.yChange = datum.textObj.yChange || 0;

            datum.textObj.yChange += y1 - datum.textObj.y;

//            datum.textObj.x *= (1 + datum.textObj.yChange/datum.textObj.y) ;

            datum.textObj.y = y1;


          }

        }


      });

    }

    reduceOverlappingOfTextLabel(exports.data)

  };


  exports = {
    endAngleOfDataI: endAngleOfDataI,
    getRadianByDataI: getRadianByDataI,
    percentageOfCircle: percentageOfCircle,
    pieData: pieData,
    initData: initData,
    updateData: initData,
    getData: function(){
      return exports.data;
    }

}

  return exports;
}


// instantiate
var dataHandler = new DataHandler();
dataHandler.updateData(data);


var updatePath = function(theDonutG) {

  theDonutG.selectAll('path').remove();
  theDonutG.selectAll('text').remove();

  // append a Path into each group
  theDonutG.append('path')

    .attr({
      'stroke': '#ff00ff',
      'stroke-width': pathStrokeWidth,
      'fill': 'white'
    })
    .attr('d', function (d, i) {
      return arc(d, i)
    })

    .transition()
    .delay(2000)
    .duration(2000)
// dynamically change Path d with d3 interpolate
    .attrTween('d', function (d, index) {

      // get the interpolator to generate value from 0 to 2 PI radian
      // in accordance to the t (from 0 to 1) passed in later
      var i = d3.interpolateObject({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})

      return function (t) {
        var interpolateOutput = arc(i(t), index);
        return interpolateOutput;
      }
    })
    .attr('fill', function (d) {
      // style it with animation as well
      return colorScale(d.data)
    });


  theDonutG.append('text')
    .text(function (d, i) {
      return dataScaleName(i);

    })
    .style('opacity', 0)
    .attr({
      fill: "#000000",
      "font-size": "15px"
    })
    .attr('x', function (d, i) {
      return dataHandler.pieData(i).textObj.x

    })
    .attr('y', function (d, i) {
      return dataHandler.pieData(i).textObj.y


    })
    .attr('text-anchor', function (d) {

      return d.textObj.textAnchor;
    })
    .transition()
    .delay(2000)
    .style('opacity', 1)
// add text

}



// the donut group
var theDonutG = theSvgG.selectAll('.donut-sector')
  .data(dataHandler.getData())
// enter state
  .enter()
  .append('g')
  .on('mouseenter', function (d) {
    d3.select(this).transition().attr('opacity', 0.5);
  })
  .on('mouseleave', function (d) {
    d3.select(this).transition().attr('opacity', 1);
  })
  .on('click', function (d, i) {
    alert('Remove ' + dataScaleName(i));
    data.splice(i, 1);
    theStatusNameMapping.splice(i - 1, 1);
    dataScaleName.range(theStatusNameMapping)
    dataHandler.updateData(data);
    var myDonutG = theSvgG.selectAll('.donut-sector').data(dataHandler.getData());
    myDonutG.enter().append('g');
    myDonutG.exit().remove();
    updatePath(theSvgG.selectAll('.donut-sector'));

  })
  .style('cursor', 'hand')
  .attr('class', 'donut-sector');

updatePath(theDonutG);