Do you remember, when you first started your X and stared at Xeyes? Aaaah, those were the days.

I wanted to have Xeyes in Processing to have it as an example of some lines of code that could be easily integrated into an interactive demo or the like. So I thought, ok, that’s quite easy, just draw a bunch or ellipses and circles. Turns out that it is not *that* easy.

First you draw an eye, then a straight line from the middle of the eye to the current position of the mouse. Then draw a tiny circle at the intersection of the line with the ellipse. But how to find that intersection?

The solution is to equate the formula of the straight line with the formula of the ellipse.

y = mx + c // straight line
x^{2}/a^{2} + y^{2}/b^{2} = 1 // ellipse

Now insert one formula into the other, and solve it for x and y. If you still know how to do that, great! I failed in a few attempts. And felt lazy. Wolfram Alpha to the rescue!

Using the Wolfram Alpha it was easy to get the right solution. See here and here for examples. Now just calculate the values for the intersection coordinates and handle some special cases (e.g. prevent division by zero).

Another example how to use math to solve real world problems ;)

*Updated* 26.02.2011 to the much easier version as proposed by cjameshuff.

/**
* xeyes in Processing
*/
int eyeDist = 60;
int eyeWidth = 70;
int eyeHeight = 100;
void setup() {
size(450, 450);
smooth();
background(120, 120, 120);
}
void draw() {
int eyeX = width / 2 - eyeDist;
int eyeY = height / 2;
translate(eyeX, eyeY);
drawEye(eyeWidth, eyeHeight, mouseX - eyeX, mouseY - eyeY);
translate(2 * eyeDist, 0);
drawEye(eyeWidth, eyeHeight, mouseX-(eyeX+2*eyeDist), mouseY - eyeY);
}
void drawEye(int eyeWidth, int eyeHeight, int mx, int my) {
// draw the eye
fill(220, 220, 220);
strokeWeight(10);
ellipse(0, 0, eyeWidth+35, eyeHeight+35);
float x2 = mx/((float)eyeWidth/eyeHeight);
float t = max(1, sqrt(x2*x2 + my*my)/(eyeHeight/2));
float sx = mx/t, sy = my/t;
// draw the pupil
strokeWeight(1);
fill(0);
ellipse(sx, sy, 20, 20);
}

### Links

### Like this:

Like Loading...

*Related*

An ellipse is just a scaled circle. This allows a much simpler approach of just applying the reverse scale to the coordinates and using Pythagoras to compute the intersection distance with that circle:

void drawEye(int eyeWidth, int eyeHeight, int mx, int my) {

// draw the eye

fill(220, 220, 220);

strokeWeight(10);

ellipse(0, 0, eyeWidth+35, eyeHeight+35);

float x2 = mx/(eyeWidth/eyeHeight);

float t = max(1, sqrt(x2*x2 + my*my)/(eyeHeight/2));

float sx = mx/t, sy = my/t;

// draw the pupil

strokeWeight(1);

fill(0);

ellipse(sx, sy, 20, 20);

}

LikeLike

Wow, that’s a nice solution. Much smaller and less special case handling. I just had to insert a cast to make it run (division by zero).

Thanks a lot!

LikeLike

its very use full

LikeLike

Can you please explain this part:

# float x2 = mx/((float)eyeWidth/eyeHeight);

# float t = max(1, sqrt(x2*x2 + my*my)/(eyeHeight/2));

# float sx = mx/t, sy = my/t;

specifically why you used the “max” and the number one

LikeLike

The eyes and mouse coordinates are scaled so the eyes are circles. sqrt(x2*x2 + my*my) is the distance of the mouse from the center of the eye, and it is divided by the radius of the eye-circles (eyeHeight/2) so it is in units of eye-radii…so the distance is 1 at the edge of the eye.

The pupils can move freely within the eye, but not past the edge of the eye. If the distance is larger than the radius of the eye, we want to divide the mouse coordinates by their distance in eye-radii to get coordinates in the same direction, but on the edge of the eye. If it is less than the radius of the eye, we don’t want to change them, so the pupil tracks the mouse within the eye. Dividing by 1 makes no change in the coordinates, so simply working in eye-radii and using max() to limit the lowest distance to 1 does the job.

We use the original mouse coordinates here, effectively scaling things back so the eye is an ellipse again…the pupil location scales proportionally to everything else, so it is still in the right spot, on a line between the center of the eye and the mouse position.

LikeLike