/**
Logistic map explorer
Visualizes the bifurcation diagram that Robert May's logistic map gives rise to.
Swedish readers are directed to the historical essay
on chaos. Also availible is the scientific report
accompanying the applet with a more detailed description of the phenomenon.
Click and drag to zoom using a rubber rectangle.
Right click to zoom out.
Keys
- f : toggle between fixed and random inital x0
- r : toggle between randomized or sequential rendering
- x/z : increase/decrease the number of iterations before plotting
- m/n : increase/decrease the number of plotted iterations
- SPACE : standard zoom
- [any other key] : clear
*/
ImgBuffer imgBuffer;
PFont font;
int infinity = 1024;
int periodChunk = 1024;
int parameterChunk = 64;
int scanChunk = 64; // must divide width
int scanline = 0;
double rMin=2.4f, rMax=4f;
double xMin=0f, xMax=1f;
int mouseDownX, mouseDownY;
int spectrum=0;
boolean fixedInitialValue = true;
boolean stochasticRendering = false;
//-----------------
void setup(){
size(640,400);
imgBuffer = new ImgBuffer( width, height );
font = loadFont("font.vlw");
textFont( font );
}
//-----------------
void draw(){
if(!mousePressed){
if(stochasticRendering)
for(int i=0;i=width) scanline=0;
}
}
imgBuffer.drawBuffer();
//axes
int arrow = 3;
int len = 40;
int sep = 10;
stroke(255);
line(sep,sep,len,sep);
line(len,sep,len-arrow,sep-arrow);
line(len,sep,len-arrow,sep+arrow);
text("r",len+5,sep+3);
line(sep,sep,sep,len);
line(sep,len,sep-arrow,len-arrow);
line(sep,len,sep+arrow,len-arrow);
text("x",sep-3,len+10);
if(mousePressed) {
noFill();
stroke(255,0,0);
rect(mouseDownX,mouseDownY,mouseX-mouseDownX,mouseY-mouseDownY);
}
text( "plot "+periodChunk+" iterations", 10, height-60 );
text( "begin plotting after "+infinity+" iterations", 10, height-50 );
if(fixedInitialValue) text( "initial x = 0.5", 10, height-40);
else text( "initial x randomized", 10, height-40);
//String mx = limitDigits(rMin+mouseX*(rMax-rMin)/width , 15);
//String my = limitDigits(xMin+mouseY*(xMax-xMin)/height , 15);
double mr = rMin+mouseX*(rMax-rMin)/width;
double mx = xMin+mouseY*(xMax-xMin)/height;
text( "mouse :( "+mr+", "+mx+" )", 10, height-30);
text( "r:( "+rMin+", "+rMax+" )", 10, height-20 );
text( "x:( "+xMin+", "+xMax+" )", 10, height-10 );
}
//-----------------
String limitDigits(double x, int digits){
int k = (int)Math.pow(10,digits);
return "" + ( ((int)(x*k)) / (double)k );
}
//-----------------
void logisticMap(double r){
double x, rFactor = width/(rMax-rMin);
double xFactor = height/(xMax-xMin);
x = fixedInitialValue ? (double)0.5 : (double)random(0,1);
for(int i=0;imouseDownX) ? mouseX : mouseDownX ;
l = (mouseX>mouseDownX) ? mouseDownX : mouseX ;
b = (mouseY>mouseDownY) ? mouseY : mouseDownY ;
t = (mouseY>mouseDownY) ? mouseDownY : mouseY ;
if( min(r-l,b-t)>4 ) zoom(l, t, r, b);
}
else if( mouseButton==RIGHT)
zoom(2.0);
}
//-----------------
void zoom(float factor){
rMax = factor*0.5*(rMax-rMin) + (rMax+rMin)/2;
xMax = factor*0.5*(xMax-xMin) + (xMax+xMin)/2;
rMin = -factor*0.5*(rMax-rMin) + (rMax+rMin)/2;
xMin = -factor*0.5*(xMax-xMin) + (xMax+xMin)/2;
clearMap();
}
//-----------------
void zoom(float l, float t, float r, float b){
double t1 = (rMax-rMin)*r/(double)width + rMin;
double t2 = (xMax-xMin)*b/(double)height + xMin;
rMin = (rMax-rMin)*l/(double)width + rMin;
xMin = (xMax-xMin)*t/(double)height + xMin;
rMax = t1;
xMax = t2;
clearMap();
}
//-----------------
void keyPressed(){
if(!online && key=='s'){
saveFrame("bifurcation-##.tif");
return;
}
if(!online && key=='a'){
imgBuffer.drawBuffer();
saveFrame("bifurcation-##.tif");
return;
}
else if(key=='f')
fixedInitialValue = !fixedInitialValue;
else if(key=='r')
stochasticRendering = !stochasticRendering;
else if(key=='x')
infinity = infinity*2;
else if(key=='z')
infinity = (infinity+1)/2;
else if(key=='m')
periodChunk = periodChunk*2;
else if(key=='n')
periodChunk = (periodChunk+1)/2;
else if(key==' '){
rMin=2.4;
rMax=4f;
xMin=0f;
xMax=1f;
}
clearMap();
}