home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / javafile / ch07 / LifeApp.java < prev    next >
Encoding:
Java Source  |  1998-12-14  |  12.4 KB  |  608 lines

  1. import java.util.*;
  2. import java.awt.*;
  3. import java.awt.event.*;
  4. import java.awt.image.ImageObserver;
  5. import java.awt.image.PixelGrabber;
  6.  
  7. /*
  8.  * class that manages a generation
  9.  */
  10. class LifeGenerator {
  11.  
  12. /*
  13.  * array containing packed cells
  14.  * n - number of elements in the CellList array
  15.  */
  16. int CellList[], n;
  17. int a[];
  18. int b[];
  19. int c[];
  20.  
  21. /*
  22.  * the current generation number
  23.  */
  24. int generations;
  25.  
  26. /*
  27.  * background and foreground colors
  28.  */
  29. Color background;
  30. Color foreground;
  31.  
  32. int statusheight;
  33. int displaywidth;
  34. int displayheight;
  35.  
  36. int originx;
  37. int originy;
  38.  
  39. int maxcells;
  40.  
  41. static int countmsk=0x1f;
  42. static int posmsk=0x7fffffe0;
  43. static int maxval=0x7fffffff;
  44.  
  45. String statusLine;
  46. String loading;
  47.  
  48. int scroll;
  49.  
  50. /*
  51.  * the rules of life
  52.  */
  53. static boolean rules[]={
  54.     false, false,
  55.     false, false,
  56.     false, true,
  57.     true,  true,
  58.     false, false,
  59.     false, false,
  60.     false, false,
  61.     false, false, 
  62.     false, false
  63. };
  64.  
  65. /*
  66.  * constructor initializes variables
  67.  */
  68. public LifeGenerator() {
  69.  
  70.     n=0;
  71.     generations=0;
  72.     maxcells=0;
  73.     background=Color.white;
  74.     foreground=Color.black;
  75.  
  76.     originx=0;
  77.     originy=0;
  78.  
  79.     loading=null;
  80.     scroll=10;
  81.  
  82.     statusLine=new String("");
  83. }
  84.  
  85. public void loading(String init_loading) {
  86.  
  87.     loading=init_loading;
  88. }
  89.  
  90. public void setScroll(int init_scroll) {
  91.  
  92.     scroll=init_scroll;
  93. }
  94.  
  95. public void setColors(Color init_background,Color init_foreground) {
  96.  
  97.     background=init_background;
  98.     foreground=init_foreground;
  99. }
  100.  
  101. public void setDisplaySize(int width, int height) {
  102.  
  103.     statusheight=35;
  104.  
  105.     displaywidth=width;
  106.     displayheight=height-statusheight; 
  107. }
  108.  
  109. /**
  110.  * translate the origin
  111.  * @param dx, dy - offsets to translate
  112.  */
  113. public void translate(int dx, int dy) {
  114.  
  115.     originx+=dx;
  116.     originy+=dy;
  117. }
  118.  
  119. public void recenter(int x, int y) {
  120.  
  121.     translate(displaywidth/2-x, displayheight/2-y);
  122. }
  123.  
  124. public void approachcenter(int x, int y) {
  125.  
  126.     translate((displaywidth/2-x)/scroll,
  127.         (displayheight/2-y)/scroll);
  128. }
  129.  
  130. public void findPattern() {
  131.  
  132.     if (n>0) {
  133.         int packed=CellList[n/2];
  134.         int plotx=(((packed>>5)&0x1fff)-(1<<12))*2+originx;
  135.         int ploty=((packed>>18)-(1<<12))*2+originy;
  136.         recenter(plotx, ploty);
  137.     }
  138. }
  139.  
  140. /**
  141.  * print status message
  142.  * @param g - destination graphics object
  143.  */
  144. public void updateStatusLine(Graphics g) {
  145.  
  146.     g.setColor(background);
  147.     g.drawString(statusLine,0,displayheight+15);
  148.  
  149.     if (loading!=null) {
  150.         statusLine="Loading: " + loading;
  151.     } else {
  152.         statusLine="Generations: " + generations + "  Cells: " + n;
  153.     }
  154.     g.setColor(foreground);
  155.     g.drawString(statusLine,0,displayheight+15);
  156. }
  157.  
  158. void resizeIfNeeded(int cellcount) {
  159.  
  160.     int tmp[];
  161.     int i;
  162.  
  163.     if (cellcount>maxcells) {
  164.  
  165.         int newsize=2*cellcount;
  166.  
  167.         tmp=new int[newsize];
  168.         for (i=0; i<maxcells; i++) tmp[i]=CellList[i];
  169.         CellList=tmp;
  170.  
  171.         tmp=new int[newsize];
  172.         for (i=0; i<maxcells; i++) tmp[i]=a[i];
  173.         a=tmp;
  174.  
  175.         tmp=new int[newsize];
  176.         for (i=0; i<maxcells; i++) tmp[i]=b[i];
  177.         b=tmp;
  178.  
  179.         tmp=new int[newsize];
  180.         for (i=0; i<maxcells; i++) tmp[i]=c[i];
  181.         c=tmp;
  182.  
  183.         maxcells=newsize;
  184.     }
  185. }
  186.  
  187. static int combineLists(int a[], int na, int b[], int nb,int c[]) {
  188.  
  189.     int i,j,nc;
  190.     i=0; j=0; nc=0;
  191.     a[na]=maxval;
  192.     b[nb]=maxval;
  193.     while (i<na || j<nb) {
  194.         if ((a[i]^b[j])<=countmsk) {
  195.             c[nc++]=(a[i++]&countmsk)+b[j++];
  196.         } else if (a[i]<b[j]) {
  197.             c[nc++]=a[i++];
  198.         } else {
  199.             c[nc++]=b[j++];
  200.         }
  201.     }
  202.     return nc;
  203. }
  204.  
  205. static void extractCenterCells(int list[], int n, int counts[]) {
  206.  
  207.     int i=0, j=0; 
  208.  
  209.     while (i<n) {
  210.         if ((list[i]^counts[j])<=countmsk) {
  211.             counts[j]-;
  212.             i++;
  213.             j++;
  214.         } else j++;
  215.     }
  216. }
  217.  
  218. static int Cell(int x, int y, int value) {
  219.  
  220.     return ((y+(1<<12))<<18) +((x+(1<<12))<<5) + value;
  221. }
  222.  
  223. /**
  224.  * plot an individual cell
  225.  * @param packed - a set of packed cells
  226.  * @param g - destination graphics object
  227.  */
  228. void plotCell(int packed, Graphics g) {
  229.  
  230.     int plotx=(((packed>>5)&0x1fff)-(1<<12))*2+originx;
  231.     int ploty=((packed>>18)-(1<<12))*2+originy;
  232.  
  233.     if (plotx > 3 && plotx < displaywidth-5 &&
  234.         ploty > 3 && ploty < displayheight-5 ) {
  235.         g.fillRect(plotx, ploty, 2, 2);
  236.     }
  237. }
  238.  
  239. /**
  240.  * paint the current generation
  241.  * @param g - destination graphics object
  242.  */
  243. public void paintAll(Graphics g) {
  244.  
  245.     g.clearRect(0,0,displaywidth, displayheight+statusheight);
  246.     g.drawRect(0,0,displaywidth-1, displayheight-1);
  247.  
  248.     g.setColor(foreground);
  249.     for (int i=0; i<n; i++) {
  250.         plotCell(CellList[i],g);
  251.     }
  252.     updateStatusLine(g);
  253. }
  254.  
  255. int nextGen(int counts[], int ncounts, int list[], Graphics g) {
  256.  
  257.     int nlist=0;
  258.     for (int i=0; i<ncounts; i++) {
  259.         int count=counts[i]&countmsk;
  260.         if (rules[count]) {
  261.             list[nlist++]=(counts[i]&posmsk)+2;
  262.             if ((count&1)==0) {
  263.                 g.setColor(foreground);
  264.                 plotCell(counts[i],g);
  265.             }
  266.         } else {
  267.             if ((count&1)==1) {
  268.                 g.setColor(background);
  269.                 plotCell(counts[i],g); 
  270.             }
  271.         }
  272.     }
  273.     return nlist;
  274. }
  275.  
  276. public void generate(Graphics g) {
  277.  
  278.     int na, nb, nc; 
  279.  
  280.     for (na=0; na<n; na++) a[na]=CellList[na]-(1<<18);
  281.     resizeIfNeeded(n+na);
  282.     nb=combineLists(CellList,n,a,na,b);
  283.  
  284.     for (na=0; na<n; na++) a[na]=CellList[na]+(1<<18);
  285.     resizeIfNeeded(na+nb);
  286.     nc=combineLists(a,na,b,nb,c);
  287.  
  288.     for (na=0; na<nc; na++) a[na]=c[na]-(1<<5);
  289.     resizeIfNeeded(na+nc);
  290.     nb=combineLists(a,na,c,nc,b);
  291.  
  292.     for (na=0; na<nc; na++) a[na]=c[na]+(1<<5);
  293.     resizeIfNeeded(na+nb);
  294.     nc=combineLists(a,na,b,nb,c);
  295.  
  296.     extractCenterCells(CellList, n, c);
  297.  
  298.     n=nextGen(c, nc, CellList, g);
  299.  
  300.     generations++;
  301. }
  302.  
  303. /**
  304.  * load a new initial image
  305.  * @param img - the image to load
  306.  * @param imgobs - the image observer
  307.  */
  308. public boolean loadLifePattern(Image img, ImageObserver imgobs) {
  309.  
  310.     int w=img.getWidth(imgobs);
  311.     int h=img.getHeight(imgobs);
  312.  
  313.     if (w<0 || h<0) return false;
  314.  
  315.     originx= (displaywidth-w*2)/2;
  316.     originy= (displayheight-h*2)/2;
  317.  
  318.     int[] pixels = new int[w * h];
  319.  
  320.     PixelGrabber pg = new PixelGrabber(img, 0, 0, w, h, pixels, 0, w);
  321.  
  322.     try {
  323.         pg.grabPixels();
  324.     } catch (InterruptedException e) {
  325.         return false;
  326.     }
  327.  
  328.     int i,j;
  329.  
  330.     int pix0= pixels[0];
  331.     int pix1= -1;
  332.     int count1= 0;
  333.  
  334.     for (i=0; i<h; i++) {
  335.         for (j=0; j<w; j++) {
  336.             if (pixels[i*w+j]!=pix0) {
  337.                 pix1= pixels[i*w+j]; 
  338.                 count1++;
  339.             }
  340.         }
  341.     }
  342.  
  343.    /* figure out which pixel color denotes a live cell */
  344.  
  345.     if (pix0==0xffffff) {}
  346.     else if (pix1==0xffffff || count1 > w*h-count1) {
  347.         pix1=pix0;
  348.         count1=w*h-count1; 
  349.     }
  350.  
  351.     resizeIfNeeded(count1);
  352.  
  353.     n=0;
  354.     for (i=0; i<h; i++) {
  355.         for (j=0; j<w; j++) {
  356.             if (pixels[i*w+j]==pix1) {
  357.                 CellList[n++]=Cell(j,i,2);
  358.             }
  359.         }
  360.     }
  361.  
  362.     return true;
  363. }
  364. }
  365.  
  366. /*
  367.  * the applet class
  368.  */
  369. public class LifeApp extends java.applet.Applet implements MouseListener,
  370.                     ActionListener, Runnable {
  371.  
  372. LifeGenerator LifeList;
  373.  
  374. /*
  375.  * the thread controlling generations
  376.  */
  377. Thread killme=null;
  378. int speed=50;
  379. boolean neverPainted=true;
  380. int count=0;
  381.  
  382. /*
  383.  * the image name text field
  384.  */
  385. TextField patfield;
  386. Button pausebutton;
  387. Button stepbutton;
  388. Button recenterbutton;
  389. Button loadbutton;
  390. boolean generating=false;
  391. int stepsleft=0;
  392. int scrollfraction=5;
  393.  
  394. /*
  395.  * called when applet is loaded
  396.  * create user interface and parse applet parameters
  397.  */
  398. public void init() {
  399.  
  400.     setLayout(new FlowLayout(FlowLayout.RIGHT, 0, getSize().height-30));
  401.     add(pausebutton=new Button("Start"));
  402.     add(stepbutton = new Button("Step"));
  403.     add(recenterbutton = new Button("Recenter"));
  404.     add(loadbutton = new Button("Load:"));
  405.  
  406.     pausebutton.addActionListener(this);
  407.     stepbutton.addActionListener(this);
  408.     recenterbutton.addActionListener(this);
  409.     loadbutton.addActionListener(this);
  410.  
  411.     addMouseListener(this);
  412.  
  413.     String patname=getParameter("pattern");
  414.     if (patname==null) patname="gun30";
  415.  
  416.     if (getParameter("started")!=null) {
  417.         pausebutton.setLabel("Stop");
  418.         generating=true; 
  419.     }
  420.  
  421.     String pstring;
  422.  
  423.     if ((pstring=getParameter("speed"))!=null) {
  424.         speed=Integer.valueOf(pstring).intValue();
  425.     }
  426.  
  427.     if ((pstring=getParameter("scrollfraction"))!=null) {
  428.         scrollfraction=Integer.valueOf(pstring).intValue();
  429.     }
  430.  
  431.     add(patfield=new TextField(patname,8));
  432.  
  433.     LifeList=null;
  434. }
  435.  
  436. /*
  437.  * called when applet is started
  438.  * start the life thread
  439.  */
  440. public void start() {
  441.  
  442.     if (killme==null) {
  443.         killme=new Thread(this);
  444.         killme.start();
  445.     }
  446. }
  447.  
  448. /*
  449.  * called when the applet is stopped
  450.  * stop the life thread
  451.  */
  452. public void stop() {
  453.  
  454.  
  455.     killme=null;
  456. }
  457.  
  458.  
  459. //********************************************************
  460. public void mouseClicked(MouseEvent e){}
  461. public void mouseEntered(MouseEvent e){}
  462. public void mouseExited(MouseEvent e){}
  463.  
  464. public void mouseReleased(MouseEvent e)
  465. {
  466. }
  467.  
  468. public void mousePressed(MouseEvent e)
  469. {
  470.     int x =e.getX();
  471.     int y =e.getY();
  472.  
  473.     LifeList.approachcenter(x,y);
  474.     LifeList.paintAll(getGraphics());
  475. }
  476.  
  477. /**
  478.  * reorder the images, depending on which button is clicked
  479.  * @param evt - event object
  480.  * @param arg - target object
  481.  */
  482. public void actionPerformed(ActionEvent evt)
  483. {
  484.     boolean acted=true;
  485.     boolean damage=true;
  486.  
  487.     Object object1 = evt.getSource();
  488.  
  489.     if (object1 == pausebutton)
  490.     {
  491.         String label= pausebutton.getLabel();
  492.         if (label == "Stop")
  493.         {
  494.             pausebutton.setLabel("Start");
  495.             generating=false;
  496.         } else if (label == "Start")
  497.         {
  498.             pausebutton.setLabel("Stop");
  499.             generating=true;
  500.         }
  501.     }
  502.  
  503.     if (object1 == stepbutton)
  504.     {
  505.         stepsleft=1;
  506.         if (generating)
  507.         {
  508.             pausebutton.setLabel("Start");
  509.             generating=false;
  510.         }
  511.     }
  512.     if (object1 == recenterbutton)
  513.     {
  514.         LifeList.findPattern();
  515.     }
  516.     if (object1 == loadbutton)
  517.     {
  518.         stop();
  519.         LifeList=null;
  520.         start();
  521.     }
  522.     if (acted && damage) LifeList.paintAll(getGraphics());
  523.  
  524. }
  525.  
  526.  
  527. //***********************************************************
  528.  
  529.  
  530. /**
  531.  * add .gif to the filename
  532.  * @param patname - base filename
  533.  */
  534. static String makeGifName(String patname) {
  535.  
  536.     int i=patname.indexOf(".");
  537.     String base=(i<0)?patname:patname.substring(0,i);
  538.     return base.concat(".gif");
  539. }
  540.  
  541. /**
  542.  * load new image file
  543.  * @parame patname - name of image file
  544.  */
  545. void loadNew(String patname) {
  546.  
  547.     Image img=getImage(getCodeBase(), makeGifName(patname));
  548.     LifeList.loading(patname);
  549.     LifeList.paintAll(getGraphics());
  550.  
  551.     while(killme!=null && !LifeList.loadLifePattern(img,
  552. σthis)) {
  553.         try {
  554.             Thread.sleep(200);
  555.         } catch (InterruptedException e) {}
  556.     }
  557.  
  558.     LifeList.loading(null);
  559.     LifeList.paintAll(getGraphics());
  560. }
  561.  
  562. /*
  563.  * life thread
  564.  * causes new generations to be created
  565.  */
  566. public void run() {
  567.  
  568.     Graphics g=getGraphics();
  569.  
  570.     if (LifeList==null) {
  571.         LifeList = new LifeGenerator();
  572.         LifeList.setColors(getBackground(), Color.black); 
  573.         LifeList.setScroll(scrollfraction);
  574.         LifeList.setDisplaySize(getSize().width, getSize().height);
  575.         loadNew(patfield.getText());
  576.     }
  577.  
  578.     while (killme != null) {
  579.         try {
  580.             Thread.sleep(speed);
  581.         } catch (InterruptedException e) {}
  582.         repaint();
  583.     }
  584.     killme=null;
  585. }
  586.  
  587. /**
  588.  * paint the current generation
  589.  * @param g - destination graphics object
  590.  */
  591. public void paint(Graphics g) {
  592.  
  593.     LifeList.paintAll(g);
  594. }
  595.  
  596. /**
  597.  * override update to avoid erase flicker
  598.  * @param g - destination graphics object
  599.  */
  600. public void update(Graphics g) {
  601.  
  602.     if (generating || stepsleft- > 0) {
  603.         LifeList.generate(g);
  604.         LifeList.updateStatusLine(g);
  605.     }
  606. }
  607. }
  608.