Checking for mouse over object
Before describing the functions for the overcheck method, I will preview why it is needed. HTML5 and JavaScript provide ways to handle (listen for and respond to) mouse events on the canvas and supply the coordinates of where the event took place. However, our code must do the work of determining what object was involved. Remember: there are no objects actually on the canvas, just the remains, think of it as paint, of whatever drawing was done. My code accomplishes this task by looping through the stuff array and invoking the overcheck method for each object. As soon as there is a hit (and I will explain the order in which this is done later), my code proceeds with that object as the one selected. The functions in which this checking occurs are startdragging and makenewitem and will be explained in the next section.
There are three functions to explain for the overcheck method since Picture and Rect refer to the same function. Each function takes two parameters. Think of the mx, my as the location of the mouse. The overrect function checks for four conditions each being true. In English, the question is: Is mx greater than or equal to this.x AND is mx less than or equal to this.x + this.w AND is my greater than or equal to this.y AND is my less than or equal to this.y + this.h? The function says this more compactly:
function overrect (mx,my) {
if ( (mx>=this.x)&&(mx<=(this.x+this.w))&&(my>=this.y)&&(my<=(this.y+this.h))) {return true;}
else {return false;}
}
The function defining the overcheck method for ovals is overoval. The overoval function performs the operation of checking if something is within a circle, but in a translated and scaled coordinate system. The check for a point being within a circle could be done by setting the center of the circle to x1,y1 and the point to x2,y2 and see if the distance between the two is less than the radius. I use a variation of this to save time and compare the square of the distance to the radius squared. I define a function distsq that returns the square of the distance. But now I need to figure out how to do this in a translated and scaled coordinate system. The answer is to set x1,y1 to 0,0. This is the location of the center of the oval in the translated coordinate system. Then my code sets x2 and y2 as indicated in the code to what would be the scaled values.
function overoval(mx,my) {
This did not come to me instantly. I worked it out trying values for mx and my located in different positions relative to the oval center. The code does represent what the transformations do in terms of the translation and then the scaling.
The overheart function consists of several distinct if statements. This is a case of not trying for a simple expression but thinking about various situations. The function starts off by setting variables to be used later. The first check made by the function is to determine if the mx,my point is outside the rectangle that is the bounding rectangle for the heart. I wrote the outside function to return true if the position specified by the last two parameters was outside the rectangle indicated by the first four parameters. The qx,qy point is the upper left corner. qwidth is the width at the widest point and qheight is the total height. I thought of this as a quick check that would return false most of the time. The next two if statements determine if the mx,my point is contained in either circle. That is, I again use the comparison of the square of the distance from mx,my to the center of each arc to the stored radsq attribute. At this point in the function, that is, if the mx,my position was not close enough to the center of either circle and if my is above (less than) this.y, then the code returns false. Lastly, the code puts the mx value in the equation for each of the sloping lines and compares the result to my. The equation for a line can be written using the slope m and a point on the line x2,y2:
y = m * (x – x2) + y2
The code sets m and x2, y2 for the line on the left and then modifies it to work for the line on the right by changing the sign of m. One possible concern here is whether or not the fact that the screen coordinate system has upside down vertical values (vertical values increase going down the screen) causes a problem. I checked out cases and the code works.
var rightctrx = this.x+this.drx;
var qx = this.x-2*this.drx;
var qy = this.y-this.drx;
var qwidth = 4*this.drx;
var qheight = this.drx+this.h;
//quick test if it is in bounding rectangle if (outside(qx,qy,qwidth,qheight,mx,my)) { return false;}
//compare to two centers
if (distsq(mx,my,leftctrx,this.y)<this.radsq) return true;
if (distsq(mx,my,rightctrx,this.y)<this.radsq) return true;
// if outside of circles AND below (higher in the screen) than this.y, return false if (my<this.y) return false;
// compare to each slope var x2 = this.x
The reasoning underlying the outside function is similar to the overrect function. You need to write code comparing the mx,my value to the sides of the rectangle. However, for outside I chose to use the OR operator, ||, and to return its value. This will be true if any of the factors are true and false otherwise.
function outside(x,y,w,h,mx,my) {
return ((mx<x) || (mx > (x+w)) || (my < y) || (my > (y+h)));
}
Actually, what I said was true, but misses what could be an important consideration if performance is an issue. The || evaluates each of the conditions starting from the first (leftmost) one. As soon as one of them is true, it stops evaluating and returns true. The && operator does a similar thing. As soon as one of the conditions is false, it returns false.
This is the basis for the four types of objects I designed for manipulation on the canvas. You can look ahead to examine all the code or continue to see how these objects are put in use in the responses to mouse events.
■ Note This example does not demonstrate the full power of object-oriented programming. In a language such