// Bifurcator.java: An applet for demoing the bifurcation behavior of // various equations. // Suzanne Skinner, completed 8-11-97 import java.applet.Applet; import java.awt.*; public class Bifurcator extends Applet { public BifDisplay display; public BifControls controls; public void init() { display = new BifDisplay(this); controls = new BifControls(this); setLayout(new BorderLayout()); add("South", controls); add("Center", display); } } class BifCanvas extends Canvas { static final int initIter = 50; static final int plottedIter = 50; private Image source = null; public BifCanvas() { setBackground(Color.lightGray); } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { Graphics gr; int width = size().width; int height = size().height; if (source == null) { source = createImage(width, height); gr = source.getGraphics(); gr.setColor(Color.white); gr.fillRect(0, 0, width, height); gr.setColor(Color.black); gr.drawLine(0, 0, 0, height-1); gr.drawLine(0, height-1, width-1, height-1); } g.drawImage(source, 0, 0, this); } public void plot(Func f, double xstart, double ystart, double xend, double yend) { Graphics gr; int width = size().width; int height = size().height; int x, y; short iter; double a, nextVal; double xscale, yscale; source = createImage(width, height); gr = source.getGraphics(); gr.setColor(Color.white); gr.fillRect(0, 0, width, height); gr.setColor(Color.black); gr.drawLine(0, 0, 0, height-1); gr.drawLine(0, height-1, width-1, height-1); gr.setColor(Color.blue); xscale = (double)width / (xend-xstart); yscale = (double)height / (yend-ystart); for (x=0; x < width; x++) { a = (double)x/xscale + xstart; nextVal = f.critPoint(); for (iter=0; iter < initIter; iter++) nextVal = f.compute(a, nextVal); for (iter=0; iter < plottedIter; iter++) { nextVal = f.compute(a, nextVal); y = (height-1) - (int)((nextVal - ystart) * yscale); if (y >=0 && y < height) gr.fillRect(x, y, 1, 1); } } repaint(); } } class CurvesCanvas extends Canvas { private Image source = null; public CurvesCanvas() { setBackground(Color.lightGray); } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { Graphics gr; int width = size().width; int height = size().height; if (source == null) { source = createImage(width, height); gr = source.getGraphics(); gr.setColor(Color.white); gr.fillRect(0, 0, width, height); gr.setColor(Color.black); gr.drawLine(0, 0, 0, height-1); gr.drawLine(0, height-1, width-1, height-1); } g.drawImage(source, 0, 0, this); } public void plot(Func f, short numIterations, double xstart, double ystart, double xend, double yend) { Graphics gr; int width = size().width; int height = size().height; int x, y; short iter; double a, nextVal; int[] prevPoints = new int[numIterations]; Color[] colors = {Color.red, Color.blue, Color.green, Color.magenta, Color.cyan, Color.black, Color.orange, Color.pink }; int numColors = colors.length; double xscale, yscale; source = createImage(width, height); gr = source.getGraphics(); gr.setColor(Color.white); gr.fillRect(0, 0, width, height); for (iter=0; iter < numIterations; iter++) prevPoints[iter] = -1; gr.setColor(Color.black); gr.drawLine(0, 0, 0, height-1); gr.drawLine(0, height-1, width-1, height-1); xscale = (double)width / (xend-xstart); yscale = (double)height / (yend-ystart); for (x=0; x < width; x++) { a = (double)x/xscale + xstart; nextVal = f.critPoint(); for (iter=0; iter < numIterations; iter++) { gr.setColor(colors[iter % numColors]); nextVal = f.compute(a, nextVal); y = (height-1) - (int)((nextVal - ystart) * yscale); if (y >=0 && y < height) { if (prevPoints[iter] == -1 || prevPoints[iter] == y) gr.fillRect(x, y, 1, 1); else gr.drawLine(x-1, prevPoints[iter], x, y); prevPoints[iter] = y; } else prevPoints[iter] = -1; } } repaint(); } } class BifDisplay extends Panel { private Bifurcator app; private BifCanvas bifCanvas; private CurvesCanvas curvesCanvas; private Func f; private short numIterations; private double xstart, ystart, xend, yend; private boolean plotting = false; public BifDisplay(Bifurcator app) { this.app = app; setLayout(new GridLayout(2, 1)); bifCanvas = new BifCanvas(); curvesCanvas = new CurvesCanvas(); add(bifCanvas); add(curvesCanvas); } public void plot(Func f, short numIterations, double xstart, double ystart, double xend, double yend) { if (plotting) return; else plotting = true; this.f = f; this.numIterations = numIterations; if (xstart < xend) { this.xstart = xstart; this.xend = xend; } else { this.xstart = xend; this.xend = xstart; } if (ystart < yend) { this.ystart = ystart; this.yend = yend; } else { this.ystart = yend; this.yend = ystart; } curvesCanvas.plot(f, numIterations, xstart, ystart, xend, yend); bifCanvas.plot(f, xstart, ystart, xend, yend); plotting = false; } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { curvesCanvas.repaint(); bifCanvas.repaint(); } } class BifControls extends Panel { private Bifurcator app; private Panel topRow, bottomRow; private TextField xstart, ystart, xend, yend; private Choice funcList; private TextField numIterations; private Func1 f1; private Func2 f2; private Func3 f3; public BifControls(Bifurcator app) { f1 = new Func1(); f2 = new Func2(); f3 = new Func3(); this.app = app; topRow = new Panel(); bottomRow = new Panel(); xstart = new TextField("1", 6); xend = new TextField("4", 6); ystart = new TextField("0", 6); yend = new TextField("1", 6); funcList = new Choice(); funcList.addItem("ax(1-x)"); funcList.addItem("asin(x)"); funcList.addItem("acos(x)"); numIterations = new TextField("6", 2); topRow.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 5)); topRow.add(new Label("HSpan:", Label.RIGHT)); topRow.add(xstart); topRow.add(new Label("--", Label.CENTER)); topRow.add(xend); topRow.add(new Label("VSpan:", Label.RIGHT)); topRow.add(ystart); topRow.add(new Label("--", Label.CENTER)); topRow.add(yend); bottomRow.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 5)); bottomRow.add(new Button("Start")); bottomRow.add(new Label("Function:", Label.RIGHT)); bottomRow.add(funcList); bottomRow.add(new Label("Num Iterations:", Label.RIGHT)); bottomRow.add(numIterations); setLayout(new GridLayout(2, 1)); setBackground(Color.lightGray); add(topRow); add(bottomRow); } public boolean action(Event evt, Object what) { if (evt.target instanceof Button) { Func f; switch (funcList.getSelectedIndex()) { case 0: f = f1; break; case 1: f = f2; break; default: f = f3; break; } app.display.plot( f, (short)Integer.parseInt(numIterations.getText()), Double.valueOf(xstart.getText()).doubleValue(), Double.valueOf(ystart.getText()).doubleValue(), Double.valueOf(xend.getText()).doubleValue(), Double.valueOf(yend.getText()).doubleValue() ); return true; } else if (evt.target instanceof Choice) { switch (funcList.getSelectedIndex()) { case 0: xstart.setText("1"); xend.setText("4"); ystart.setText("0"); yend.setText("1"); numIterations.setText("6"); break; case 1: xstart.setText("1"); xend.setText("4"); ystart.setText("-4"); yend.setText("4"); numIterations.setText("6"); break; default: xstart.setText("1"); xend.setText("4"); ystart.setText("-4"); yend.setText("4"); numIterations.setText("6"); } repaint(); return true; } else return false; } } abstract class Func { abstract double compute(double a, double x); abstract public double critPoint(); } class Func1 extends Func { public double compute(double a, double x) { return a*x*(1-x); } public double critPoint() {return 0.5;} } class Func2 extends Func { public double compute(double a, double x) { return a*Math.sin(x); } public double critPoint() {return Math.PI/2;} } class Func3 extends Func { public double compute(double a, double x) { return a*Math.cos(x); } public double critPoint() {return 0;} }