d3 / d3-shape

Graphical primitives for visualization, such as lines and areas.
https://d3js.org/d3-shape
ISC License
2.48k stars 310 forks source link

curveStep.radius? #74

Open mbostock opened 8 years ago

mbostock commented 8 years ago

From d3/d3#2864 by @lgrkvst, it might be nice to support a radius for rounded corners on d3.curveStep and related curves. Example:

image

The radius would probably need to be constant for the whole curve, although it’s interesting to speculate whether it could also be defined per-point.

lgrkvst commented 8 years ago

On a side note, I noticed interpolate("step") does listen to tension, with the effect of steps aligning perfectly half-way between points – no doubt a(n undocumented) feature.

A constant radius would most likely dominate use-cases, however how should the function handle radii that exceed the shortest vector component between two points?

mbostock commented 8 years ago

If the desired radius is too big to fit, reduce it to fit; arcs use a similar strategy with arc.cornerRadius as shown in the arc corners animation.

lgrkvst commented 8 years ago

Wow, mesmerizing example...

On another side note, I'm using arc.cornerRadius in my rest-loaded sunburst menu. d3-sunburst-menu

I've noticed the arc.cornerRadius implementation tends to flicker when used on sunbursts with exactly two partitions. Cosmetics, so I haven't gotten around to exploring the bug further.

mbostock commented 8 years ago

You’re still using D3 3.x; the flickering has been fixed in 4.0.

lgrkvst commented 7 years ago

I've made an attempt at an implementation. My question is whether the parameter really should be .tension. That term is better aligned with the cardinal implementation, however .radius is more straight-forward in describing what to actually pass in the function call (at least in my implementation below). EDIT: realised Mike's six months ahead of me as usual – the tension discussion was closed in June last year.

Thoughts?

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: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
      case 1: this._point = 2; // proceed
      default: {
        if (this._t <= 0) {
          this._context.arcTo(this._x, y, this._x-this._tension, y, this._tension);
          this._context.lineTo(x, y);
        } else {
          var x1 = this._x * (1 - this._t) + x * this._t;
          if (this._y < y-this._tension) {
            this._context.arcTo(x1, this._y, x1, this._y+this._tension, this._tension);
            if (0 < this._t && this._t < 1) this._context.arcTo(x1, y, x1-this._tension, y, this._tension);
            else this._context.lineTo(x, y);
          } else if (this._y > y+this._tension) {
            this._context.arcTo(x1, this._y, x1, this._y-this._tension, this._tension);
            if (0 < this._t && this._t < 1) this._context.arcTo(x1, y, x1-this._tension, y, this._tension);
            else this._context.lineTo(x, y);
          } else {
            this._context.lineTo(x, y);
          }
        }
        break;
      }
    }
    this._x = x, this._y = y;
  }
satyamkuril143 commented 4 years ago

I am using step graph and wants to change corner radius like above design, I applied this above given code in step.js but not getting expected result. So how can I get this can I get this rounded corner radius in my step graph.

lgrkvst commented 4 years ago

The following step function works for d3-shape 1.0.4. It needs some attention to play with the current version (1.3.7).

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

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: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
      case 1: this._point = 2; // proceed
      default: {
        if (this._t <= 0) {
          this._context.arcTo(this._x, y, this._x-this._radius, y, this._radius);
          this._context.lineTo(x, y);
        } else {
          var x1 = this._x * (1 - this._t) + x * this._t;
          if (this._y < y-this._radius) {
            this._context.arcTo(x1, this._y, x1, this._y+this._radius, this._radius);
            if (0 < this._t && this._t < 1) this._context.arcTo(x1, y, x1-this._radius, y, this._radius);
            else this._context.lineTo(x, y);
          } else if (this._y > y+this._radius) {
            this._context.arcTo(x1, this._y, x1, this._y-this._radius, this._radius);
            if (0 < this._t && this._t < 1) this._context.arcTo(x1, y, x1-this._radius, y, this._radius);
            else this._context.lineTo(x, y);
          } else { // ∆y < radius
              this._context.lineTo(x, y);
          }
        }
        break;
      }
    }
    this._x = x, this._y = y;
  }
};

export default (function custom(radius) {

  function step(context) {
    return radius ? new Step(context, 0.5, radius) : new Step(context, 0.5, 0);
  }

  step.radius = function(radius) {
    return custom(+radius);
  };

  return step;
})(0);

This is how I invoke it: d3.curveStepBefore.radius(10)

satyamkuril143 commented 4 years ago

Thank's for help, I applied this code and it is working fine, I need one more help, actually the curve is appearing on right side of step, can we make it to appear both the side's?

brettsprads commented 4 months ago

Thanks for this @lgrkvst

So your example here is really close to what I'm after, but I see that the lines are oriented left of the nodes instead of right. Do you have any ideas of how I can solve this please?

Many thanks!

image
anhtuanlee commented 1 month ago
function Step(context, t, radius) {
    this._context = context;
    this._t = t;
    this._radius = radius;
}

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:
                this._point = 1;
                this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
                break;
            case 1:
                this._point = 2;
            default: {
                var x1, y1;
                if (this._t <= 0) {
                    this._context.arcTo(this._x, y, this._x - this._radius, y, this._radius);
                    this._context.lineTo(x, y);
                } else {
                    // Intermediate inference content between previous point and current point
                    x1 = this._x * (1 - this._t) + x * this._t;
                    y1 = this._y * (1 - this._t) + y * this._t;

                    // check angle
                    if (this._y < y - this._radius) {
                        // (Top to Bottom Arc)
                        this._context.arcTo(this._x, this._y, x, y1, this._radius);
                    } else if (this._y > y + this._radius) {
                        //  (Bottom to Top Arc)
                        this._context.arcTo(this._x, this._y, x, y1, this._radius);
                    } else if (this._x < x - this._radius) {
                        // (Left to Right Arc)
                        this._context.arcTo(this._x, this._y, x1, y, this._radius);
                    } else if (this._x > x + this._radius) {
                        //   (Right to Left Arc)
                        this._context.arcTo(this._x, this._y, x1, y, this._radius);
                    } else {
                        //  spacing lower with radiant draw line
                        this._context.lineTo(x, y);
                    }
                }
                break;
            }
        }
        this._x = x, this._y = y;
    }
};

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

// And here i was used 

 const line = d3.line()
            .x(d => d.x)
            .y(d => d.y)
            .curve(stepRound);

I have same problem, and i was found solution in 2days. It depend of code of @lgrkvst , tks u so much.

Hope will help another go after