donut

by phloe

JavaScript

var donut = (function (doc) {
    
    var doublePI = Math.PI * 2;
    
    function create (size, thickness, data, spacing) {
        var ns = "http://www.w3.org/2000/svg";
        var sum = 0;
        var offset = 0;
        var outerRadius = size / 2;
        var innerRadius = outerRadius - thickness;
        
        data.forEach(function (d) {
            sum += d.value;
        });
        
        spacing = spacing || 0;

        var svg = doc.createElementNS(ns, "svg");
        svg.setAttribute("width", size + "px");
        svg.setAttribute("height", size + "px");
        var path = doc.createElementNS(ns, "path");
        data.forEach(function (d) {
            var _path = path.cloneNode(true);
            _path.setAttribute("d", wedge(outerRadius, innerRadius, d.value, sum, offset, spacing));
            if (d.color) {
                _path.setAttribute("fill", d.color);
            }
            svg.appendChild(_path);
            offset += d.value;
        });
        return svg;
    }
    
    function wedge (or, ir, value, sum, offset, spacing) {
        var op = [or, 0];
        var ip = [or, or - ir];
        var flip = 0;
        // if wedge is larger than 180 degrees large-arc-flag needs to be flipped
        if (value / sum > 0.5) {
            flip = 1;
        }
        // calculate correct spacing offset for outer and inner edges
        var os = 0;
        var is = 0;
        if (spacing) {
            var s = spacing / 2;
            os = (Math.asin(s / or) / doublePI) * sum;
            is = (Math.asin(s / ir) / doublePI) * sum;
        }
        
        // concatenate path value
        return [
            "M", (offset || spacing) ? rotate(or, or, op, (offset + os) / sum) : op,
            "A", or, or, 0, flip, 1, rotate(or, or, op, (value + offset - os) / sum),
            "L", rotate(or, or, ip, (value + offset - is) / sum),
            "A", ir, ir, 0, flip, 0, (offset || spacing) ? rotate(or, or, ip, (offset + is) / sum) : ip,
    ...