KKostya
7/17/2013 - 2:18 PM

Animated Joukowski map

Animated Joukowski map

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.ani .line { fill: none; stroke: steelblue; stroke-width: 1.5px; }
.grd .line { fill: none; stroke: black;     stroke-width: 0.5px; }
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
    function Complex(re, im) { this.re  =   re; this.im  =   im; };
    Complex.prototype.clone = function(){ return new Complex(this.re,this.im); };
    Complex.prototype.add = function(z) { this.re += z.re; this.im += z.im; return this; };
    Complex.prototype.sub = function(z) { this.re -= z.re; this.im -= z.im; return this; };
    Complex.prototype.mul = function(z) 
    { 
        var tmp = this.re;
        this.re = this.re*z.re - this.im*z.im; 
        this.im =     tmp*z.im + this.im*z.re; 
        return this;
    };
                                                     
    Complex.prototype.div = function(z) 
    { 
        var tmp =  this.re, 
              n =     z.re*z.re +    z.im*z.im;
        this.re = (this.re*z.re + this.im*z.im)/n; 
        this.im = (   -tmp*z.im + this.im*z.re)/n; 
        return this;
    };
                                                     
 
 
    var width = 960, height = 500;
 
    var svg = d3.select("body")
                .append("svg")
                .attr("width",   width)
                .attr("height", height);

    var grd = svg.append("g").attr("class","grd");
    var ani = svg.append("g").attr("class","ani");
 
    var x = d3.scale.linear()
                    .range( [ 0, width])
                    .domain([    -5, 5]);
 
    var y = d3.scale.linear()
                    .range( [height, 0])
                    .domain([    -5, 5]);
 
    var line = d3.svg.line()
                     .x(function(d) { return x(d.re); })
                     .y(function(d) { return y(d.im); });


    function ProduceData(roff,poff)
    {
        var data = d3.range(0.87+roff,roff,-0.02).map(function(r){
                   return d3.range(-Math.PI/4+poff,7*Math.PI/4,Math.PI/50).map(function(p){
                          return new Complex(r*Math.cos(p)-0.2,r*Math.sin(p)+0.2);})})
        data.forEach(function(d){d.forEach(function(z) {z.add((new Complex(1,0)).div(z));})});
        return data;
    }
 
    var data = ProduceData(0,0.01); 
    var save = data[0];
    data = d3.transpose(data);
    save.push(save[0]);
    data.unshift(save);
    
    var paths = grd.selectAll('path').data(data);
    paths.enter().append('path').attr('class','line');
    paths.attr('d',line)
    
    function plot(offset)
    { 
        var data = ProduceData(offset,0); 
        var paths = ani.selectAll('path').data(data);
        paths.enter().append('path').attr('class','line');
        paths.attr('d',function(d) { return line(d) + 'Z'; })
             .attr('stroke-opacity', function(d,i){ if(i<20) return i/20; else return 1.; });
        paths.exit().remove();
    }
    var off = 0;
    d3.timer(function() { plot(off); off -= 0.001; if(off<0) off=0.019;});
        
 
</script>
</body>