2013-09-28

Pi to rad visualization

Intro

Remy Sharp organized a competition to celebrate JS Bin's 5th birthday. In short, there are animated gif images and the people have to write javascript / css to match the gifs. By the time I started trying to figure out the assignments most of the stuff was already solved by these wonderful, incredible and very smart people:

Analysis

An example that looked solvable to me was this one: When I saw it the first time I knew it's going to take a couple of tricks to get the timings right and since I wasn't completely satisfied with the way I solved the number Pi visualization I decided that I had to implement a state machine.

State machine

The usual state machine switches the states cycle by cycle. I modified the concept slightly so that each state checks and maintains the end conditions for the state but the switch happens with a timeout.

In it's end condition every state initiates the transition to the new state. Please not that the state will not be changed immediately and that the code for state change will be called multiple times. In order to avoid quirky behavior I used a simple flag named newState so that the timeout for the new state is actually set only once avoiding multiple timeouts.

An important feature of setTimeout function is that it doesn't maintain the this context so I had to apply the usual that equals this pattern.

 PiRad.prototype.changeState = function (newState, timeout) {
   var that = this;
   (function() {
     if (newState !== this.nextState) {
       setTimeout(function() {that.state = newState;}, timeout);
       this.nextState = newState;
     }
   })();
 };

Sticking the straight red line to the circle

Another challenge I had to face was transforming the straight line into a part of circumference. I gave this a lot of thought and came to a conclusion that this wood look best if I moved the end slowly to the destination on the circle and bind the beginning with a quadratic curve.

 // from stickRedLineToCircle state

 // move the dot to the destination
 this.movingCenter.x -= 2;
 this.movingCenter.y += 0.7;

 // from drawRedDotWithMovingCenterCurved function

 // end coordinates of a top
 var endx = ~~(cwidth / 2 + Math.cos(-1) * this.r);
 var endy = ~~(cheight / 2 + Math.sin(-1) * this.r);

 // calculate the distance 
 var distance = ~~(Math.sqrt(
  Math.pow(endx - this.movingCenter.x, 2) 
  +
  Math.pow(endy - this.movingCenter.y, 2))
 );

 // this factor came out from a lot of try and fail
 var factor = 1.12 + distance / 400;

 // Bézier control point
 var x = ~~(cwidth / 2 + Math.cos(-0.5) * this.r * factor);
 var y = ~~(cheight / 2 + Math.sin(-0.5) * this.r * factor);

 // drawing code
 ctx.lineWidth = 2;
 ctx.beginPath();
 ctx.moveTo(this.movingDot.x, this.movingDot.y);
 ctx.quadraticCurveTo(x, y, this.movingCenter.x, this.movingCenter.y);
 ctx.stroke();

And here's my example on:
JS Bin
Codepen

No comments: