/* * * * Faces.c, by Dave Johnson, 3/91. * * Based on Clifford Pickover's Chernoff face routine, in his book "Computers, Pattern, Chaos, * and Beauty." * * * The face is specified by an array of ten byte values ranging from -127 to 127. Here's how * the parameters map to facial features (there's no rhyme or reason here, I just used the * same set that Pickover did): * * 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" * * * */ /* prototypes for the Face routines */ void CenterRectOverPoint(Rect*, Point); Point GetCenter(Rect *theRect); int ScaleChar(char param, int max); pascal void Face(Rect *rect, char params[]); /*------------------------------------------------------------------------- CenterRectOverPoint() Centers theRect over thePt... --------------------------------------------------------------------------*/ void CenterRectOverPoint(Rect *theRect, Point thePt) { /* First home theRect... */ OffsetRect(theRect, -theRect->left, -theRect->top); /* ...then center it over thePt */ OffsetRect(theRect, thePt.h - (theRect->right / 2), thePt.v - (theRect->bottom / 2)); } /*------------------------------------------------------------------------- GetCenter() Returns the center of theRect --------------------------------------------------------------------------*/ Point GetCenter(Rect *theRect) { Point pt; pt.h = theRect->left + ((theRect->right - theRect->left) / 2); pt.v = theRect->top + ((theRect->bottom - theRect->top) / 2); return(pt); } /*------------------------------------------------------------------------- ScaleChar() Scales param to a range of + or - max, returns the scaled value. param is assumed to be between -127 and 127. --------------------------------------------------------------------------*/ int ScaleChar(char param, int max) { return ((long)param * max) / 127; } /*------------------------------------------------------------------------- Face() Draws a Chernoff face, scaled to bounds, in the current port --------------------------------------------------------------------------*/ pascal void Face(Rect *bounds, char params[]) { Rect rect, temprect; int x1, x2, y1, y2, xunit, yunit, temp; Point ctr, eyectr; PenState oldpen; /* * Set up */ /* These are our basic units in x and y directions */ xunit = (bounds->right - bounds->left) / 8; yunit = (bounds->bottom - bounds->top) / 8; /* The center of the face */ ctr = GetCenter(bounds); /* Adjust the pen size */ GetPenState(&oldpen); PenNormal(); x1 = xunit / 10; if(x1 < 1) x1 = 1; y1 = yunit / 10; if(y1 < 1) y1 = 1; PenSize(x1, y1); EraseRect(bounds); /* * Draw the Head */ /* Inset by one unit so there is room to squish around */ rect = *bounds; InsetRect(&rect, xunit, yunit); /* Now add eccentricity (params[0], positive is taller) , and draw it */ InsetRect(&rect, ScaleChar(params[0], xunit / 2), ScaleChar(-params[0], yunit / 2)); FrameOval(&rect); /* * Draw the Eyes */ /* Set up the default eye rect */ SetRect(&rect, 0, 0, (5 * xunit) / 4, (5 * yunit) / 4); /* Change the size (param[7], positive is bigger) */ InsetRect(&rect, ScaleChar(-params[7], xunit / 4), ScaleChar(-params[7], yunit / 4)); /* Add eccentricity (param[1], positive is taller) */ InsetRect(&rect, ScaleChar(params[1], xunit / 4), ScaleChar(-params[1], yunit / 4)); /* 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(&rect, ctr); temprect = rect; /* save for other eye */ OffsetRect(&rect, -(xunit + temp), -((2 * yunit) / 3)); FrameOval(&rect); /* Draw the pupil, params[2] is diameter, bigger is bigger. We want a minimum of a 1 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, min of 1 */ y1 = ScaleChar(x1, yunit / 3); if(y1 < 1) y1 = 1; x1 = ScaleChar(x1, xunit / 3); if(x1 < 1) x1 = 1; /* Get the center of the eye rect */ eyectr = GetCenter(&rect); /* Set up the rect and draw it */ SetRect(&rect, 0, 0, 0, 0); InsetRect(&rect, -x1, -y1); CenterRectOverPoint(&rect, eyectr); PaintOval(&rect); /* Other eye... */ rect = temprect; OffsetRect(&rect, xunit + temp, -((2 * yunit) / 3)); FrameOval(&rect); /* Pupil... */ eyectr = GetCenter(&rect); SetRect(&rect, 0, 0, 0, 0); InsetRect(&rect, -x1, -y1); CenterRectOverPoint(&rect, eyectr); PaintOval(&rect); /* * Eyebrows */ /* params[3] is slope, positive slopes down to the outside */ temp = ScaleChar(params[3], yunit / 2); y1 = y2 = ctr.v - (yunit + (2 * yunit) / 3); y1 += temp; y2 -= temp; MoveTo(ctr.h - (3 * xunit) / 2, y1); LineTo(ctr.h - xunit / 2, y2); MoveTo(ctr.h + (3 * xunit) / 2, y1); LineTo(ctr.h + xunit / 2, y2); /* * Nose */ /* params[4] is length, positive is bigger */ y1 = yunit + (yunit / 3) + ScaleChar(params[4], yunit / 3); MoveTo(ctr.h, ctr.v - (yunit / 3)); Line(xunit / 2, y1); Line(-xunit, 0); /* * Mouth */ /* params[8] is width, positive is wider */ temp = ScaleChar(params[8], xunit / 2); y1 = ctr.v + (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 (relative to first lip) */ y2 += temp; /* Draw the first lip */ if(temp > 0) /* if positive, use the bottom (smile) */ { SetRect(&rect, ctr.h - x1, y1 - temp, ctr.h + x1, y1 + temp); FrameArc(&rect, 90, 180); } else if(temp < 0) /* if negative, use the top (frown) */ { SetRect(&rect, ctr.h - x1, y1 + temp, ctr.h + x1, y1 - temp); FrameArc(&rect, -90, 180); } else /* if neutral, just draw a line */ { MoveTo(ctr.h - x1, y1); LineTo(ctr.h + x1 - (xunit / 10), y1); /* subtract the pen width on the right */ } /* Now the second lip */ temp = y2; if(temp > 0) /* if positive, use the bottom (smile) */ { SetRect(&rect, ctr.h - x1, y1 - temp, ctr.h + x1, y1 + temp); FrameArc(&rect, 90, 180); } else if(temp < 0) /* if negative, use the top (frown) */ { SetRect(&rect, ctr.h - x1, y1 + temp, ctr.h + x1, y1 - temp); FrameArc(&rect, -90, 180); } else /* if neutral, just draw a line */ { MoveTo(ctr.h - x1, y1); LineTo(ctr.h + x1 - (xunit / 10), y1); /* subtract the pen width on the right */ } /* All Done! restore the pen and return */ SetPenState(&oldpen); }