D3: Circle Points don't update when changing slider

admin

Administrator
Staff member
I've started trying to learn D3 as merely a hobby and I've wanted to create a type of animation that displays the '<a href="https://theturnofgears.files.wordpress.com/2014/11/method-of-exhaustion.png" rel="nofollow noreferrer">method of exhaustion</a>' to approximate the area of a circle. The behavior I wish to obtain is shown in this <a href="https://lh3.googleusercontent.com/-...C/w506-h750/0_ExhaustionMethod_Archimedes.gif" rel="nofollow noreferrer">animation</a>.

I have been able to draw the circle, the points around the circle as well as the triangles. Now I am working on a function called 'update' which upon listening to a slider event on the HTML dynamically changes the value for 'n' and use that to recalculate the coordinates and display them on screen.

I have managed to be able to change the fill of the circles but aside from that the update function doesn't update the coordinates of the circles as well as add new circles. I would really appreciate if someone could help me get this working as well as provide some insight as to why my approach is failing.

<div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override">
Code:
var w = 500;
var h = 500;
var n = 17;
var r = h / 2 - 20;
var coords = [];
//var id = 0;

function points() {
  coords = []; //Clear Coords Array

  for (var i = 0; i &lt; n; i++) {
    var p_i = {};
    p_i.x = w / 2 + r * math.cos((2 * math.pi / n) * i);
    p_i.y = h / 2 - r * math.sin((2 * math.pi / n) * i);
    p_i.id = i;
    coords.push(p_i);
  }

  //id++;
};

points(); //Generate Points

var svg = d3.select('body') //SVG Canvas
  .append('svg')
  .attr('width', w)
  .attr('height', h);

var circle = svg.append('circle') //Draw Big Circle
  .attr('cx', w / 2)
  .attr('cy', h / 2)
  .attr('r', r)
  .attr('fill', 'teal')
  .attr('stroke', 'black')
  .attr('stroke-width', w / 100);

var center = svg.append('circle') //Construct Center
  .attr('cx', w / 2)
  .attr('cy', h / 2)
  .attr('r', r / 50)
  .attr('fill', 'red')
  .attr('stroke', 'red')
  .attr('stroke-width', w / 100);

var approx_pts = svg.selectAll('circle_points') //Construct Approx Points
  .data(coords, function(d) {
    return d.id; //Sets Identifier to uniquely identify data. 
  })
  .enter()
  .append('circle')
  .attr('cx', function(d) {
    return d.x;
  })
  .attr('cy', function(d) {
    return d.y;
  })
  .attr('r', w / 100)
  .attr('fill', 'red');

var approx_tri = svg.selectAll('polygon') //Draw Triangles
  .data(coords, function(d) {
    return d.id;
  })
  .enter()
  .append('polygon')
  .attr('points', function(d, i) {
    if (i != n - 1) {
      return w / 2 + ',' + h / 2 + ' ' + d.x + ',' + d.y + ' ' + coords[i + 1].x + ',' + coords[i + 1].y;
    } else {
      return w / 2 + ',' + h / 2 + ' ' + d.x + ',' + d.y + ' ' + coords[0].x + ',' + coords[0].y;
    }
  })
  .attr('fill', 'Transparent')
  .attr('stroke', 'orange')
  .attr('stroke-width', w / 250);

d3.select('#slider') //Listen to Slider Event Change
  .on('input', function() {
    n = +this.value;
    update(n);
  });

function update() {
  console.log('Hey man!');
  points(); // Re-generate points
  console.log('coords[1].x = ' + coords[1].x);
  console.log('coords[1].y = ' + coords[1].y);

  //Make Selection
  approx_pts
    .selectAll('circle')
    .data(coords, function(d) {
      return d.id;
    });

  approx_pts
    .attr('fill', 'blue')
    .attr('r', r / 25);

  approx_pts
    .enter()
    .append('circle')
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    })
    .attr('r', r / 50)
    .attr('fill', 'green');
};
<pre class="snippet-code-html lang-html prettyprint-override">
Code:
&lt;script src="https://d3js.org/d3.v4.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.16.2/math.min.js"&gt;&lt;/script&gt;
&lt;input id="slider" type="range" min="3" max="100" step="1" value="3"&gt;
</div>
</div>