Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Round One Side of D3 Arc

I'm using D3.js's built-in arc function to generate SVG <path>s for my data.

.attr("d", function(element, index) {

    var arc = d3.arc()
        .innerRadius(iR)
        .outerRadius(iR + 10)
        .startAngle(element[1])
        .endAngle(element[2])
        .cornerRadius(isRounded ? cR : 0);

    return arc();

});

This works perfectly, but I'd like to round one side (both corners) of certain arcs. When a corner radius is supplied with .cornerRadius(), however, it rounds all four corners.

I know there are various ways to selectively round the corners of rectangles, but I'm hoping there's some generic way to do this for arcs.

I also saw this question about rounding only some corners of an arc, but it has no answer (and D3 v4 has come out since it was posted).

like image 309
Randoms Avatar asked Oct 18 '25 21:10

Randoms


1 Answers

Even with the v4 API, still no straight-forward way to do this. Looking at the source code, the cornerRadius becomes a fixed value for the calculation of the whole arc (all 4 corners). Easiest fix is to just append two arcs for every data point with the 2nd arc just filling in the corners.

Example, say we have this nicely rounded arcs:

      var myArcs = [
        [0, 45],
        [180, 300]
      ];

      var vis = d3.select('body')
        .append('svg')
        .attr('width', 400)
        .attr('height', 400);

      var arc = d3.arc()
        .innerRadius(80)
        .outerRadius(150)

      var someArcs = vis.selectAll('path')
        .data(myArcs)
        .enter();

      someArcs
        .append("path")
        .attr("transform", "translate(200,200)")
        .attr("d", function(d) {
          arc.startAngle(d[0] * (Math.PI / 180))
            .endAngle(d[1] * (Math.PI / 180))
            .cornerRadius(20);
          return arc();
        })
        .attr("fill", function(d, i) {
          return d3.schemeCategory10[i];
        });
<script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>

My fix would look like:

var myArcs = [
      [0, 45],
      [180, 300]
    ];

    var vis = d3.select('body')
      .append('svg')
      .attr('width', 400)
      .attr('height', 400);

    var arc = d3.arc()
      .innerRadius(80)
      .outerRadius(150)

    var someArcs = vis.selectAll('path')
      .data(myArcs)
      .enter();

    someArcs
      .append("path")
      .attr("transform", "translate(200,200)")
      .attr("d", function(d) {
        arc.startAngle(d[0] * (Math.PI / 180))
          .endAngle(d[1] * (Math.PI / 180))
          .cornerRadius(20);
        return arc();
      })
      .attr("fill", function(d, i) {
        return d3.schemeCategory10[i];
      });

    someArcs
      .append("path")
      .attr("transform", "translate(200,200)")
      .attr("d", function(d) {
        arc.startAngle(d[0] * (Math.PI / 180))
          .endAngle((d[0] + 10) * (Math.PI / 180))
          .cornerRadius(0);
        return arc();
      })
      .attr("fill", function(d, i) {
        return d3.schemeCategory10[i];
      });
  <script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
like image 61
Mark Avatar answered Oct 20 '25 10:10

Mark