Sign in to follow this  
Waterfox

Mouse hit test/know when mouse is over something.

Recommended Posts

Hopefully the answer to this will be fairly simple. I'm using Win32 and Direct2D and I need to know when the mouse is over something so I can move/rotate it only when the mouse is over it and is down. I know how to rotate/move stuff and how to tell when the mouse is down and up, all I need to know is when it's over something, like a simple square or circle. Could someone tell me how that is done?

Share this post


Link to post
Share on other sites
Catch the WM_MOUSEMOVE event. The coordinates of the mouse are accessed via LOWORD(lParam) and HIWORD(lParam) respectively. For the square, you can check if the mouse is over it with 4 'if' statements. For the circle you can use the distance formula;
if((mX-cX)^2 + (mY-cY)^2 <= circleRadius^2)
// Mouse in circle

Share this post


Link to post
Share on other sites
Quote:
Surely it can't be efficient to check if the mouse is over every single one..

Why not? The number of checks you make will just increase linearly with the number of items. It will still be a very quick operation, particularly if you code the if statements correctly.

When checking things like mouseX < boxMinX OR mouseX > boxMaxX OR .., the if statement will "abort" on the first true comparison and the remainder of the checks for that item won't be performed.

If you want to get fancy, you could use some sort of quadtree (or similar) but, unless you have many, many items, simple bounding box checks will be faster.

By the way, for rectangular items you can use PtInRect. Probably won't be any faster but I seriously doubt you'll notice any change in performance.

[Edited by - Buckeye on December 27, 2009 10:35:26 AM]

Share this post


Link to post
Share on other sites
You can divide the screen into sections and record which object is in which. Then you can determine in O(1) which section the mouse is in, and thus cut down on the number of objects that need to be checked.
That's what you'd need to do if you had, say, 10 million objects to check against.

Share this post


Link to post
Share on other sites
One thing I was experimenting with using GDI+ and drawing to a panel control (C#) was how to catch the MouseDown, ~Move, etc. No matter which way I looked, all the suggestions pointed towards using a 'foreach' loop to determine the mouse coordinates. Only problem is that the foreach cannot keep up with fast movement of the mouse.

I published static events in the panel that each GDI+ object (Rectangle, etc.) then subscribed to (if it cared). Then, each of my GDI+ objects were made aware of mouse events. I was even told that this was effectively no different than a 'foreach' loop, but performance differences between the two implementations tells a different story.

I know there may be some differences between Direct2D and GDI+, but this solution shouldn't be affected by those differences - at least I wouldn't think so. I, admittedly, am not experienced with any graphics other than GDI+ (yet).

Share this post


Link to post
Share on other sites
Quote:
Original post by taz0010
Catch the WM_MOUSEMOVE event. The coordinates of the mouse are accessed via LOWORD(lParam) and HIWORD(lParam) respectively. For the square, you can check if the mouse is over it with 4 'if' statements. For the circle you can use the distance formula;
if((mX-cX)^2 + (mY-cY)^2 <= circleRadius^2)
// Mouse in circle


A little OT, but: How well do the WM_MOUSEMOVE, ~DOWN, etc. port to other platforms?

Share this post


Link to post
Share on other sites
Quote:
Original post by ddboarm
A little OT, but: How well do the WM_MOUSEMOVE, ~DOWN, etc. port to other platforms?

Assuming you mean mac, linux etc. It's part of the Win API so not very well unless they're using WINE or something.

Looks like I'll be doing the loop check for each object every time I move the mouse then.
Thanks for the help, I'll reply later to say if it's working good or not.

Share this post


Link to post
Share on other sites
How is this going to work with transforms? My movement is going to be done by changing the positions stuff is to be drawn, so I'll know where my objects are after moving them which is fine for this. But my rotations are going to done with a rotate transformation, so how will I know their locations then?

Also a bit off-topic. Do games have to do the same thing? So like in menu's etc. it has do use this process of checking every single item if the mouse is over it every time the mouse moves? Or is the stuff supplied by OpenGL and Direct3D?

Share this post


Link to post
Share on other sites
Quote:
Original post by szecs
And why would you rotate the items?

Some of the stuff can be rotated by the mouse, so you click on it, move the mouse to rotate it, let go with mouse. If you can think of a better way to rotate than with a rotate transform let me know :P

And I'm going along with the checking, I just thought it might use a lot of CPU usage doing 200+ (4 'if's for 50+ objects) 'if' statements every time WM_MOUSEMOVE is sent which would be perhaps 20 times per second.

Anyway my question still is how can I apply this to object that have changed via a transform since I only know their location before the transform?

Share this post


Link to post
Share on other sites
You only check when the mouse button is pressed not every time the mouse moves.

// some pseudo code

// if the mouse button is clicked
if (mouseButtonDown)
{
// get the mouse position
mousePos = getMousePos();

// Test ALL possible collisions
if (mouseInRect(mySprite->getRect() )
{
// the mouse clicked on mySprite
}
else if (mouseInRect(myMenuItemSprite->getRect() )
{
// the mouse clicked on myMenuItemSprite
}
else if (etc...)
}


and if your using Windows then check into this stuff

POINT mousePos; // Mouse position

GetCursorPos(&mousePos);
ScreenToClient(hWnd, &mousePos);

RECT clickableZone;
SetRect(&clickableZone, 0, 0, 100, 100));

if (PtInRect(&clickableZone, mousePos))
{
// Mouse clicked in this zone
}


and you could do something like this in your sprite class

RECT Sprite::getRect()
{
RECT r1;

r1.left = static_cast<LONG>(position.x);
r1.top = static_cast<LONG>(position.y);
r1.right = static_cast<LONG>(position.x + size.x);
r1.bottom = static_cast<LONG>(position.y + size.y);

return r1;
}

Share this post


Link to post
Share on other sites
Thanks for that, I get a better idea of how I'm going to do this now. It makes sense if I do the check when the mouse is down, and that code will be helpful.

Now I only have one more problem, getting the location of an object after a transform. In GDI/GDI+, was there some way to get the position of something after a transform? Because as far as my program knows, it hasn't moved.

Here's what I mean.
//find the angle to rotate by using the mouse position relative to where the rotation will occur
angle=atan2((mousePos.y-200.0f),(mousePos.x-250.0f))* 57.29f;

//rotate the render target by the angle
RT->SetTransform(D2D1::Matrix3x2F::Rotation(angle, D2D1::Point2F(250.0f, 200.0f)));

//draw the line from 250,200 to 250,50 on the rotated render target
RT->DrawLine(D2D1::Point2F(250, 200),D2D1::Point2F(250, 50),Black, 12.0, Strokestyle);


My program will always see the line as being from 250,200 to 250,50 while I see it rotated to be facing towards to mouse. I know it'll start at 250,200 to 250,50 but after I've rotated it how will I know it's new position?

EDIT: For the actual hit testing I found this function on MSDN, ID2D1Geometry::FillContainsPoint pretty self explanatory.
Should work for the normal hit testing, may also fix my transform problem but if not I still need to find out how. I'll reply later saying if it fixes it or not.

[Edited by - Waterfox on December 28, 2009 5:37:07 AM]

Share this post


Link to post
Share on other sites
There's a very effective way to determine if the mouse is over an object after it has been transformed. You transform the mouse coordinates by the inverse of the transformation matrix that transformed the object. Then you can check the modified mouse coordinates against the original object location using exactly the same methods as before.

Simple example:

The mouse is at (10,5). The object you're checking against is centered at (4,1), but you wish to render it at (10,5) instead. Which will make it appear as though it's under the mouse. The translation matrix must apply an offset of (6,4) to move it into the correct position.
Now the inverse of that matrix is (-6,-4), and if you transform the mouse coords by this, the result is (4,1). Which is where the object is recorded as being!
This property extends to any affine transformation, so you can rotate, stretch, skew, etc, and your program will still detect the mouse hit perfectly.

Since you're concerned about optimisation, it would be ideal to compute and store the inverse transformation matrix for each object when they change. Although inverting a 3x3 matrix is quite a bit faster than the 4x4s you deal with in 3D.

Share this post


Link to post
Share on other sites
In addition to what taz outlines regarding applying the inverse transforms, there are a couple of ways to do this if you need to know, without the user clicking the mouse, what object the mouse is over. It is not too slow to perform a foreach for 50 objects the mouse-move event and catch the reference of the selected object. If you break when finding a mouse-object collision, you'll save some cycles (but again, this should be *negligable* - think about a real-time game, it has way more collisions to detect than fifty objects!)and if you need outrageous efficiency, there's data structures that can sort the objects effectively for searching by coordinate (as mentioned, quad-trees; but you probably don't want to mess around quad-trees if you're struggling with this type of problem)

An alternative is to set up a Timer object, that fires an event every 25-50ms or so. In your MouseMove handler, you update the current mouse position. When the Timer fires, you perform the collision detection on all of your objects. This way the foreach is only executed every time the timer fires, instead of every time the mouse moves. You can even make it so the timer only runs the foreach in cases where the mouse is in the client area, for example - if the position hasn't changed, don't bother running the foreach loop.

Share this post


Link to post
Share on other sites
Quote:
Original post by ddboarm
One thing I was experimenting with using GDI+ and drawing to a panel control (C#) was how to catch the MouseDown, ~Move, etc. No matter which way I looked, all the suggestions pointed towards using a 'foreach' loop to determine the mouse coordinates. Only problem is that the foreach cannot keep up with fast movement of the mouse.

I published static events in the panel that each GDI+ object (Rectangle, etc.) then subscribed to (if it cared). Then, each of my GDI+ objects were made aware of mouse events. I was even told that this was effectively no different than a 'foreach' loop, but performance differences between the two implementations tells a different story.

I know there may be some differences between Direct2D and GDI+, but this solution shouldn't be affected by those differences - at least I wouldn't think so. I, admittedly, am not experienced with any graphics other than GDI+ (yet).


I would be interested to see this profiled; my guess is the bottle-neck was elsewhere; there may have been something specifically in the implementation of the first case that prevented it from performing as efficiently as it should.

Share this post


Link to post
Share on other sites
Hi!

Well, this is something I've asked long long time ago.
The solution is called "image map".

Lets say you have a 800x600px screen resolution.
Now, in the app's memory you'd create an image.
Let's suppose you know you're not gonna have more than 255 images, or at least "clickable items", on your screen.
Now, in the app's memory you'd create a 800x600px bitmap image with color depth of 255 colors.

Now listen carefully;
Create an array such as "Images *m_color_to_images[255]".
Each "clickable item" will be put in a different slot in that array.
Now draw only the "figure" of each of these items, in the color of that item's index, in a "background image" which you are not gonna render. It's internal.

Now when you want to see which item is being pointed to, you'd get the mouse coordinates.
Then you check that pixel's color. As said in my example, it's maximum value would be 255.
Now that you know the color, it maps to your "m_color_to_images", which will give you a pointer to that item.

Example:
You have 5 images.
Set "m_color_to_images" from index of zero to four a pointer to these images, respectively.
On your "background image" draw these five item's figures, in any order you want, while for example, item indexed '3' will have it's figure color set to '3'.
When you get the mouse pointer's coordinates, check the pixel's color.
If the color is '3' then you know that the user is pointing at this image.
Check your "m_color_to_images" array for the appropriate pointer to that actual image/object.

The down-side is that if your images move a lot, then you'll to update this "background image" map; Clear it and redraw as necessary. Much CPU work I think, but works flawlessly, and you never get into any loop when checking where the user points to.

I hope it's clear. If not, read it again and again.
This technique will grant you the ability to have non-rectangle images!

Good luck!

Share this post


Link to post
Share on other sites
Quote:
Original post by mynick123123
Hi! ...
It is clear and called "color picking" (in a way). I was thinking of the same thing, but it seems to use too much memory for a simple GUI. In most cases, you won't have 255 GUI elements, or if you do, they are aligned or something, so touching each other, like a list/listbox/calendar/whatever, so you could simply calculate which item is clicked.

For example (list):
item = starting_item_index + (mouse_y-list_top_y)/item_height; 


So at the end, you will only have say 20 mouse-inside-area tests, so not much.

Checking it will take say 5 time, rendering say 1000. (=1005)
Optimized the picking 1 time, rendering say 1000 (=1001) (not much)

Color picking could be used in windowed GUI, when you have lot of GUI windows.
So r can be the window ID, g can be item ID, b can be attributes for the item.
This way you can render the windows in proper order on top of each other or anything.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this