Skip to content

Instantly share code, notes, and snippets.

@KKostya
Last active December 20, 2015 03:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KKostya/6066548 to your computer and use it in GitHub Desktop.
Save KKostya/6066548 to your computer and use it in GitHub Desktop.
More fun with Joukowski
function dist(z0,z,z1)
{
var dx10 = z1.re-z0.re, dy10 = z1.im-z0.im;
var dx0 = z.re-z0.re, dy0 = z.im-z0.im;
return Math.pow(dx10*dy0-dy10*dx0,2)/(dx0*dx0+dy0*dy0);
}
function resampleLoop(f,ps,eps)
{
var ds = ps.map(f);
function refStep(params,data)
{
for(var i = 0; i < params.length; i++)
{
var ni = i+1<params.length?i+1:0;
var p0 = params[i], p1 = params[ni];
var newp = (p0+p1)/2;
if( p1-p0 >= Math.PI || p1-p0 <= -Math.PI ) newp += Math.PI;
var newf = f(newp);
if(dist(data[i],newf,data[ni]) > eps)
{ params.splice(++i,0,newp); data.splice(i,0,newf); }
}
}
refStep(ps,ds);
refStep(ps,ds);
refStep(ps,ds);
refStep(ps,ds);
refStep(ps,ds);
return ds;
}
function resampleLine(f,ps,eps)
{
var ds = ps.map(f);
function refStep(params,data)
{
for(var i = 0; i < params.length-1; i++)
{
var p0 = params[i], p1 = params[i+1];
var newp = (p0+p1)/2;
var newf = f(newp);
if(dist(data[i],newf,data[i+1]) > eps)
{ params.splice(++i,0,newp); data.splice(i,0,newf); }
}
}
refStep(ps,ds);
refStep(ps,ds);
refStep(ps,ds);
refStep(ps,ds);
refStep(ps,ds);
return ds;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg { font: 10px sans-serif; }
.brsh .extent { fill: steelblue; stroke: grey; stroke-width: 0.5px; fill-opacity: .125;}
.brsh .back { fill: none; stroke: black; stroke-width: 1px;}
.line { fill: none; stroke: black; stroke-width: 1px; }
.force .line { fill: none; stroke: red; stroke-width: 1.5px; }
.poten .line { fill: none; stroke: blue; stroke-width: 1.5px; }
.axis { fill: none; stroke: black; }
</style>
<body>
<form>
<label><input type="radio" name="mode" value="rctng"> Rectangular</label>
<label><input type="radio" name="mode" value="crclr" checked> Circular</label>
</form>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="confmap.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);
svg.append("clipPath")
.attr("id","clip")
.append("rect")
.attr("x",0).attr("y",0)
.attr("width",width/2).attr("height",height);
var mapd = svg.append("g").attr("class","mapd").attr("clip-path","url(#clip)");
var brsh = svg.append("g").attr("class","brsh")
.attr("transform","translate("+width/2+")");
var brsR = brsh.append("g").attr("class", "poten");
var brsP = brsh.append("g").attr("class", "force");
var mapR = mapd.append("g").attr("class", "poten");
var mapP = mapd.append("g").attr("class", "force");
var x1 = d3.scale.linear().range([0, width/2]).domain([-5, 5]);
var y1 = d3.scale.linear().range([ height, 0]).domain([-5, 5]);
var x2 = d3.scale.linear().range([0, width/2]).domain([-3, 3]);
var y2 = d3.scale.linear().range([ height, 0]).domain([-3, 3]);
mapd.append("g").attr("class","axis")
.attr("transform","translate(0,"+height/2+")")
.call(d3.svg.axis().scale(x1).orient("bottom"));
mapd.append("g").attr("class","axis")
.attr("transform","translate("+width/4+")")
.call(d3.svg.axis().scale(y1).orient("left"));
brsh.append("g").attr("class","axis")
.attr("transform","translate(0,"+height/2+")")
.call(d3.svg.axis().scale(x2).orient("bottom"));
brsh.append("g").attr("class","axis")
.attr("transform","translate("+width/4+")")
.call(d3.svg.axis().scale(y2).orient("left"));
var line1 = d3.svg.line()
.x(function(d) { return x1(d.re); })
.y(function(d) { return y1(d.im); });
var line2 = d3.svg.line()
.x(function(d) { return x2(d.re); })
.y(function(d) { return y2(d.im); });
var brush = d3.svg.brush().x(x2).y(y2).on("brush", plotC);
brsh.append("rect").attr("class","back")
.attr("width",width/2)
.attr("height",height);
brsh.call(brush);
function plines(sel,data, f)
{
var paths = sel.selectAll('.line').data(data);
paths.enter().append('path').attr('class','line');
paths.attr('d',f);
paths.exit().remove();
}
d3.selectAll("input").on("change", function(){
if(this.value==="crclr") brush.on("brush",plotC);
else brush.on("brush",plotR);
brush.on("brush")(); });
function f(z) { return z.add((new Complex(1,0)).div(z)) }
function plotC()
{
if(brush.empty()) return;
ext = brush.extent();
var rads = d3.range(0,1,1/10);
var phis = d3.range(-Math.PI,Math.PI,Math.PI/10);
var x0 = (ext[1][0] + ext[0][0])/2;
var y0 = (ext[1][1] + ext[0][1])/2;
var dx = (ext[1][0] - ext[0][0])/2;
var dy = (ext[1][1] - ext[0][1])/2;
var Ppars = rads.map(function(r){return function(t){return new Complex(x0+r*dx*Math.cos(t),y0+r*dy*Math.sin(t));};});
var Rpars = phis.map(function(p){return function(t){return new Complex(x0+t*dx*Math.cos(p),y0+t*dy*Math.sin(p));};});
plines(brsR, Ppars.map(function(par){return phis.map(par);}), function(d){return line2(d)+'Z';});
plines(brsP, Rpars.map(function(par){return rads.map(par);}), function(d){return line2(d);});
var PparsF = Ppars.map(function(par){return function(t){return f(par(t)); };});
var RparsF = Rpars.map(function(par){return function(t){return f(par(t)); };});
plines(mapR,PparsF.map(function(par){return resampleLoop(par,phis,0.0001);}),function(d){return line1(d)+'Z';});
plines(mapP,RparsF.map(function(par){return resampleLine(par,rads,0.0001);}),function(d){return line1(d);});
}
function plotR()
{
if(brush.empty()) return;
ext = brush.extent();
var x1 = ext[1][0], x0 = ext[0][0]
var y1 = ext[1][1], y0 = ext[0][1]
var dx = ext[1][0] - ext[0][0];
var dy = ext[1][1] - ext[0][1];
var xs = d3.range(0,1.01,1/10);
var ys = d3.range(0,1.01,1/10);
var Xpars = xs.map(function(x){return function(t){return new Complex(x0+x*dx,y0+t*dy);};});
var Ypars = ys.map(function(y){return function(t){return new Complex(x0+t*dx,y0+y*dy);};});
plines(brsR, Xpars.map(function(par){return ys.map(par);}), function(d){return line2(d);});
plines(brsP, Ypars.map(function(par){return xs.map(par);}), function(d){return line2(d);});
var XparsF = Xpars.map(function(par){return function(t){return f(par(t)); };});
var YparsF = Ypars.map(function(par){return function(t){return f(par(t)); };});
plines(mapR,XparsF.map(function(par){return resampleLine(par,ys,0.0001);}),function(d){return line1(d);});
plines(mapP,YparsF.map(function(par){return resampleLine(par,xs,0.0001);}),function(d){return line1(d);});
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment