0

I have a graphics application in JAVA, which is made up of many different shapes (lines, circles, arcs, etc, which are drawn via the Graphics.drawLine(), drawArc()... methods). I would like to create mouse-over events on many, if not all of the drawn objects.

What I was thinking was to store some sort of bitmap with metadata in it, and use that to figure out which object the mouse is over. Is there a way to do this in Java? (looping through all the objects per mouse move doesn't seem viable).

Thanks,

John

John
  • 3,400
  • 3
  • 31
  • 47
  • You can use java shapes contains http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/Shape.html#contains%28double,%20double%29 – Makky Jul 15 '13 at 15:43
  • 1
    I would try key colors and off-screen surface (like BufferedImage). Draw your objects at off-screen image buffer: Each object with it's own color. Then read image buffer (example here http://stackoverflow.com/questions/6524196/java-get-pixel-array-from-image ) and determine your object index by color at mouse position. – DRCB Jul 15 '13 at 15:56
  • Thanks for the suggestions. Drawing twice -- once to an off screen buffer, and once to an on-screen buffer seems to make the most sense. I can reuse the drawing code for that, and then create a hash function to convert color to object. – John Jul 15 '13 at 17:10
  • @John, I have moved my comment to a separate answer. – DRCB Jul 16 '13 at 14:34

2 Answers2

1

It depends on your specifications. You do not mention if those shapes are allowed to overlap, to move, how many of them can exist etc.

Solution a) The easiest approach that comes to mind is to implement each shape as a JComponent descedant (e.g. JPanel). So you would have a CirclePanel, an ArcPanel etc that extend JPanel and each one of them paints itself in the same way it is being done now.

Having the shapes as a JComponent allows you to add a MouseListener to each panel that would then handle the mouseEntered(), mouseExited() events.

Solution b) If on the other hand you need to draw all the shapes on a single component's area (as I understand is the case now) then you still do not need to iterate over all the shapes. You just need to introduce an algorithm to categorize the shapes based on their position, to be able to exclude them fast inside your "isMouseOver(Shape s)" test procedure.

For example lets say you divide the area to 2 equal sub-areas left and right (let's call them tiles). When you create each shape you test which tile they intersect to, and you store this information both in the shape and in the corresponding tile.

Now when you need to test if the mouse is over a shape, you decide which tile the mouse is over. This way you only have to check shapes that intersect either the left or the right tile. Assuming that your shapes are distributed uniformly on the screen, you have just rejected 50% of the shapes with one test.

Depending on how many shapes you have, you could use 4 or 8 tiles, or you could even create/delete tiles dynamically (e.g. based on how many objects tend to gather in one area of the screen or not).

I would suggest to try the first solution because it is easier and a cleaner approach. If you decide that it does not fit your needs, you could then go for an approach similar to the second one.

c.s.
  • 4,786
  • 18
  • 32
  • Thanks for the suggestions. The shapes are indeed allowed to overlap, they may move, and I will have potentially hundreds of them. Solution b) does not work simply because it creates a lot of overhead to the program itself, which would make it hard to maintain. Solution a) essentially loops through all of the shapes as well, so it would not work. I think I will go with DRCB's suggestion, and draw to an off-screen buffer, and then do a color hash to get the object. – John Jul 15 '13 at 17:09
  • Perhaps I did not correctly understand your problem. Solution a) loops all the objects for drawing only. You would still have to loop at least once to draw them all right? The only additional thing it does, is to add mouse over capabilities. It is rather easy to implement and check the performance. At least this is what I would try first. – c.s. Jul 15 '13 at 19:42
  • It might amount to the same thing, in that the MouseEntered and mouseExited events may use a raster in the background. As far as performance goes, during animation, I only need to update the 'mouse over raster' every five or so frames, which significantly reduces the overhead. Plus, with the raster solution, I can just call all of my current drawing methods with a different Graphics object, and just adjust the output colors (some of these drawing methods are somewhat involved, so this would significantly reduce coding and maintenance). – John Jul 16 '13 at 13:30
1

Key-color solution

(moved from comment)

  • Create an off-screen graphics buffer (like BufferedImage), same size as subject image.
  • Draw all objects into this buffer. Each object with one own color. Depending on object count you can optimize image buffer: For example use 8-bit graphics.
  • Read resulting image buffer by pixel (example Java - get pixel array from image). Determine pixel color at current mouse position, and map color index (or RGB value) to the source object.

Pros:

  • The solution is "pixel-accurate": Object boundaries are exact - pixel to pixel.
  • Easy to solve overlapping objects problem. Just draw them at the desired order.
  • Object complexity is not limited. Theoretically bitmaps are also possible.

Cons:

  • To move one object, the complete off-screen buffer must be repainted
  • Number of objects can be limited when using low-bit image buffer
Community
  • 1
  • 1
DRCB
  • 2,111
  • 13
  • 21