Apollonian circle packing: Difference between revisions

From JSXGraph Wiki
No edit summary
 
(20 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<jsxgraph width="600" height="600" box="box">
<jsxgraph width="600" height="600" box="box">
var brd = JXG.JSXGraph.initBoard('box', {originX: 300, originY: 300, grid:false, unitX: 150, unitY: 150});
var brd = JXG.JSXGraph.initBoard('box', {boundingbox: [-2, 2, 2, -2]});
HSV2RGB = function(H,S,V) {
    var R,G,B, f,i,hTemp, p,q,t;
    if (S==0) {
        if (isNaN(H)) {
            R = V;
            G = V;
            B = V;
        } else {
            return '#000000';
        }
    } else {
        if (H>=360) {
            hTemp = 0.0;
        } else {
            hTemp = H;
        }
        hTemp = hTemp / 60;    // h is now IN [0,6)
        i = Math.floor(hTemp);        // largest integer <= h
        f = hTemp - i;                  // fractional part of h
        p = V * (1.0 - S);
        q = V * (1.0 - (S * f));
        t = V * (1.0 - (S * (1.0 - f)));
        switch (i) {
            case 0: R = V; G = t;  B = p; break;
            case 1: R = q; G = V;  B = p; break;
            case 2: R = p; G = V;  B = t; break;
            case 3: R = p; G = q;  B = V; break;
            case 4: R = t; G = p;  B = V; break;
            case 5: R = V; G = p;  B = q; break;
        }
    }
    R = Math.round(R*255).toString(16); R = (R.length==0)?'00':((R.length==1)?'0'+R:R);
    G = Math.round(G*255).toString(16); G = (G.length==0)?'00':((G.length==1)?'0'+G:G);
    B = Math.round(B*255).toString(16); B = (B.length==0)?'00':((B.length==1)?'0'+B:B);
    return '#'+R+G+B;
}
solveQ2 = function(x1,x2,x3,off) {
solveQ2 = function(x1,x2,x3,off) {
     var a, b, c, d;
     var a, b, c, d;
Line 47: Line 11:
}   
}   
      
      
a = brd.createElement('line',[[0,0],[2,0]],{straightFirst:false,straightLast:false,visible:false});
a = brd.create('segment',[[0,0],[2,0]],{visible:false});
p0 = brd.createElement('point',[0,0],{name:'',visible:false});
p1 = brd.create('glider',[1.3,0,a],{name:'Drag me'});
p1 = brd.createElement('glider',[1.3,0,a],{name:'Drag me'});
 
b0 = -0.5;
b0 = -0.5;
r1 = 2-p1.X();
b1 = 1.0/r1;
r2 = (2.0-r1);
b2 = 1.0/r2;


p2 = brd.createElement('point',[function(){return p1.X()-2;},0],{name:'',visible:false});
c0 = brd.create('circle',[[0,0],Math.abs(1.0/b0)],{strokeWidth:1});
c1 = brd.create('circle',[p1,function(){return 2-p1.X();}],{strokeWidth:1});
c2 = brd.create('circle',[[function(){return p1.X()-2;},0],function(){return p1.X();}],{strokeWidth:1});


c0 = brd.createElement('circle',[p0,Math.abs(1.0/b0)],{strokeWidth:1});
c1 = brd.createElement('circle',[p1,function(){return 2-p1.X();}],{strokeWidth:1});
c2 = brd.createElement('circle',[p2,function(){return p1.X();}],{strokeWidth:1});
c0.curvature = function(){ return b0;}; // constant
c0.curvature = function(){ return b0;}; // constant
c1.curvature = function(){ return 1/(2-p1.X());};
c1.curvature = function(){ return 1/(2-p1.X());};
Line 101: Line 58:
}
}


p3 = brd.createElement('point',[thirdCircleX,thirdCircleY],{name:'',visible:false});
c3 = brd.create('circle',[[thirdCircleX,thirdCircleY],thirdCircleRadius],{strokeWidth:1});
c3 = brd.createElement('circle',[p3,thirdCircleRadius],{strokeWidth:1});
c3.curvature = function(){ return 1.0/this.radius;};
c3.curvature = function(){ return 1.0/this.radius;};


otherCirc = function(circs,level) {
otherCirc = function(circs,level) {
     var p,c, fx,fy,fr;
     var c, fx,fy,fr;
     if (level<=0) return;
     if (level<=0) return;
     fx = function() {
     fx = function() {
Line 141: Line 97:
         }
         }
         b[4] = 2*(b[0]+b[1]+b[2])-b[3];
         b[4] = 2*(b[0]+b[1]+b[2])-b[3];
         return 1/b[4];
         if (isNaN(b[4])) {
            return 1000.0;
        } else {
            return 1/b[4];
        }
     }
     }
     p = brd.createElement('point',[fx,fy],{name:'',visible:false});
     c = brd.create('circle',[[fx,fy],fr],{strokeWidth:1,name:'',
    c = brd.createElement('circle',[p,fr],{strokeWidth:1,
                 fillColor:JXG.hsv2rgb(50*level,0.8,0.8),highlightFillColor:JXG.hsv2rgb(50*level,0.5,0.8),fillOpacity:0.5,highlightFillOpacity:0.5});
                 fillColor:HSV2RGB(360/(level),0.8,0.8),highlightFillColor:HSV2RGB(360/(level),0.8,0.6),fillOpacity:0.5});
     c.curvature = function(){ return 1/this.radius;};
     c.curvature = function(){ return 1/this.radius;};


Line 167: Line 126:
===The underlying JavaScript code===
===The underlying JavaScript code===
<source lang="javascript">
<source lang="javascript">
var brd = JXG.JSXGraph.initBoard('box', {originX: 300, originY: 300, grid:true, unitX: 100, unitY: 100});
var brd = JXG.JSXGraph.initBoard('box', {boundingbox: [-2, 2, 2, -2]});
solveQ2 = function(x1,x2,x3,off) {
solveQ2 = function(x1,x2,x3,off) {
     var a, b, c, d;
     var a, b, c, d;
Line 177: Line 136:
     return [(-b+Math.sqrt(d))/(2.0*a),(-b-Math.sqrt(d))/(2.0*a)];
     return [(-b+Math.sqrt(d))/(2.0*a),(-b-Math.sqrt(d))/(2.0*a)];
}   
}   
   
a = brd.create('segment',[[0,0],[2,0]],{visible:false});
p1 = brd.create('glider',[1.3,0,a],{name:'Drag me'});
b0 = -0.5;


// The outer circle   
c0 = brd.create('circle',[[0,0],Math.abs(1.0/b0)],{strokeWidth:1});
p0 = brd.createElement('point',[0,0],{name:'',visible:false});
c1 = brd.create('circle',[p1,function(){return 2-p1.X();}],{strokeWidth:1});
b0 = -0.5;
 
c0 = brd.createElement('circle',[p0,Math.abs(1.0/b0)],{strokeWidth:1});
c2 = brd.create('circle',[[function(){return p1.X()-2;},0],function(){return p1.X();}],{strokeWidth:1});
c0.curvature = function(){ return b0;}; // constant
c0.curvature = function(){ return b0;}; // constant
// The left initial inner circle   
a = brd.createElement('line',[[0,0],[2,0]],{straightFirst:false,straightLast:false,visible:false});
p1 = brd.createElement('glider',[1.3,0,a],{name:'Drag me'});
c1 = brd.createElement('circle',[p1,function(){return 2-p1.X();}],{strokeWidth:1});
c1.curvature = function(){ return 1/(2-p1.X());};
c1.curvature = function(){ return 1/(2-p1.X());};
// The right initial inner circle   
p2 = brd.createElement('point',[function(){return p1.X()-2;},0],{name:'',visible:false});
c2 = brd.createElement('circle',[p2,function(){return p1.X();}],{strokeWidth:1});
c2.curvature = function(){ return 1/(p1.X());};
c2.curvature = function(){ return 1/(p1.X());};


// Now, we construct the fourth initial circle
thirdCircleX = function() {
thirdCircleX = function() {
     var b0,b1,b2,x0,x1,x2, b3,bx3;
     var b0,b1,b2,x0,x1,x2, b3,bx3;
Line 231: Line 184:
}
}


p3 = brd.createElement('point',[thirdCircleX,thirdCircleY],{name:'',visible:false});
c3 = brd.create('circle',[[thirdCircleX,thirdCircleY],thirdCircleRadius],{strokeWidth:1});
c3 = brd.createElement('circle',[p3,thirdCircleRadius],{strokeWidth:1});
c3.curvature = function(){ return 1.0/this.radius;};
c3.curvature = function(){ return 1.0/this.radius;};


// Recursion
// In an Apollonian configuration replace the fourth circle by its
// second possible variant.
otherCirc = function(circs,level) {
otherCirc = function(circs,level) {
     var p,c, fx,fy,fr;
     var c, fx,fy,fr;
     if (level<=0) return;
     if (level<=0) return;
     fx = function() {
     fx = function() {
Line 275: Line 223:
         }
         }
         b[4] = 2*(b[0]+b[1]+b[2])-b[3];
         b[4] = 2*(b[0]+b[1]+b[2])-b[3];
         return 1/b[4];
         if (isNaN(b[4])) {
            return 1000.0;
        } else {
            return 1/b[4];
        }
     }
     }
     p = brd.createElement('point',[fx,fy],{name:'',visible:false});
     c = brd.create('circle',[[fx,fy],fr],{strokeWidth:1,name:'',
    c = brd.createElement('circle',[p,fr],{strokeWidth:1,fillColor:HSV2RGB(360/(level),0.8,0.8),fillOpacity:0.5});
                fillColor:JXG.hsv2rgb(50*level,0.8,0.8),highlightFillColor:JXG.hsv2rgb(50*level,0.5,0.8),fillOpacity:0.5,highlightFillOpacity:0.5});
     c.curvature = function(){ return 1/this.radius;};
     c.curvature = function(){ return 1/this.radius;};
      
 
     // Recursion
     otherCirc([circs[0],circs[1],c,circs[2]],level-1);
     otherCirc([circs[0],circs[1],c,circs[2]],level-1);
     otherCirc([circs[0],circs[2],c,circs[1]],level-1);
     otherCirc([circs[0],circs[2],c,circs[1]],level-1);
Line 300: Line 253:
* [http://arxiv.org/abs/math.MG/0101066 Jeffrey C. Lagarias, Colin L. Mallows, Allan R. Wilks: Beyond the Descartes circle theorem]
* [http://arxiv.org/abs/math.MG/0101066 Jeffrey C. Lagarias, Colin L. Mallows, Allan R. Wilks: Beyond the Descartes circle theorem]
* [http://en.wikipedia.org/wiki/Apollonian_gasket http://en.wikipedia.org/wiki/Apollonian_gasket]
* [http://en.wikipedia.org/wiki/Apollonian_gasket http://en.wikipedia.org/wiki/Apollonian_gasket]
* [http://mathworld.wolfram.com/SoddyCircles.html Weisstein, Eric W. "Soddy Circles." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/SoddyCircles.html]
* [http://mathworld.wolfram.com/ApollonianGasket.html Weisstein, Eric W. "Apollonian Gasket." From MathWorld--A Wolfram Web Resource]
* [http://mathworld.wolfram.com/SoddyCircles.html Weisstein, Eric W. "Soddy Circles." From MathWorld--A Wolfram Web Resource]




[[Category:Examples]]
[[Category:Examples]]
[[Category:Geometry]]
[[Category:Geometry]]

Latest revision as of 09:34, 7 June 2011

The underlying JavaScript code

var brd = JXG.JSXGraph.initBoard('box', {boundingbox: [-2, 2, 2, -2]});
solveQ2 = function(x1,x2,x3,off) {
    var a, b, c, d;
    a = 0.5;
    b = -(x1+x2+x3);
    c = x1*x1+x2*x2+x3*x3-0.5*(x1+x2+x3)*(x1+x2+x3)-off;
    d = b*b-4*a*c;
    if (Math.abs(d)<0.00000001) d = 0.0;
    return [(-b+Math.sqrt(d))/(2.0*a),(-b-Math.sqrt(d))/(2.0*a)];
}   
    
a = brd.create('segment',[[0,0],[2,0]],{visible:false});
p1 = brd.create('glider',[1.3,0,a],{name:'Drag me'});
b0 = -0.5;

c0 = brd.create('circle',[[0,0],Math.abs(1.0/b0)],{strokeWidth:1});
c1 = brd.create('circle',[p1,function(){return 2-p1.X();}],{strokeWidth:1});

c2 = brd.create('circle',[[function(){return p1.X()-2;},0],function(){return p1.X();}],{strokeWidth:1});
c0.curvature = function(){ return b0;}; // constant
c1.curvature = function(){ return 1/(2-p1.X());};
c2.curvature = function(){ return 1/(p1.X());};

thirdCircleX = function() {
    var b0,b1,b2,x0,x1,x2, b3,bx3;
    b0 = c0.curvature();
    b1 = c1.curvature();
    b2 = c2.curvature();
    x0 = c0.midpoint.X();
    x1 = c1.midpoint.X();
    x2 = c2.midpoint.X();

    b3 = solveQ2(b0,b1,b2,0);
    bx3 = solveQ2(b0*x0,b1*x1,b2*x2,2);
    return bx3[0]/b3[0];
}
thirdCircleY = function() {
    var b0,b1,b2,y0,y1,y2, b3,by3;
    b0 = c0.curvature();
    b1 = c1.curvature();
    b2 = c2.curvature();
    y0 = c0.midpoint.Y();
    y1 = c1.midpoint.Y();
    y2 = c2.midpoint.Y();

    b3 = solveQ2(b0,b1,b2,0);
    by3 = solveQ2(b0*y0,b1*y1,b2*y2,2);
    return by3[0]/b3[0];
}
thirdCircleRadius = function() {
    var b0,b1,b2, b3,bx3,by3;
    b0 = c0.curvature();
    b1 = c1.curvature();
    b2 = c2.curvature();
    b3 = solveQ2(b0,b1,b2,0);
    return 1.0/b3[0];
}

c3 = brd.create('circle',[[thirdCircleX,thirdCircleY],thirdCircleRadius],{strokeWidth:1});
c3.curvature = function(){ return 1.0/this.radius;};

otherCirc = function(circs,level) {
    var c, fx,fy,fr;
    if (level<=0) return;
    fx = function() {
        var b,x,i;
        b = [];
        x = [];
        for (i=0;i<4;i++) {
            b[i] = circs[i].curvature();
            x[i] = circs[i].midpoint.X();
        }
    
        b[4] = 2*(b[0]+b[1]+b[2])-b[3];
        x[4] = (2*(b[0]*x[0]+b[1]*x[1]+b[2]*x[2])-b[3]*x[3])/b[4];
        return x[4];
    }
    fy = function() {
        var b,y,i;
        b = [];
        y = [];
        for (i=0;i<4;i++) {
            b[i] = circs[i].curvature();
            y[i] = circs[i].midpoint.Y();
        }
    
        b[4] = 2*(b[0]+b[1]+b[2])-b[3];
        y[4] = (2*(b[0]*y[0]+b[1]*y[1]+b[2]*y[2])-b[3]*y[3])/b[4];
        return y[4];
    }
    fr = function() {
        var b,i;
        b = [];
        for (i=0;i<4;i++) {
            b[i] = circs[i].curvature();
        }
        b[4] = 2*(b[0]+b[1]+b[2])-b[3];
        if (isNaN(b[4])) {
            return 1000.0;
        } else {
            return 1/b[4];
        }
    }
    c = brd.create('circle',[[fx,fy],fr],{strokeWidth:1,name:'',
                 fillColor:JXG.hsv2rgb(50*level,0.8,0.8),highlightFillColor:JXG.hsv2rgb(50*level,0.5,0.8),fillOpacity:0.5,highlightFillOpacity:0.5});
    c.curvature = function(){ return 1/this.radius;};

    // Recursion
    otherCirc([circs[0],circs[1],c,circs[2]],level-1);
    otherCirc([circs[0],circs[2],c,circs[1]],level-1);
    otherCirc([circs[1],circs[2],c,circs[0]],level-1);
    return c;
}

//-------------------------------------------------------
brd.suspendUpdate();
level = 4;
otherCirc([c0,c1,c2,c3],level);
otherCirc([c3,c1,c2,c0],level);
otherCirc([c0,c2,c3,c1],level);
otherCirc([c0,c1,c3,c2],level);
brd.unsuspendUpdate();

References