CompSci Today’s topics Java Recursion in Graphics Writing a Class Simple Animation Upcoming Simulation Reading Great Ideas, Chapters 5
CompSci Using Recursion in Graphics Recursion can be a powerful tool Closely related to Fractals Self-similarity Keep zooming in: still looks the same Can produce very interesting figures with very little input Serpinsky Gasket is just a lot of triangles Define recursively
CompSci Serpinsky Gasket Start with triangle Then put (1/2 size) triangles within triangle
CompSci Serpinsky Gasket Continue process with ¼ sized triangles, etc Insight : use Serpinsky Gaskets instead of triangles
CompSci Serpinsky public class Serpinsky extends java.applet.Applet implements ActionListener { Graphics g; Canvas c; IntField iSlow; Button b1, b2, b3, b4, b5, b6, b7, b8, b9; Color col; int x, y, slow; public void init() { c = new Canvas(); c.setSize(400, 400); add(c); g = c.getGraphics(); b1 = new Button("Start"); b2 = new Button("Triangle"); b3 = new Button("Gasket"); b4 = new Button("Red");
CompSci Serpinsky.2 b5 = new Button("Green"); b6 = new Button("Blue"); b7 = new Button("Black"); b8 = new Button("White"); b9 = new Button("Slow"); iSlow = new IntField(10); col = Color.blue; slow = 0; b1.addActionListener(this); b2.addActionListener(this); b3.addActionListener(this); b4.addActionListener(this); b5.addActionListener(this); b6.addActionListener(this); b7.addActionListener(this); b8.addActionListener(this); b9.addActionListener(this); add(iSlow); add(b1); add(b2); add(b3); add(b4); add(b5); add(b6); add(b7); add(b8); add(b9); }
CompSci Serpinsky.3 int mid(int a, int b) { return (a + b)/2; } void triangle(int x1, int y1, int x2, int y2, int x3, int y3) { int k; k = 0; while ( k < slow) k = k + 1; // code to slow down g.drawLine(x1, y1, x2, y2); g.drawLine(x2, y2, x3, y3); g.drawLine(x3, y3, x1, y1); }
CompSci Serpinsky.4 void serpinsky(int x1, int y1, int x2, int y2, int x3, int y3) { int size, limit=10; size = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1); if (size < limit) return; // base case: do nothing! i.e.: halting case triangle(x1, y1, x2, y2, x3, y3); // recursive calls serpinsky(x1, y1, mid(x1,x2), mid(y1,y2), mid(x1,x3), mid(y1,y3)); serpinsky(x2, y2, mid(x2,x1), mid(y2,y1), mid(x2,x3), mid(y2,y3)); serpinsky(x3, y3, mid(x3,x1), mid(y3,y1), mid(x3,x2), mid(y3,y2)); }
CompSci Serpinsky.5 public void actionPerformed(ActionEvent event) { Object cause = event.getSource(); if (cause == b1) { g.setColor(Color.white); // Color the whole canvas white g.fillRect(0, 0, 400, 400); g.setColor(Color.black); g.drawString("Serpinsky Gasket", 20, 20); } if (cause == b2) { g.setColor(col); triangle(50, 310, 200, 70, 350, 310); } if (cause == b3) { g.setColor(col); serpinsky(50, 310, 200, 70, 350, 310); }
CompSci Serpinsky.6 if (cause == b4) { col = Color.red; } if (cause == b5) { col = Color.green; } if (cause == b6) { col = Color.blue; } if (cause == b7) { col = Color.black; } if (cause == b8) { col = Color.white; } if (cause == b9) { slow = iSlow.getInt(); }
CompSci Serpinsky Details Note feature to slow down drawing Get betters sense of how recursive calls work Also see how incredibly fast computer is… Note new graphics method void drawString(String s, int x, int y) Review recursive features What is done in the base case? What would figure be like if we drew nothing except In the base case?
CompSci Writing a Class Have been writing one class all along Our applet is always a class Have used other classes such as TextFields Rewrite Serpinsky applet Write class named Serp which actually draws the gasket Class requires data (instance variables) Class requires methods (functions) Needs constructor (to initialize) (like init in applet) Now that we have such a class, can create many instances Use the new operator to accomplish this: new Serp(x1, y1, x2, y2, x3, y3, g); Where we pass in the 3 vertices of the triangle and the Graphics object
CompSci SerpClass public class SerpClass extends java.applet.Applet implements ActionListener { Graphics g; Canvas c; Button b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11; Color col; int range = 70; public void init() { c = new Canvas(); c.setSize(400, 400); add(c); g = c.getGraphics(); b1 = new Button("Start"); b2 = new Button("NW"); b3 = new Button("NE"); b4 = new Button("SW"); b5 = new Button("SE"); b6 = new Button("CTR"); b7 = new Button("Red"); b8 = new Button("Green"); b9 = new Button("Blue"); b10 = new Button("Black");
CompSci SerpClass.2 col = Color.black; b1.addActionListener(this); b2.addActionListener(this); b3.addActionListener(this); b4.addActionListener(this); b5.addActionListener(this); b6.addActionListener(this); b7.addActionListener(this); b8.addActionListener(this); b9.addActionListener(this); b10.addActionListener(this); b11.addActionListener(this); add(b1); add(b2); add(b3); add(b4); add(b5); add(b6); add(b7); add(b8); add(b9); add(b10); add(b11); } public void actionPerformed(ActionEvent event) { Object cause = event.getSource(); if (cause == b1) { g.setColor(Color.white); // Color the whole canvas white g.fillRect(0, 0, 400, 400); g.setColor(Color.black); g.drawString("Serpinsky Gasket", 20, 20); }
CompSci SerpClass.3 if (cause == b2) { // NW int dx = -range, dy = -range; g.setColor(col); new Serp(50+dx, 310+dy, 200+dx, 70+dy, 350+dx, 310+dy, g); } if (cause == b3) { // NE int dx = range, dy = -range; g.setColor(col); new Serp(50+dx, 310+dy, 200+dx, 70+dy, 350+dx, 310+dy, g); } if (cause == b4) { // SW int dx = -range, dy = range; g.setColor(col); new Serp(50+dx, 310+dy, 200+dx, 70+dy, 350+dx, 310+dy, g); }
CompSci SerpClass.4 if (cause == b5) { // SE int dx = range, dy = range; g.setColor(col); new Serp(50+dx, 310+dy, 200+dx, 70+dy, 350+dx, 310+dy, g); } if (cause == b6) { // CTR g.setColor(col); new Serp(50, 310, 200, 70, 350, 310, g); } if (cause == b7) col = Color.red; if (cause == b8) col = Color.green; if (cause == b9) col = Color.blue; if (cause == b10) col = Color.black; if (cause == b11) col = Color.white; }
CompSci SerpClass.5 public class Serp { Graphics g; public Serp(int x1, int y1, int x2, int y2, int x3, int y3, Graphics gg) { g = gg; serpinsky(x1, y1, x2, y2, x3, y3); } public int mid(int a, int b) { return (a + b)/2; } public void triangle(int x1, int y1, int x2, int y2, int x3, int y3) { g.drawLine(x1, y1, x2, y2); g.drawLine(x2, y2, x3, y3); g.drawLine(x3, y3, x1, y1); }
CompSci SerpClass.6 public void serpinsky(int x1, int y1, int x2, int y2, int x3, int y3) { int size, limit=200; size = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1); if (size < limit) // base case return; // do nothing triangle(x1, y1, x2, y2, x3, y3); // recursive calls serpinsky(x1, y1, mid(x1,x2), mid(y1,y2), mid(x1,x3), mid(y1,y3)); serpinsky(x2, y2, mid(x2,x1), mid(y2,y1), mid(x2,x3), mid(y2,y3)); serpinsky(x3, y3, mid(x3,x1), mid(y3,y1), mid(x3,x2), mid(y3,y2)); }
CompSci Simple Minded Animation Simple Recipe 1. Draw figure 2. Delay 3. Erases figure 4. Shift slightly 5. Repeat (step 1., etc.) Draw and Erase Draw figure: is straight-forward Erase: overdraw with background color (white) Will demonstrate with Serpinsky Gasket
CompSci Moving Serpinsky public class SerpMove extends java.applet.Applet implements ActionListener { Graphics g; Canvas c; IntField iSlow; Button b1, b2, b3, b4, b5, b6, b7, b8, b9; Color col; int slow = ; public void init() { c = new Canvas(); c.setSize(400, 400); add(c); g = c.getGraphics(); b1 = new Button("Start"); b2 = new Button("Move"); b3 = new Button("Gasket"); b4 = new Button("Red"); b5 = new Button("Green"); b6 = new Button("Blue"); b7 = new Button("Black"); b8 = new Button("White");
CompSci Moving Serpinsky.2 b9 = new Button("Slow"); iSlow = new IntField(10); col = Color.blue; b1.addActionListener(this); b2.addActionListener(this); b3.addActionListener(this); b4.addActionListener(this); b5.addActionListener(this); b6.addActionListener(this); b7.addActionListener(this); b8.addActionListener(this); b9.addActionListener(this); add(iSlow); add(b1); add(b2); add(b3); add(b4); add(b5); add(b6); add(b7); add(b8); add(b9); } public void delay() { double sum = 0.0, x = ; int k = 0; while (k < slow) { sum = sum + 1.0/x; k = k + 1; }
CompSci Moving Serpinsky.3 public void actionPerformed(ActionEvent event) { Object cause = event.getSource(); if (cause == b1) { g.setColor(Color.white); // Color the whole canvas white g.fillRect(0, 0, 400, 400); g.setColor(Color.black); g.drawString("Serpinsky Gasket", 20, 20); } Interesting Part Follows Actual Motion done here
CompSci Moving Serpinsky.4 if (cause == b2) { // move int k = 0, range = 100; int dx = -range, dy = -range; while (k < range) { dx = dx + 2; dy = dy + 2; g.setColor(col); new Serp(50+dx, 310+dy, 200+dx, 70+dy, 350+dx, 310+dy, g); delay(); g.setColor(Color.white); new Serp(50+dx, 310+dy, 200+dx, 70+dy, 350+dx, 310+dy, g); g.setColor(col); k = k + 1; }
CompSci Moving Serpinsky.5 if (cause == b3) { g.setColor(col); new Serp(50, 310, 200, 70, 350, 310, g); } if (cause == b4) col = Color.red; if (cause == b5) col = Color.green; if (cause == b6) col = Color.blue; if (cause == b7) col = Color.black; if (cause == b8) col = Color.white; if (cause == b9) slow = iSlow.getInt(); }
CompSci Simple Minded Animation Did not show the Serp class which is unchanged Quality of animation is poor: flicker, not smooth Beats with monitor and outside lighting Speed dependence on processor and load Erases anything “below” Fixing this is outside the scope of this class Should do much in this class that we do outside Could (should) add a number of methods Let it handle the move, just pass new locations void setLocation(int newX, int newY) void glideTo(int newX, int newY) The former would jump to new location, the latter would provide continuous motion