bhanu146
bhanu146

Reputation: 45

Fractal Spirographs: possible rounding errors in javascript

I have been trying to replicate the example present in the link. So I have started out with two circles, one fixed and one rotating. But the rotating circle is drifting away from the stationary circle and some times it is falling into the stationary circle. Can someone please help me fix this. I have created a live demo of code at the following link.Below shared is the complete code.

// takes two points x1,y1,x2,y2 and return r and Θ 
function coordinateToPolar(x1,y1,x2,y2){
polar={}

polar.r= Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2))

polar.theta=Math.atan2(y2-y1,x2-x1)

return polar;
}

function degreeToRadians(degree){
return (degree/180)*Math.PI;
}

function radiansToDegrees(rad){
return (rad/Math.PI)*180
}

// takes one point h,k the center of circle and r and Θ
function polarToCoordinate(r,theta,h,k){
 point={}
 point.x=h+r*Math.cos(theta);
 point.y=k+r*Math.sin(theta);
 return point;
}



 //circle ds
 function Circle(r,h,k) {
            this.r=r;
            this.h=h;
            this.k=k;
        }



 /// Adjusting the geometric center
 bufferX=250;
 bufferY=250;


 svg=d3.select("body").append("svg");


            startX=200;
            startY=200;
            startR=150;
            dsCircles=[];

            // intiating prevR and lastR to 0 
            prevR=0;
            lastR=0;
            for(i=0;i<2;i++){
                currR=startR>>(i*1);
                dsCircles[i]=new Circle(currR, i==0?bufferX+prevR+currR: bufferX+ prevR+lastR+currR,bufferY+startY)
                lastR=currR;
                prevR +=currR;
            }

            svg.selectAll("circles").data(dsCircles).enter().append("circle").attr("id",function(d,i){return "circle"+(i+1)}).attr("r",function(d){ return d.r}).attr("cy",function(d){return d.k}).attr("cx",function(d){ return d.h});

            window.setInterval(function interval(){
                // static variable initiated only once
                if(interval["itr"]==undefined){
                    // initializing iteration counter to 0
                    interval.itr=0;
                    // getting the polar coordinates of the circles with respect to geometric center
                    interval.theta=coordinateToPolar(dsCircles[0].h,dsCircles[0].k,dsCircles[1].h,dsCircles[1].k).theta;
                    interval.r=coordinateToPolar(dsCircles[0].h,dsCircles[0].k,dsCircles[1].h,dsCircles[1].k).r;
                }




                d3.select("#circle2").attr("cx",function(d){                         
                    // assigning new center x-coordinate 
                    return polarToCoordinate(interval.r,interval.theta,dsCircles[0].h,dsCircles[0].k).x;

                }).attr("cy",function(d){ 
                    // assigning new center y-coordinate  
                    return polarToCoordinate(interval.r,interval.theta +.03,dsCircles[0].h,dsCircles[0].k).y;

                });

                d3.select("#circle2").attr("style",function(d){ return interval.itr%2==0?"stroke:red":"stroke:blue"; })
                interval.itr++;
                interval.theta +=.003

            },10)

Upvotes: 2

Views: 149

Answers (2)

Mark
Mark

Reputation: 108537

Interesting visualization.

This is not a direct answer to you question because I find your code overly confusing and troubling to debug. You seem to be doing way more math then you need; converting back from forth to different coordinate systems. Also, animating in a setTimeout is not a good practice.

Here's a quick refactor that takes advantage of d3.transition and simplifies the calculations. It also drives the addition of new circles through data.

<!DOCTYPE html>
<html>

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

  <body>
    <script>
    
      var width = 500,
          height = 500;
    
      var svg = d3.select('body')
        .append('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g')
        .attr('transform','translate(' + width/2 + ',' + height/2 + ')');
        
      var data = [
        {
          r: 75,
          x: 0,
          y: 0,
          c: 'black',
          d: 0
        }, {
          r: 50,
          x: 0,
          y: 0,
          c: 'steelblue',
          d: 7000
        },{
          r: 30,
          x: 0,
          y: 0,
          c: 'orange',
          d: 5000
        },{
          r: 20,
          x: 0,
          y: 0,
          c: 'red',
          d: 2000
        },{
          r: 10,
          x: 0,
          y: 0,
          c: 'green',
          d: 500
        }
      ];
      
      data.forEach(function(d,i){
        if (i === 0) d.pD = null;
        else d.pD = data[i-1];
      });
      
      svg.selectAll('circle')
        .data(data)
        .enter()
        .append('circle')
        .attr('r', function(d){
          return d.r;
        })
        .style('fill', 'none')
        .style('stroke', function(d){
          return d.c
        })
        .each(goRound);
        
      function goRound(d,i){
        
        if (!d.pD) return function(t) { }
        
        var self = d3.select(this);
        
        self.transition()
          .ease(d3.easeLinear)
          .duration(function(d){
            return d.d;
          })
          .tween("go.round", function(){

            var inter = d3.interpolate(0, Math.PI * 2);
            
            return function(t) {
              d.x = Math.cos(inter(t)) * (d.pD.r + d.r) + d.pD.x;
              d.y = Math.sin(inter(t)) * (d.pD.r + d.r) + d.pD.y;
              self.attr('cx', d.x);
              self.attr('cy', d.y);
              
            };
            
          })
          .on('end', goRound);
      }

    </script>
  </body>

</html>

Upvotes: 1

MBo
MBo

Reputation: 80197

Just remove angle shift for cy +.03 here:

attr("cy",function(d){ 
                // assigning new center y-coordinate  
                return polarToCoordinate(interval.r,interval.theta +.03,

Upvotes: 3

Related Questions