9

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?

stojkosd
  • 228
  • 2
  • 7

1 Answers1

14

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>
Mark
  • 106,305
  • 20
  • 172
  • 230
  • Thank you Mark for your time! This is what I need. Now I need to figure out how to edit this code for curveStep. Current curveStepBefore (rounded) push my line path to be aligned with background dasshed grid but path need to be between that grid. – stojkosd Nov 23 '16 at 23:03
  • @djstojadin, see updated answer. I implemented `curveStep` and `curveStepAfter`. – Mark Nov 24 '16 at 14:03
  • Outstanding! Good on you. – Gerardo Furtado Nov 24 '16 at 14:07
  • @GerardoFurtado, thanks. It's hard to keep up with you on the `d3.js` tag :) – Mark Nov 24 '16 at 14:08