djstojadin djstojadin - 8 days ago 5
Javascript Question

d3.js custom curve step round

I'm working on d3.js .v4 graph with x and y axis and I need your help.

Description:

xAxis is linear scaled with path on it like this image. round corner on d3.curveStep

I'm stuck here and can't find solution to make path like on this image path with rounded corner

This is my code for line function



// the path generator for the line chart
line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d, i) {
return yScale(d);
})
.curve(d3.curveStep);





I tried with cardinal, monotone and catmull buth can't archive desired path.

Is it possible to round corners on d3.curveStep?

Answer

I finally found sometime to comeback to this question. This is a great opportunity to implement a custom curve. I essentially stole the source code to d3.curveStepBefore and modified to fit your requirements.

function Step(context, t) {
  this._context = context;
  this._t = t;
}

Step.prototype = {
  areaStart: function() {
    this._line = 0;
  },
  areaEnd: function() {
    this._line = NaN;
  },
  lineStart: function() {
    this._x = this._y = NaN;
    this._point = 0;
  },
  lineEnd: function() {
    if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
    if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
  },
  point: function(x, y) {
    x = +x, y = +y;
    switch (this._point) {
      case 0:
      case 0:
        this._point = 1;
        this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
        break;
      case 1:
        this._point = 2; // proceed
      default:
        {
          var xN, yN, mYb, mYa;
          if (this._t <= 0) {
            xN = Math.abs(x - this._x) * 0.25;
            yN = Math.abs(y - this._y) * 0.25;
            mYb = (this._y < y) ? this._y + yN : this._y - yN;
            mYa = (this._y > y) ? y + yN : y - yN;

            this._context.quadraticCurveTo(this._x, this._y, this._x, mYb);
            this._context.lineTo(this._x, mYa);
            this._context.quadraticCurveTo(this._x, y, this._x + xN, y);
            this._context.lineTo(x - xN, y);

          } else {
            var x1 = this._x * (1 - this._t) + x * this._t;

            xN = Math.abs(x - x1) * 0.25;
            yN = Math.abs(y - this._y) * 0.25;
            mYb = (this._y < y) ? this._y + yN : this._y - yN;
            mYa = (this._y > y) ? y + yN : y - yN;

            this._context.quadraticCurveTo(x1, this._y, x1, mYb);
            this._context.lineTo(x1, mYa);
            this._context.quadraticCurveTo(x1, y, x1 + xN, y);
            this._context.lineTo(x - xN, y);
          }
          break;
        }
    }
    this._x = x, this._y = y;
  }
};

stepRound = function(context) {
  return new Step(context, 0.5);
};

stepRoundBefore = function(context) {
  return new Step(context, 0);
};

stepRoundAfter = function(context) {
  return new Step(context, 1);
};
<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <script>
    document.addEventListener("DOMContentLoaded", function(event) {

      var width = 500,
        height = 500,
        N = 10;

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

      var points = [];
      for (var i = 0; i < N; i++) {
        points.push({
          x: (width / N) * i + (width / N / 2),
          y: Math.random() * height
        });
      }

      var line1 = d3.line()
        .x(function(d) {
          return d.x;
        })
        .y(function(d) {
          return d.y;
        })
        .curve(stepRound);

      var line2 = d3.line()
        .x(function(d) {
          return d.x;
        })
        .y(function(d) {
          return d.y;
        })
        .curve(d3.curveStep);

      svg.append('path')
        .datum(points)
        .attr('d', line1)
        .attr('fill', 'none')
        .attr('stroke', 'orange')
        .attr('stroke-width', '3px');

      svg.append('path')
        .datum(points)
        .attr('d', line2)
        .attr('fill', 'none')
        .attr('stroke', 'steelblue')
        .attr('stroke-width', '1px');

    });
  </script>
</body>

</html>

Comments