score:1

Instead of using width, you should use getTotalLength() for the size of the line:

pathLength = this.getTotalLength();

Here is the demo with that change only:

function curve(val) {
  var w = d3.select("#new_row_1").attr("width");

  var
    numLines = 1,
    lineSpacing = 18,
    parabDepth = -30;

  var row_spacing = 18;

  if (val == 0) {
    parabDepth = -18;
  } else if (val == 1) {
    parabDepth = 0;
  } else if (val == 15) {
    parabDepth = 150;
    row_spacing = 20;
  } else {
    parabDepth = val * 10;
  }

  width = w;

  var curveData = [];

  curveData.push([0, 0]);
  curveData.push([width / 3 * 1, lineSpacing + parabDepth]);
  curveData.push([width / 3 * 2, lineSpacing + parabDepth]);
  curveData.push([width / 3 * 3 - 8, - 8]);

  var line = d3.line()
    .x(function(d) {
      return d[0];
    })
    .y(function(d) {
      return d[1];
    })
    .curve(d3.curveCardinal);

  var svg = d3.select("#new_row_1").attr("height", (numLines * lineSpacing) + lineSpacing + parabDepth + row_spacing).attr("width", width);

  var g = svg.selectAll(".line")
    .data(d3.range(numLines))
    .enter()
    .append("g")
    .attr("class", "line")
    .attr("transform", function(d) {
      return "translate(7," + (d * lineSpacing + 7) + ")";
    });

  var path = g.append("path")
    .attr("d", line(curveData))
    .style("fill", "none")
    .style("stroke", "pink")
    .style("stroke-width", "4")
    .each(function() {
      var g = d3.select(this.parentNode),
        self = d3.select(this),
        pathLength = this.getTotalLength();

      g.selectAll("circle")
        .data(d3.range(1, pathLength, row_spacing))
        .enter()
        .append("circle")
        .attr("transform", (d, i) => {
          var p = this.getPointAtLength(d);
          return "translate(" + p.x + "," + p.y + ")";
        })
        .attr("r", 7)
        .style("fill", "darkslategray")
        .attr("stroke", "black")
        .attr("stroke-width", "1");
    });
}

setTimeout(function() {
  curve(5)
}, 500)
<script src="https://d3js.org/d3.v4.min.js"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<svg width="252" height="18" id="new_row_1" class="new_row" style="top: 150px; left: 302px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);"><g><circle cx="8" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="3" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">1</text></g><g><circle cx="26" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="21" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">2</text></g><g><circle cx="44" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="39" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">3</text></g><g><circle cx="62" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="57" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">4</text></g><g><circle cx="80" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="75" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">5</text></g><g><circle cx="98" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="93" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">6</text></g><g><circle cx="116" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="111" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">7</text></g><g><circle cx="134" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="129" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">8</text></g><g><circle cx="152" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="147" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">9</text></g><g><circle cx="170" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="165" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">10</text></g><g><circle cx="188" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="183" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">11</text></g><g><circle cx="206" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="201" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">12</text></g><g><circle cx="224" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="219" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">13</text></g><g><circle cx="242" cy="8" r="7" style="fill: white; stroke: black; stroke-width: 1;"></circle><text dx="237" dy="11" class="label" style="font-size: 10px; font-weight: 500; display: none;">14</text><g class="label" style="display: none;"><rect x="126" y="1" width="14" height="14" style="fill: black;"></rect><text dx="130" dy="11" style="font-size: 9px; font-weight: 400; fill: white;">A</text></g></g></svg>

Alternatively, if you want to use width, you shouldn't use getPointAtLength(), as LeBeau explains in his answer, because in Euclidean geometry a straight line is shorter than any other geometric shape going from point A to point B.

Besides that, you have two problems: 1. there are a lot of magic numbers everywhere, and 2. you could design a better math to get the arc. I believe you should get rid of this code and write an entirely new, different approach.

score:2

You are using getPointAtLength() to find the X,Y coordinates of a point at a distance along the curve. Obvoiously that's wrong. You want to find the Y coord for a given X.

There is no built-in SVG function for that. You are going to have to calculate that yourself. For that you would need the polynomial form of the bezier equation, which is not trivial.

Since you are only making a parabola, you would be better to avoid using bezier curves at all, and just calculate your curve using the normal simple quadratic equation (ax^2 + bx + c).

Aside: if you do decide to stick with bezier curves, then you should really use a quadratic bezier, instead of a cbic one. Quadratic beziers have the property that they always form a segmnt of a parabola.


Related Query

More Query from same tag