/** * Face.java - implements the Chernoff faces generation routines * * Based on Faces.c, by Dave Johnson, who got it from Clifford * Pickover's Chernoff face routine, in the book "Computers, * Pattern, Chaos, and Beauty." Inspired by Brad Mohr's Faces * plug-in for AfterDark screen saver for Macintosh. * * The params are as follows, and are stored in a ten byte array * with values ranging from -127 to 127: * * 0 head eccentricity * 1 eye eccentricity * 2 pupil size * 3 eyebrow slope * 4 nose size * 5 mouth vert. offset * 6 eye spacing * 7 eye size * 8 mouth width * 9 mouth "openness" */ import java.lang.*; import java.awt.*; public class Face extends Panel { public void CenterRectOverPoint( Rectangle theRect, Point thePt ) { int t1, t2 = 0; // first home theRect theRect.move(0,0); // center it over thePt t1 = theRect.x + (thePt.x - (theRect.width / 2)); t2 = theRect.y + (thePt.y - (theRect.height / 2)); theRect.move(t1,t2); } public Point GetCenter(Rectangle theRect) { Point pt = new Point(0,0); pt.x = theRect.x + (theRect.width / 2); pt.y = theRect.y + (theRect.height / 2); return(pt); } public int ScaleChar( int param, int max ) { return (param * max) / 127; } public void InsetRect( Rectangle theRect, int dh, int dv) { Point curCtr = GetCenter(theRect); theRect.x += dh; theRect.width -= dh; theRect.y += dv; theRect.height -= dv; if((theRect.width < 1) || (theRect.height < 1)) { SetRect(theRect, 0, 0, 0, 0); } CenterRectOverPoint(theRect, curCtr); } public void SetRect( Rectangle theRect, int left, int top, int right, int bottom ) { if( right < 0 ) right = -right; if( bottom < 0 ) bottom = -bottom; theRect.reshape( left, top, right, bottom ); } public void OffsetRect( Rectangle theRect, int dh, int dv ) { theRect.x += dh; theRect.y += dv; } public void FrameOval( Rectangle theRect, Graphics g ) { g.drawOval( theRect.x, theRect.y, theRect.width, theRect.height ); } public void PaintOval( Rectangle theRect, Graphics g ) { g.fillOval( theRect.x, theRect.y, theRect.width, theRect.height ); } // Set up public void DrawFace( Rectangle bounds, int[] params, Graphics g ) { Rectangle rect = new Rectangle(); Rectangle tRect = new Rectangle(); Rectangle eyeRect = new Rectangle(); Rectangle mRect = new Rectangle(); int i, x1, x2, y1, y2, t1, t2 = 0; int xunit, yunit, temp = 0; Point ctr = new Point(0,0); Point eyectr = new Point(0,0); Point teyectr = new Point(0,0); // These are our basic units in x and y directions xunit = ((bounds.width) / 8); yunit = ((bounds.height) / 8); // the center of the face ctr = GetCenter(bounds); // // draw the head // // inset by one unit so there is room to squish around InsetRect(bounds, xunit, yunit); // now add eccentricity (param[0], positive is taller. Get from HTML?) t1 = ScaleChar( params[0], (xunit / 2) ); t2 = ScaleChar( -params[0], (yunit / 2) ); InsetRect(bounds, t1, t2); // clear the oval to the background color Color tempColor = g.getColor(); g.setColor(Color.black); PaintOval(bounds, g); g.setColor(tempColor); // draw the head oval FrameOval(bounds, g); // // draw the eyes // // set up the default eye rect t1 = ((5 * xunit) / 4); t2 = ((5 * yunit) / 4); SetRect(eyeRect, 0, 0, t1, t2); // change the size (params[7], positive is bigger.) t1 = ScaleChar(-params[7], xunit / 4); t2 = ScaleChar(-params[7], yunit / 4); InsetRect(eyeRect, t1, t2); // Add eccentricity (params[1], positive is taller) t1 = ScaleChar(params[1], xunit / 4); t2 = ScaleChar(-params[1], yunit / 4); InsetRect(eyeRect, t1, t2); // Set location (params[6] is eye spacing, positive is wider), and draw // the eyes. Also draw the pupils, while we're here. temp = ScaleChar(params[6], (xunit/2)); CenterRectOverPoint(eyeRect, ctr); // Save the current settings for the other eye - must new up another object tRect = new Rectangle(eyeRect.x, eyeRect.y, eyeRect.width, eyeRect.height); t1 = -(xunit + temp); t2 = -((2 * yunit) / 3); OffsetRect(eyeRect, t1, t2); FrameOval(eyeRect, g); // Draw the pupil, params[2] is diameter, bigger is bigger. We want at // least one pixel dot: no empty eyes. // Convert params[2] to a positive number from 0 to 127 x1 = (params[2] + 127 ) / 2; // scale in x and y directions, minimum of 2 y1 = ScaleChar(x1, yunit / 2); if(y1 < 2) { y1 = 2; } x1 = ScaleChar(x1, xunit / 2); if(x1 < 2) { x1 = 2; } // Get the center of the eye rect eyectr = GetCenter(eyeRect); // Set up the rect and draw it SetRect(eyeRect, 0, 0, 0, 0); InsetRect(eyeRect, -x1, -y1); CenterRectOverPoint(eyeRect, eyectr); PaintOval(eyeRect, g); // Other eye... t1 = xunit + temp; t2 = (- ((2 * yunit) / 3)); OffsetRect(tRect, t1, t2); FrameOval(tRect, g); // Pupil... teyectr = GetCenter(tRect); // set up the rect and draw it SetRect(tRect, 0, 0, 0, 0); InsetRect(tRect, -x1, -y1); CenterRectOverPoint(tRect, teyectr); PaintOval(tRect, g); // // Eyebrows // // params[3] is slope, positive slopes down to the outside temp = ScaleChar(params[3], yunit / 2); y1 = y2 = ctr.y - (yunit + (2 * yunit) / 3); y1 += temp; y2 -= temp; g.drawLine((ctr.x - (3 * xunit) / 2), y1, (ctr.x - xunit / 2), y2); g.drawLine((ctr.x + (3 * xunit) / 2), y1, (ctr.x + xunit / 2), y2); // Nose // params[4] is length, positive is bigger y1 = yunit + (yunit / 3) + ScaleChar(params[4], yunit / 3); g.drawLine(ctr.x, (ctr.y - (yunit / 3)), (ctr.x + (xunit / 2)), (ctr.y + y1)); g.drawLine(ctr.x + (xunit / 2), (ctr.y + y1), ctr.x - (xunit / 2), (ctr.y + y1)); // // Mouth // // params[8] is width, positive is wider temp = ScaleChar(params[8], (xunit / 2)); y1 = ctr.y + (2 * yunit); // the neutral position, vertically x1 = xunit + temp; // the horizontal offset from center // Get the points for the lips temp = ScaleChar(params[5], (yunit / 3)); // offset for first lip // For lip 2, scale it y2 = ScaleChar(params[9], (yunit / 3)); // offset for the second lip y2 += temp; // Draw the first lip if(temp > 0) { // if positive, use the bottom (smile) mRect.x = (ctr.x - x1); mRect.y = (y1 - temp); mRect.width = (mRect.x - (ctr.x + x1)); mRect.height = (mRect.y - (y1 + temp)); g.drawArc( mRect.x, mRect.y, Math.abs(mRect.width), Math.abs(mRect.height), 0, 180 ); } else if(temp < 0) { // if negative, use the top (frown) mRect.x = (ctr.x - x1); mRect.y = (y1 + temp); mRect.width = (mRect.x - (ctr.x + x1)); mRect.height = (mRect.y - (y1 - temp)); g.drawArc( mRect.x, mRect.y, Math.abs(mRect.width), Math.abs(mRect.height), -180, 180 ); } else { // if neutral, just draw a line // subtract the pen width on the right g.drawLine((ctr.x - x1), y1, (ctr.x + x1 - (xunit / 10)), y1); } // Now the second lip temp = y2; if(temp > 0) { // if positive, use the bottom (smile) mRect.x = (ctr.x - x1); mRect.y = (y1 - temp); mRect.width = (mRect.x - (ctr.x + x1)); mRect.height = (mRect.y - (y1 + temp)); g.drawArc( mRect.x, mRect.y, Math.abs(mRect.width), Math.abs(mRect.height), 0, 180 ); } else if(temp < 0) { // if negative, use the top (frown) mRect.x = (ctr.x - x1); mRect.y = (y1 + temp); mRect.width = (mRect.x - (ctr.x + x1)); mRect.height = (mRect.y - (y1 - temp)); g.drawArc( mRect.x, mRect.y, Math.abs(mRect.width), Math.abs(mRect.height), -180, 180 ); } else { // if neutral, just draw a line g.drawLine((ctr.x - x1), y1, (ctr.x + x1 - (xunit / 10)), y1); } } }