views:

1580

answers:

5

Hello. I have a program in C# (Windows Forms) which draws some rectangles on a picturebox. They can be drawn at an angle too (rotated).

I know each of the rectangles' start point (upper-left corner), their size(width+height) and their angle. Because of the rotation, the start point is not necessarely the upper-left corner, but that does not matter here. Then when I click the picturebox, I need to check in which rectangle (if any) I have clicked.

So I need some way of checking if a point is in a rectangle, but I also need to take into account the rotation of each rectangle. Does anybody know of a way to do this in C#?

+2  A: 

You could keep a second, undisplayed image where you draw duplicates of the rectangles, each uniquely colored. When the user clicks on the picturebox, find the color of the corresponding pixel in the 2nd image, which will identify which rectangle was clicked.

outis
That would be a waste of memory and it would be slow.
Ove
Outis' solution does work. i am using it in CF project right now to do some particularly complex hit testing. But also check out: http://msdn.microsoft.com/en-us/library/system.drawing.rectangle.contains(VS.80).aspxThe Rectangle class has a Contains method that can be used to test point containment.
Paul Sasik
+1 for non-math solution
JeffH
not a perfect solution, as it wouldnt scale too well, but for your average sized image, this wouldnt be too bad. If you can keep the bit depth of this "buffer" low, it wouldnt take too much memory at all.
Neil N
@psasik - post yours as an answer. It's the most straightforward assuming the OP is using .NET and generic C#.
JeffH
It would use a chunk of memory, but needn't to be that much. There probably would be less than 256 rectangles; an 800x600 grayscale image would take up 468k, which is well within a modern computer's capacity and memory usage for programs. And how would it be slow? It would be O(1) to look up a pixel, O(1) or O(log(n)) to map the pixel to a rectangle, depending on how you stored the list of rectangles. Faster than checking each rectangle. It's a standard time-space tradeoff.
outis
Yes, this is a good solution, but it's not the one I'm taking. I'll be going with Neil N's. Thank you :)
Ove
Outis I like your solution, the only thing I'm not sure of is how you would handle overlapping rectangles? This is where the bit depth of the memory block comes into play, and could get hairy. But depending on the size of the image, the total number of rectangles expected, this would likely be easier to implement. And less code = better code almost always.
Neil N
@Neil: It's not (yet) defined in my suggestion. If the OP wants all rectangles under a point, my suggestion isn't workable. If the OP wants just one, that will define the order that rectangles are drawn.
outis
Your suggestion IS workable for all rects under a point, you just have to use your buffer accordingly. Say he has 8 rects. Keep a byte for each pixel, and assign a bit to each rect.
Neil N
Ah, interpret pixel colors as bit-vectors, where a rectangle's index in a bit-vector is the rectangle's z-index.
outis
+2  A: 

There's a general (language-neutral) discussion of your issue here

JeffH
Yes, but I referenced C# because I thought maybe it knows of an easier way to solve this problem.
Ove
+1  A: 

Is it possible to apply the same rotation applied to the rectangle to the point in reverse?

For example, Rectangle A is rotated 45 degrees clockwise from its origin (upper left corner), you would then just rotate point B around the same origin 45 degrees COUNTER clockwise, then check to see if it falls within Rectangle A pre-rotation

Neil N
This is certainly the way to go, but I can't seem to get my math right this moment. Thank you.
Ove
I think the ideal solution depends on the framework you have set up in your application. In some cases, I think Outis's idea of a z-buffer is more ideal. Keep in mind his solution is the same way GPU's render objcts in 3D.
Neil N
To be fair, you brought the "z-" to the buffering idea.
outis
A: 

Would the rectangles be allowed to overlap? If so, would you want all the rectangles in a point, or just the one in the top layer?

kurast
A: 

I know this was already answered but I had to do something similar a while ago. I created an extension method for the System.Windows.Point class that helped do exactly what Neil suggested:

    public static double GetAngle(this Point pt)
    {
        return Math.Atan2(pt.X, -pt.Y) * 180 / Math.PI;
    }

    public static Point SetAngle(this Point pt, double angle)
    {
        var rads = angle * (Math.PI / 180);
        var dist = Math.Sqrt(pt.X * pt.X + pt.Y * pt.Y);
        pt.X = Math.Sin(rads) * dist;
        pt.Y = -(Math.Cos(rads) * dist);
        return pt;
    }

This would allow me to work with the angles of points around 0, 0. So if you know the center of the rect that you are testing you would offset the point by the negative of this value (for example: pt.X -= 32; pt.Y -= 32) And then you would apply the negative rotation of the rectangle (as suggested by Neil: pt.SetAngle(-45);)...

Now if the point is within 64, 64 you know you hit the rectangle. More specifically I was checking a pixel of a rotated image to make sure I hit a pixel of a specific color.

DataDink