aspose file tools*
The moose likes Swing / AWT / SWT and the fly likes Wanted: Java2D example of zooming and panning with scroll bars Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Wanted: Java2D example of zooming and panning with scroll bars" Watch "Wanted: Java2D example of zooming and panning with scroll bars" New topic
Author

Wanted: Java2D example of zooming and panning with scroll bars

Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
I've been google searching for examples of zooming and panning with scroll bars and cannot find one.

Can someone give me an example of at least panning with scroll bars some image or drawing that is too big to fit into the window?

I can probably figure out zooming myself, although an example would be helpful.

Thanks,
Siegfried
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
Craig (or anyone else),
(1) I'm curious why you chose to draw your line segments and circles to an image and then draw the image instead of just doing the affine transfer to g2 and then drawing the line segments and circles. I enhanced your code with a call to draw an ArrayList of rects and it seems to work fine:

private BufferedImage getScaledImage(double scale) {
...
AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
g2.drawRenderedImage(image, at);
g2.setTransform(at);
draw(rects, g2);
g2.dispose();
return bi;
}

Perhaps there is some advantage? Does this have something to do with double buffering that I am defeating by not drawing my rectangles to the image first?

(2) I have also been studying the example at http://java.sun.com/docs/books/tutorial/2d/display/user.html. Here they add an instance of a descendant of class canvas to an applet. Then the write to a instance of BufferedImage. Is this basically the same thing you are doing with a Label?

Thanks,
Siegfried
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
I'm curious why you chose to draw your line segments and circles to an image and then draw the image instead of just doing the affine transfer to g2 and then drawing the line segments and circles.
It was from you question above
Can someone give me an example of at least panning with scroll bars some image...
I made up an image (instead of loading one) for rescaling and didn't consider the drawing part of your requset. I didn't read all of your request.
Doing this with drawing code works as well but there is an extra need for supplying size information to the parent scroll pane (so it can properly set its scrollbars). The JLabel takes care of this in the example above (it knows the size of its ImageIcon child which it uses to set its preferred size).
To do this for graphics–only you can call setPreferredSize or override getPreferredSize:

About setting the graphics transform for the scaling. A side–effect of this approach is that the line widths are scaled as if we were using a magnifying glass. Sometimes this is what you want. If not, you can transform the geom primitives (shown above)(vis–a–vis the graphics context) which will scale them without altering the line widths.
Perhaps there is some advantage?
Not necessarily, it's just another way we can do things.
Does this have something to do with double buffering that I am defeating by not drawing my rectangles to the image first?
No, what you did above is fine.
... Here they add an instance of a descendant of class canvas to an applet. Then the write to a instance of BufferedImage. Is this basically the same thing you are doing with a Label?
Maybe, but not exactly. The demo applet shows good form in using a separate graphic component, viz, the SMCanvas, to draw on as opposed to drawing in the applets paint method. The BufferedImages are used to initialize the two TexturePaint instances which are then used in drawing/filling the movable Rectangle. SMCanvas is a graphic component, ie, it doesn't contain any child components.
In PanAndZoom the BufferedImage is added to a component, the JLabel. That's the main difference. GraphicOnly is a graphic component. BufferedImages are usually shown in an ImageIcon/JLabel or rendered in a components painting method — AWT uses paint for everything, Swing uses several methods (see JComponent paint method).
Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
Wow, it works Great! Thanks. I noticed the lines stay one pixel wide which is what I want. But I'm curious: I don't understand your comment here:

"About setting the graphics transform for the scaling. A side´┐Żeffect of this approach is that the line widths are scaled as if we were using a magnifying glass."


What I would change to make the lines wider as I zoom in?

Thanks,
Siegfried
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
What I would change to make the lines wider as I zoom in?
Transforming the graphics context instead of the geom primitives.
Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
Craig (or anyone else),
I'm trying to enhance your example to display multi-line blocks of text. I want the text to scale with the rest of the drawing.

Font font = new Font("Arial", Font.PLAIN, 10);
g2.setPaint(Color.BLACK);
g2.setFont(font);
FontMetrics fm = g2.getFontMetrics(font);
g2.setTransform(at);
int y = m_y;
for (String s : m_sText) {
Rectangle2D rect = fm.getStringBounds(s, g2);
g2.drawString(s, m_x, y);
y += rect.getHeight();
}

The zoom seems to work fine. But panning right and then back left again (after zooming) does not redraw the text properly.

Now since I don't want the line widths to change when I zoom, I am not (as per Craig's explanation) using g2.setTransform for the shapes. But I could not figure out how to adapt this fragment for use with character strings:

g2.setPaint(Color.blue);
g2.draw(at.createTransformedShape(shapes[0]));

Should I create a descendant of class shape that draws text? How would I do this?

I'm worried that if I don't put all my text strings last, I'll need to undo the effect of "g2.setTransform(at)" (and I don't know how to do that and it does not sound efficient anyway).

Siegfried
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
panning right and then back left again (after zooming) does not redraw the text properly
Setting the graphics context transform can cause trouble.
How would I do this?
Another option is to apply the transform to the font:
g2.setFont(someFont.deriveFont(at));
I'll need to undo the effect of "g2.setTransform(at)" (and I don't know how to do that
AffineTransform orig = g2.getTransform();
g2.setTransform(someTransform);
// render with the transformed graphics context
g2.setTransform(orig); // restore original transform
Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
Thanks Craig!

The g2.setFont(someFont.deriveFont(at)); fixed the problem with panning but created a new mystery.

g2.drawString seems to be using different rules than draw shape. How do I transform the points for drawString?

at.transform(ptsrc, ptdest) does not work correctly when used with drawString.

I tried creating a rectangle at the same x and y I was using for drawString.
I used your suggestion of g2.draw(shape2=at.tranformShape(rect));
Then I used g2.drawString(shape2.getMinX(), shape2.getMinY(), "string") and you would think the rectangle would stay with the text when I zoom and pan but it does not!

How do I compensate for this wierd behavior of drawString?

Thanks,
Siegfried
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
drawString seems to be using different rules than draw shape.
No, not exactly. AffineTransform will work okay for both uses. You may not need to use the same transform on the font as you use for the spatial, viz, translation and scaling, transform.
How do I transform the points for drawString?
There are different ways of doing this. Try to visualize/draw diagrams to understand and be clear about exactly what you want to acomplish with each step in a transform operation. There are many ways to do these things and you have to experiment to find a way that makes sense to you every time you want to use AffineTransform. Do one thing at a time until you can tell what is happening with your transform operation. Then, on to the next. Sometimes you have to start over and try something else.
Here is one way to transform the font using AffineTransform. You may find another approach that suits you better. The paintComponent method looks a little thick but I tried to make each step understandable and easy to follow. You can find more examples of using AffineTransform in your sdk demo folder.
Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
How would I modify the above code so that I can draw axes with tick marks that don't pan or zoom. I'd want to change the labels for the tick marks, of course, as the user pans or zooms.

I want the axis, labels and tick marks to be always visible, regardless of where the user pans or zooms.

Thanks,
Siegfried
Siegfried Heintze
Ranch Hand

Joined: Aug 11, 2000
Posts: 385
I have modified the above example so it will work either as a main program or an applet. This is working.

However, it is incompatible with my attempts to add rulers on the side.

I tried following the example at Sun's scolling tutorial and got stuck with the following code because cp apparently does not have a border because it is not a JPanel. (search for "Help" in the code below).

I'm not sure how to modify the Ruler class to accommodate zooming.

OK, I'm editing this a third time (after having slept on it)!

Craig already helped me create a two layer onion: I have a private JPanel called InnerGraphicsOnly which has all the fancy 2d logic. Then we created a public JApplet called GraphicsOnly (I must think of a more descriptive name) whose init function passes a new instance of InnerGraphicsOnly to the JScrollPane constructor.

After studying sun's example, I think I need a three layer onion because it looks like the component that creates the JScrollPane must itself be a JPanel, not a JApplet. So I guess I need a three layer onion to fully exploit JScrollPane's features where the inner two layers are JPanels and the outer one is a JApplet?

Wow! That is a lot of wrappers! Is there not an easier way? I hope sun had a good reason to require applets using JScrollPanel to be three layer onions. I guess I'll try it out. Oh, but it is such a beautiful day! I see Blue sky! (One does not see that often in western Washington State).

Thanks!
Siegfried



[ September 28, 2008: Message edited by: Siegfried Heintze ]
[ September 28, 2008: Message edited by: Siegfried Heintze ]
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
//Help: This won't compile:
cp.setBorder(BorderFactory.createEmptyBorder(20,20,20,20));

You can try:

I got this to work with an applet containing a JScrollPane with the row/column
headers, a simple graph and a JSlider.
The header labels scale with the graph scaling.
The header data and its placement are simple but this may be enough to get you
started.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Wanted: Java2D example of zooming and panning with scroll bars