// faces.java // Copyright 2008 by Jeffrey Rosenthal // All rights reserved. // jeff@math.toronto.edu // http://probability.ca/jeff/ import java.util.*; import java.lang.Math; import java.applet.*; import java.awt.*; import java.awt.image.*; public class faces extends Applet implements Runnable { int i,j,NUMDOTS,NUMOBJ; int dotwidth = 4; // physical upper-left corner = (dotwidth*i, dotwidth*j) int MAXNUMDOTS = 8000; int MAXNUMOBJ = 25; int BUTTONWIDTH = 150; int BUTTONHEIGHT = 40; int BUTTONEDGE = 5; int CONTROLWIDTH = 150; int CONTROLHEIGHT = 45; int CONTROLEDGE = 25; int YWIDTH = 20; String[] BUTLAB = {"SPRINKLE DOTS", "CLEAR DOTS", "FIND ONE", "FIND ANOTHER", "CLEAR SEARCH", "DUMP DATA", "RESTORE DATA", " BLOB FACE"}; int NUMBUTTONS = BUTLAB.length; String[] CONTROLLAB = {" ", " NUMTRIES", " NUMLOC", "BACKTRACK", "ONEATTIME", " ABORTVAL" }; int NUMCONTROLS = CONTROLLAB.length; int dotx[] = new int[MAXNUMDOTS]; int doty[] = new int[MAXNUMDOTS]; boolean FACES = true; boolean BACKTRACKING = true; boolean ONEATTIME = true; int ABORTVAL = -100; int nextcoord = 0; boolean MUSTFIND = false; int imagewidth, imageheight, imageright, imagebottom; // int imageval[][] = new int[imagewidth][imageheight]; int imageval[][] = new int[600][400]; Color latestcolor = Color.red; Color oldcolor = new Color(255,100,100); // faded red Color lightblue = new Color(180,180,255); // faded red int testx, testy, testw, testh, teste, tests, testb; int bestx, besty, bestw, besth, beste, bests, bestb; int prevx, prevy, prevw, prevh, preve, prevs, prevb; int prevscore; int ox[] = new int[MAXNUMOBJ]; int oy[] = new int[MAXNUMOBJ]; int ow[] = new int[MAXNUMOBJ]; int oh[] = new int[MAXNUMOBJ]; int oe[] = new int[MAXNUMOBJ]; int os[] = new int[MAXNUMOBJ]; int ob[] = new int[MAXNUMOBJ]; int left[] = new int[MAXNUMOBJ]; int right[] = new int[MAXNUMOBJ]; int top[] = new int[MAXNUMOBJ]; int bottom[] = new int[MAXNUMOBJ]; boolean isface[] = new boolean[MAXNUMOBJ]; int minrad = 0; int maxrad = 8; int highestscore = 0; int newscore = 0; int localcount; // int NUMTRIES = 500000; int NUMTRIES = 50000; int NUMLOC = 1000; // int neginf = -999999999; int neginf = -9999999; Random randvar = new Random(); Image holdImage; Graphics holdGraphics; public void init() { NUMDOTS = NUMOBJ = 0; Font strongfont = new Font ("TimesRoman", Font.BOLD, 14); setFont(strongfont); holdImage = createImage(size().width, size().height); holdGraphics = holdImage.getGraphics(); imageright = size().width - BUTTONWIDTH - CONTROLWIDTH; imagebottom = size().height; imagewidth = imageright / dotwidth; imageheight = imagebottom / dotwidth; // Output some initialisation information. System.out.println(""); System.out.println("Creating " + imagewidth + " x " + imageheight + " image window."); // Start with pre-set image, plus extra sprinkled dots. setimage(); sprinkledots(5000); } public void run() { while (true) { dosleep(50); if (MUSTFIND) { MUSTFIND = false; updateimage(); findobj(); repaint(); } } } public void paint(Graphics g) { // Background rectangle. g.setColor(Color.pink); g.fillRect(0,0,imageright,imagebottom); // Draw dots. g.setColor(Color.black); for (int ii=0; ii < NUMDOTS; ii++) { g.fillRect(dotx[ii]*dotwidth,doty[ii]*dotwidth, dotwidth,dotwidth); } // Draw objects. g.setColor(oldcolor); for (int ii=0; ii < NUMOBJ; ii++) { if (ii==NUMOBJ-1) g.setColor(latestcolor); if (isface[ii]) { drawface(g, ox[ii], oy[ii], ow[ii], os[ii], oe[ii], ob[ii], oh[ii]); } else { drawblob(g, ox[ii], oy[ii], ow[ii], oh[ii]); } } // Button/Control background. g.setColor(Color.orange); g.fillRect(imageright,0,BUTTONWIDTH,imagebottom); g.setColor(lightblue); g.fillRect(imageright+BUTTONWIDTH,0,CONTROLWIDTH,imagebottom); g.setColor(Color.green); g.fillRect(imageright + indicator(FACES) * BUTTONWIDTH/2, BUTTONHEIGHT*(NUMBUTTONS-1), BUTTONWIDTH/2, BUTTONHEIGHT); g.fillRect(imageright + BUTTONWIDTH + (CONTROLWIDTH-YWIDTH)*indicator(BACKTRACKING), CONTROLHEIGHT*3, YWIDTH, CONTROLHEIGHT); // System.out.println(imagewidth + " " + BUTTONWIDTH + " " + CONTROLWIDTH + " " + YWIDTH + " " + (CONTROLWIDTH/YWIDTH-1)*indicator(BACKTRACKING)*YWIDTH ); g.fillRect(imageright + BUTTONWIDTH + (CONTROLWIDTH-YWIDTH)*indicator(ONEATTIME), CONTROLHEIGHT*4, YWIDTH, CONTROLHEIGHT); // Button/Control text. g.setColor(Color.black); for (i=0; i < NUMBUTTONS; i++) { g.drawString(BUTLAB[i], imageright+BUTTONEDGE, (2*i+1)*BUTTONHEIGHT/2 + 8); g.fillRect( imageright, (2*i+2)*BUTTONHEIGHT/2 - 0, BUTTONWIDTH, 6); } for (i=0; i < NUMCONTROLS; i++) { g.drawString(CONTROLLAB[i], imageright+BUTTONWIDTH+CONTROLEDGE, (2*i+1)*CONTROLHEIGHT/2 + 8 - 6*indicator((i<=2)||(i==5)) ); if (i==1) // NUMTRIES g.drawString(" " + NUMTRIES, imageright+BUTTONWIDTH+CONTROLWIDTH/2-30, (2*i+1)*CONTROLHEIGHT/2 + 8 + 12); if (i==2) // NUMLOC g.drawString(" " + NUMLOC, imageright+BUTTONWIDTH+CONTROLWIDTH/2-25, (2*i+1)*CONTROLHEIGHT/2 + 8 + 12); if (i==5) // ABORTVAL if (ABORTVAL >= -500) g.drawString(" " + ABORTVAL, imageright+BUTTONWIDTH+CONTROLWIDTH/2-25, (2*i+1)*CONTROLHEIGHT/2 + 8 + 12); else g.drawString("-infinity", imageright+BUTTONWIDTH+CONTROLWIDTH/2-25, (2*i+1)*CONTROLHEIGHT/2 + 8 + 12); if ( (i==3) || (i==4) ) { g.drawString("Y", size().width-15, (2*i+1)*CONTROLHEIGHT/2 + 8); g.drawString("N", imageright+BUTTONWIDTH+5, (2*i+1)*CONTROLHEIGHT/2 + 8); } if ( (i==1) || (i==2) || (i==5) ) { g.drawString("+", size().width-15, (2*i+1)*CONTROLHEIGHT/2 + 8); g.drawString("-", imageright+BUTTONWIDTH+5, (2*i+1)*CONTROLHEIGHT/2 + 8); } g.fillRect( imageright+BUTTONWIDTH, (2*i+2)*CONTROLHEIGHT/2 - 0, CONTROLWIDTH, 6); } // Draw lines through invalid button choices. if (NUMOBJ >= MAXNUMOBJ) { g.fillRect( imageright+2, (2*3+1)*BUTTONHEIGHT/2 - 0, BUTTONWIDTH-8, 3); } if (NUMDOTS >= MAXNUMDOTS) { g.fillRect( imageright+2, (2*0+1)*BUTTONHEIGHT/2 - 0, BUTTONWIDTH-8, 3); } // Draw latest score. if (NUMOBJ>0) { g.setColor(latestcolor); g.drawString("score = " + highestscore, imageright+BUTTONWIDTH/5, imagebottom - BUTTONHEIGHT/3); } } public void drawoval(Graphics g, int xx, int yy, int rr, int ss) { g.drawOval(xx-rr, yy-ss, 2*rr, 2*ss); } public void drawblob(Graphics g, int xx, int yy, int ww, int hh) { for (i=1; i<6; i++) { drawoval(g,dotwidth*xx+dotwidth/2,dotwidth*yy+dotwidth/2, dotwidth*ww+i,dotwidth*hh+i); } } public void drawface(Graphics g, int xx, int yy, int ww, int ss, int ee, int bb, int hh) { drawblob(g, xx+ww, yy, ee, ee/2); drawblob(g, xx-ww, yy, ee, ee/2); drawtriangle(g, xx, yy+ss, bb, hh); } // public void triangleit(Graphics g, int xxx, int yyy, int ww, int hh) { // for (i=0; i<3; i++) { // if ( (ww-i>0) && (hh-2*i>0) ) // drawtriangle(g, xxx, yyy, ww-i, hh-2*i); // } // } public void drawtriangle(Graphics g, int xxx, int yyy, int ww, int hh) { drawthickline(g, xxx, yyy, xxx-ww, yyy+hh); // System.out.println("drawing line from (" + xxx + "," + yyy + ") to (" + (xxx-ww) + "," + (yyy+hh) + ")"); drawthickline(g, xxx, yyy, xxx+ww, yyy+hh); drawthickline(g, xxx-ww, yyy+hh, xxx+ww, yyy+hh); } public void drawthickline(Graphics g, int x1, int y1, int x2, int y2) { for (i=-1; i<=+1; i++) for (j=-1; j<=+1; j++) g.drawLine(x1*dotwidth+i, y1*dotwidth+j, x2*dotwidth+i, y2*dotwidth+j); } public boolean action(Event e, Object o) { return true; } public void update(Graphics g) { paint(holdGraphics); g.drawImage(holdImage, 0, 0, this); } public boolean mouseDown(Event evt, int x, int y) { if ( (x > imageright) && (x <= imageright+BUTTONWIDTH) ) { // Mouse is on a yellow button. switch( ifloor(y/BUTTONHEIGHT) ) { case 0: // SPRINKLE DOTS sprinkledots(100); break; case 1: // CLEAR DOTS NUMDOTS = NUMOBJ = 0; break; case 2: // FIND ONE if (NUMOBJ==0) { updateimage(); findobj(); } else { NUMOBJ = 0; // repaint(); MUSTFIND = true; } break; case 3: // FIND ANOTHER if (NUMOBJ < MAXNUMOBJ) { updateimage(); findobj(); } break; case 4: // CLEAR SEARCH NUMOBJ = 0; break; case 5: // DUMP DATA dumpimage(); break; case 6: // RESTORE DATA setimage(); break; case 7: // BLOB ... FACE if (x < imageright + BUTTONWIDTH/2) FACES = false; else FACES = true; break; } } else if ( x > imageright+BUTTONWIDTH ) { // Mouse is on a blue control. switch( ifloor(y/CONTROLHEIGHT) ) { case 1: // NUMTRIES if (x >= imageright + BUTTONWIDTH + CONTROLWIDTH - YWIDTH) NUMTRIES = zeroround(NUMTRIES) + 10000; else if (x <= imageright + BUTTONWIDTH + YWIDTH) NUMTRIES = imax(1, NUMTRIES - 10000); break; case 2: // NUMLOC if (x >= imageright + BUTTONWIDTH + CONTROLWIDTH - YWIDTH) { if (NUMLOC==1) NUMLOC = 10; else if (NUMLOC==10) NUMLOC = 50; else if (NUMLOC==50) NUMLOC = 100; else NUMLOC = NUMLOC + 100; // NUMLOC = zeroround(NUMLOC) + 100; } else if (x <= imageright + BUTTONWIDTH + YWIDTH) { if (NUMLOC==100) NUMLOC = 50; else if (NUMLOC==50) NUMLOC = 10; else if (NUMLOC==10) NUMLOC = 1; else NUMLOC = imax(1, NUMLOC - 100); } break; case 3: // BACKTRACK if (x > imageright + BUTTONWIDTH + CONTROLWIDTH - YWIDTH) BACKTRACKING = true; else if (x < imageright + BUTTONWIDTH + YWIDTH) BACKTRACKING = false; break; case 4: // ONEATTIME if (x > imageright + BUTTONWIDTH + CONTROLWIDTH - YWIDTH) ONEATTIME = true; else if (x < imageright + BUTTONWIDTH + YWIDTH) ONEATTIME = false; break; case 5: // ABORTVAL if (x >= imageright + BUTTONWIDTH + CONTROLWIDTH - YWIDTH) ABORTVAL = ABORTVAL + 10; else if (x <= imageright + BUTTONWIDTH + YWIDTH) ABORTVAL = imax(ABORTVAL - 10, -510); break; } } else { // Mouse is on pink drawing area. adddot(x/dotwidth,y/dotwidth); } repaint(); return true; } // public boolean mouseUp(Event evt, int x, int y) { // } // public boolean mouseMove(Event evt, int x, int y) { // } public boolean mouseDrag(Event evt, int x, int y) { // http://www.javacoffeebreak.com/java107/java107.html adddot(x/dotwidth,y/dotwidth); repaint(); return true; } public void adddot(int xx, int yy) { if (NUMDOTS < MAXNUMDOTS) { dotx[NUMDOTS] = xx; doty[NUMDOTS] = yy; NUMDOTS++; NUMOBJ = 0; } } public int randx() { // return( ifloor(Math.random() * imagewidth) ); return( randvar.nextInt(imagewidth-1) ); // randvar.nextInt(n) is integer between 0 and n-1 ... } public int randy() { // return( ifloor(Math.random() * imageheight) ); return( randvar.nextInt(imageheight-1) ); } public int randn() { return( randvar.nextInt(12) + 2); } public int randinc() { return( randvar.nextInt(5) - 2); } public int randincsm() { return( randvar.nextInt(3) - 1); } public int randrad() { return( randvar.nextInt(maxrad-minrad+1) + minrad); } public void updateimage() { for (i=0 ; i 0) { System.out.println(" "); System.out.println(" "); System.out.print("int datax[] = {"); for (i=0 ; i= NUMLOC) || ((localcount==0) && (newscore==neginf)) || ((ABORTVAL >= -500) && (newscore < ABORTVAL)) ) { // Try completely new values ("global update"). testx = prevx = randx(); testy = prevy = randy(); testw = prevw = randrad(); testh = prevh = randrad(); if (FACES) { teste = preve = randrad(); testb = prevb = randn(); tests = prevs = randn(); } else { teste = testb = tests = preve = prevb = prevs = 0; } localcount = 0; } else { // Increment previous values ("local update"). if (ONEATTIME) { nextcoord = randvar.nextInt(4 + 3*indicator(FACES)); // randvar.nextInt(n) is integer between 0 and n-1 ... } else { nextcoord = 0; } switch (nextcoord) { case 0: testx = testx + randinc(); if (ONEATTIME) break; case 1: testy = testy + randinc(); if (ONEATTIME) break; case 2: testw = testw + randincsm(); if (ONEATTIME) break; case 3: testh = testh + randincsm(); if ( ONEATTIME || (!FACES) ) break; // if (FACES) { case 4: teste = teste + randincsm(); if (ONEATTIME) break; case 5: testb = testb + randincsm(); if (ONEATTIME) break; case 6: tests = tests + randincsm(); if (ONEATTIME) break; // } } localcount++; } // Determine if new values improve previous best. if (!tooclose(testx,testy,testw,tests,teste,testh,testb)) { // Check restrictions and compute score. if (FACES) { if ( (1 <= teste) && (teste < testw) && (testw <=20) && (0 <= tests) && (tests <= testw) && (0 <= testh) && (testh <= 20) && (0 <= testb) && (testb <= testw) && (tests+testh > teste/2) ) { newscore = blobscore(testx-testw,testy,teste,teste/2) + blobscore(testx+testw,testy,teste,teste/2) + trianglescore(testx,testy,tests,testb,testh); } else { newscore = neginf; } } else { // not FACES, i.e. blob ... if ( (testw>=minrad) && (testw<=maxrad) && (testh>=minrad) && (testh<=maxrad) ) { newscore = blobscore(testx,testy,testw,testh); } else { newscore = neginf; } } if (newscore >= highestscore) { // We have found new best values. bestx = testx; besty = testy; bestw = testw; besth = testh; beste = teste; bestb = testb; bests = tests; highestscore = newscore; } } // Do backtracking. if (BACKTRACKING && (newscore < prevscore) ) { testx = prevx; testy = prevy; testw = prevw; testh = prevh; teste = preve; testb = prevb; tests = prevs; } else { prevx = testx; prevy = testy; prevw = testw; prevh = testh; preve = teste; prevb = testb; prevs = tests; } } // Save the values for the new object. isface[NUMOBJ] = FACES; ox[NUMOBJ] = bestx; oy[NUMOBJ] = besty; ow[NUMOBJ] = bestw; oh[NUMOBJ] = besth; oe[NUMOBJ] = beste; os[NUMOBJ] = bests; ob[NUMOBJ] = bestb; left[NUMOBJ] = theleft(bestx, besty, bestw, besth, bests, beste, bestb); right[NUMOBJ] = theright(bestx, besty, bestw, besth, bests, beste, bestb); top[NUMOBJ] = thetop(bestx, besty, bestw, besth, bests, beste, bestb); bottom[NUMOBJ] = thebottom(bestx, besty, bestw, besth, bests, beste, bestb); NUMOBJ++; // Output values to the java console. System.out.print("n=" + NUMOBJ + ", x=" + bestx + ", y=" + besty + ", w=" + bestw + ", h=" + besth); System.out.print(", s=" + bests + ", b=" + bestb + ", e=" + beste); System.out.println(", sc=" + highestscore); } public int theleft(int xx, int yy, int ww, int hh, int ss, int ee, int bb) { return(xx - ww - ee); } public int theright(int xx, int yy, int ww, int hh, int ss, int ee, int bb) { return(xx + ww + ee); } public int thetop(int xx, int yy, int ww, int hh, int ss, int ee, int bb) { if (FACES) { return(yy-ee/2); } else { return(yy - hh); } } public int thebottom(int xx, int yy, int ww, int hh, int ss, int ee, int bb) { return(yy + imax(ee/2, ss+hh)); } public boolean tooclose(int xx, int yy, int ww, int ss, int ee, int hh, int bb) { for (int jj = 0; jj < NUMOBJ; jj++) { // if ( ( (xx+ww+ee>=left[jj]) && (xx-ww-ee<=right[jj]) ) // && ( (yy+imax(ee/2,ss+hh)>=top[jj]) && (yy-ee/2<=bottom[jj]) ) ) if ( (theright(xx,yy,ww,hh,ss,ee,bb)>=left[jj]) && (theleft(xx,yy,ww,hh,ss,ee,bb)<=right[jj]) && (thebottom(xx,yy,ww,hh,ss,ee,bb)>=top[jj]) && (thetop(xx,yy,ww,hh,ss,ee,bb)<=bottom[jj]) ) return(true); } return(false); } public int blobscore(int xx, int yy, int xwidth, int ywidth) { int tmpscore = 0; for (i=-xwidth; i<=xwidth; i++) { for (j=-ywidth; j<=ywidth; j++) { if (theimageval(xx+i,yy+j) == 1) tmpscore++; else // tmpscore--; tmpscore = tmpscore - 2; } } return(tmpscore); } public int trianglescore(int xx, int yy, int ooh, int ww, int hh) { int linewidth; if ( (ww<=0) || (hh<=0) ) return(neginf); int tmpscore = 0; for (i=0; i<=hh; i++) { linewidth = ww * i / hh; for (j=-linewidth; j<=linewidth; j++) { if (theimageval(xx+j,yy+ooh+i) == 1) tmpscore++; else // tmpscore--; tmpscore = tmpscore - 2; } } return(tmpscore); } public int theimageval(int xx, int yy) { if ( (xx<0) || (xx>imagewidth) || (yy<0) || (yy>imageheight) ) return(0); else return(imageval[xx][yy]); } public void dosleep(long nummilisecs) { try { Thread.currentThread().sleep(nummilisecs); } catch (InterruptedException e) { } } // public boolean keyDown(Event evt, int key) { // char keystroke = (char) key; // boolean paused = false; // // if (keystroke == 'p') // paused = !paused; // // repaint(); // return true; // } public void setimage() { int datax[] = {75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 80, 80, 80, 80, 79, 79, 79, 79, 78, 78, 78, 78, 77, 77, 77, 77, 76, 76, 76, 76, 76, 75, 75, 75, 75, 76, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 77, 77, 77, 77, 76, 76, 76, 76, 75, 75, 75, 75, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 94, 93, 93, 93, 92, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 87, 87, 87, 86, 86, 86, 85, 85, 85, 85, 84, 84, 84, 84, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 84, 82, 84, 83, 81, 81}; int datay[] = {61, 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 60, 63, 63, 63, 63, 63, 67, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 70, 70, 70, 70, 70, 70, 69, 69, 69, 69, 69, 70, 70, 70, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 74, 68, 70, 74, 73}; NUMDOTS = datax.length; for (int ii=0; ii0) return(iii); return(-iii); } public int imax (int iii, int jjj) { if (iii>jjj) return(iii); return(jjj); } public int zeroround (int iii) { if (iii==1) return(0); return(iii); } public int indicator (boolean bb) { if (bb) return 1; return 0; } Thread t; public void start() { t = new Thread(this); t.start(); } public void stop() { t.stop(); t = null; } }