[Solved] Detecting clicks on custom Buttons/Objects

Hi dudes, i was wondering how to do this. Well i can imagine a way, but i think it’s too “brute”.

Lets explain thigs a little bit better:

Basically i have a program where you have a canvas (passive rendered) and you can add there squares or circles. So you choose the shape and then click on the canvas to draw it where you want. But when you draw the shape, your mose goes back to its normal behaviour (i.e. dont draw anything). But the idea is that when you click an existing shape, you can modify its radius, width and or height.

So basically i was thinking in a big arrayList with all my shapes (with their boundary rectangle) and when the user clicks on the canvas retrieve the coords of the mouse and check


if (mouse.x < shape.x + shape.width && mouse.x > shape.x && mouse.y < shape.y + shape.width && mouse.y > shape.y)
          finallyYouClickedTheF***ingShape(shape);


So you guys that are smarter and have more imagination, should this be OK for rectangles and or circles? Keep in mind that the rectangles can be rotated in any angle (hopefully i dont need to do any trig here :P)

Could i create a rectangle surrounding the mouse and use the intersects function ? Does this function only consider rectangles at 0º ?

Any suggestions on how to implement this?

Thanks in advance

You can use the inverse of the AffineTransform you use for Rectangle you render – to transform your mouse-coordinates. Then check the coords for intersection with the untransformed Rectangle.

So i should straighten up the rectangle (return it to 0º) and then check with that big " if " if the mouse is in the straighten up rectangle? this with all my rectangles in the arrayList PER CLICK?

This is a small image of my biggest doubt with this solution:

http://img19.yfrog.com/img19/5720/doubtsketch.jpg

(just in case the image is not displayed properly, here it is: http://img19.yfrog.com/img19/5720/doubtsketch.jpg)

Anyways thanks for the super fast reply

It’s the other way around:

You have this original rect, and you rotate (transform) it with the AffineTransform, then draw it.

When you receive a mouse-input, you take the inverse of the AffineTransform, and transform your mouse-coords, these are now in the ‘local transform’ of the transformed rect. So when they are in the same transform, you can simply call rect.inside(point) to see whether the transformed mouse-coords are in the transformed-rect

Well i think im starting to understand the point. What i should do is to move not the rectangle or the mouse coords but the whole coordinate system like in this image (or something like that?):

http://img195.yfrog.com/img195/5070/newcoordsystem.jpg

(link to image: http://img195.yfrog.com/img195/5070/newcoordsystem.jpg)

But how to rotate the whole coord system / transform the mouse coords?

Thanks again

PS: sorry if im getting you nervous, im a big noob

AffineTransform trans = new AffineTransform();

// apply your rotations

Rectangle rect = …;

g.setTransform(trans);
g.draw(rect);

Point mouseGlobal = …;
Point mouseLocal = new Point();
trans.inverseTransform(mouseGlobal, mouseLocal);

boolean inside = rect.inside(mouseLocal);

Try this…

http://n4te.com/temp/rotateMouse.gif

Do your mouse detection using the red “Original box” and “Rotated mouse”

that seems logical to me.

Anyways im gonna give a try to what Riven said. When he told me to swap the coords, i was thinking it was going to be a real pain in the a**. God bless the huge libraries Sun created for us, it saves you a lot of mental hassle.

By the way, no one mentioned how to detect which rectangle was clicked. I guess i need to check it for every rectangle on screen (creating a new transform with the current angles for that rectangle and then do the inverse transform for the mouse coords), true?

Just by curiosity, when you add an action listener to your swing component, how does swing detect which button was clicked? Does it check the mouse coords with all the components in the canvas, lets say for example, buttons ?

[quote=“fermixx,post:8,topic:34146”]
I believe Riven and I have said the same thing.

You can (and for the sake of efficiency, probably should) store the AffineTransform instance, rather than recreate it every time your code is executed in a render or mouse event.

Essencially, yes - though obviously AWT’s tree structure reduces the complexity of this search, so it doesn’t necessarily have to search the bounding box of every visible component.

One small suggestion - instead of creating a customized Button, I think you’d instead do better to create a customized Container that supports rotation.

That way you will be able to create rotated instances of any component, not just Buttons.
You would also be able to nest rotations, and do all the other funky things a scenegraph-like structure allows.

Ofcourse, this suggestion is only applicable if you are using the AWT/Swing component system for managing these Buttons you are talking about.

I’ll have stored of course coords, width, height and rotation angle of each shape. Do you suggest that i should create an affine transform and store it in each object?

Or you mean using the same affine transform for all rectangles but updating it with the correct angle for each rect?

Another question, lets say i have two rectangles, A rotated 45º and B rotated 85º.
When the user clicks inside the canvas i’ll do:


trans.rotate(45);  //trans is an already created AffineTransform
//some stuff here
trans.inverseTransform(mouseGlobal, mouseLocal);
boolean inside = rect.inside(mouseLocal);

And then for shape B:


trans.rotate(85);  
//some stuff here
trans.inverseTransform(mouseGlobal, mouseLocal);
boolean inside = rect.inside(mouseLocal);

But im not sure if rotating 85º will be added to the existing 45º in the affineTransform. Should i straighten up the affine transform first ?

version straightening up after check:


trans.rotate(45);  //trans is an already created AffineTransform
//some stuff here
trans.inverseTransform(mouseGlobal, mouseLocal);
boolean inside = rect.inside(mouseLocal);
trans.rotate(-45); //going back to 0 degrees

Is that right?

Well they are just shapes for a map editor. The idea is that you can draw shapes into the map (the canvas) with an specified width, height and angle (at a specific location of course). But if you want to edit something, i thought clicking on the shape to edit it would be the best way to find the exact shape you want to edit. I dont know if i should call them Buttons

It was done by doing this:

the mouseListener goes throught the whole arrayList of shapes and for each one it calls shape.checkClick(point p)
where p are the mouse coordinates.

Well the circle was easy because pithagoras already did the job (distance <= radius^2)

Here’s the code for the rectangles:


public boolean checkClick (Point p){
        AffineTransform aft = new AffineTransform ();
        aft.rotate(Math.toRadians(angle), x, y);
        Point pDst = new Point();

        aft.inverseTransform(p, pDst);
        
        Rectangle rect = new Rectangle (x-width/2,y-height/2,width,height); 
        //i do x-width/2 because the coordinates (x,y) are the center of the shape (not top left corner)

        selected = rect.contains(pDst); //selected is a boolean value that indicates the shape was selected
                                                   //i need it for later usage
     
        return selected;
    } 

This is because the rects are stored without rotation (i guess its not possible store them rotated). They are rotated in render time (just before drawing) so i keep the original x,y, width and height. Thats why i’ve only had to modify the mouse coordinates

Its really accurate, considering that i’ve used integers for everything (no floats for angle or coordinates)

The only problem using integers is that when i rotate it several times (anchoring it from its center) it slightly moves some pixels. But nothing big considering that im cutting decimals without rounding or anything

Thanks for all your replies