Wednesday, January 21, 2009

Scalable JFreeChart Applet

I created a new applet that allows one to scale a JFreeChart using the mouse scroll wheel. I use the term “scale” rather than “zoom” because JFreeChart already has a well-defined interface for zooming, and it is not the same behavior as my scaling. In the applet below, scrolling up will “zoom in” or magnify the view at the cursor position. Scrolling down “zooms out” centered at the cursor. The behavior is similar to the Mac OS X Universal Access Zoom behavior, which when enabled, allows the user to zoom in and out at the cursor position while holding the Control key and scrolling with the mouse wheel. To quickly get back to the original scaling, hold down the Control and Shift keys and scroll down.

Note for Windows users: if the scaling does not work or quits working, right-click to bring-up a popup menu, then click anywhere on the applet to dismiss the popup. I discussed this bug in a previous post.

The Code
The scaling is an extension of the generic ZoomUI by Piet Blok, which is built on JXLayer. Specifically, I extend two classes. My WheelZoomableUI class extends ZoomUI, and WheelZoomablePort extends ZoomPort. The WheelZoomableUI class contains only one method that overrides transformMouseWheelEvent(...). That method calls the appropriate scaling algorithms in the corresponding WheelZoomablePort class.

The scaling is performed by applying appropriate scaling and translation transformations to the AffineTransform field in the ZoomPort class. When zooming in, the operation is quite simple. First, the AffineTransform.scale(...) transformation applies the scaling. Then, the cursor position in the view is converted to the “unscaled” coordinate system. This position is then used to translate the view so that the “zoomed-in” point is centered at the original mouse cursor position. This simply means that the content under the cursor remains in the same position while scaling.

Zooming out requires one additional step. After the above scaling and translation have been applied, it is possible that part of the chart has fallen off the edge of the view. Another way of describing this is that the user zoomed-out so far that the chart has “slid over” toward the cursor. This results in a blank area in the applet window. In order to avoid this blank area, the chart needs to be “slid back” to cover the blank area. So a corrective translation is applied to ensure that no part of the view is empty.

The minimum scaling allowed is 1.0, meaning that one cannot make the chart smaller than the applet panel size. So no blank areas are possible in the applet. If you observe my magnifier applet, it is possible to zoom out to a scaling factor less than 1.0. This creates a blue area within the magnifying glass that shows the area outside the chart. This blue area corresponds to the empty area described above that necessitates the corrective translation when zooming out.

Constructing the applet is quite simple, as seen here:
public JXLayerScaleDemo() {
  super();
  XYDataset dataset = createDataset();
  JFreeChart chart = createChart(dataset);
  chartPanel = new ChartPanel(chart);
  final WheelZoomablePort zoomPort = new WheelZoomablePort();
  final WheelZoomableUI zoomUI = new WheelZoomableUI();
  zoomPort.setView(chartPanel);
  zoomPort.setOpaque(true);
  final JXLayer<ZoomPort> layer = new JXLayer<ZoomPort>(zoomPort, zoomUI);
  chartPanel.setPreferredSize(new java.awt.Dimension(480, 260));
  setContentPane(layer);
}

Instances of the WheelZoomablePort and WheelZoomableUI are created from the default constructors. The view of the port is set to the chart panel. Then the port and UI are sent to the JXLayer constructor. The JXLayer is then set as the main content.

Future Work
As mentioned above, the wheel zoomer behaves similarly to the Mac OS X Universal Access Zoom, but with one important omission. When zoomed-in, the user cannot pan and explore the hidden parts of the panel. Luckily, JXLayer already has a nice UI to handle panning and scrolling called MouseScrollableUI. I hope to add this functionality in the future.

From a coding perspective, it is not really necessary to subclass ZoomUI and ZoomPort. It may be better to simply add a MouseWheelListener to the ZoomPort and override the mouseWheelMoved method. In fact, the test classes in the ZoomUI and the new TransformUI packages do just this to achieve similar scaling behavior without subclassing.

Resources
To build the applet, you will need these:
  1. My code
  2. JXLayer library (I use version 3.0)
  3. JFreeChart library (I use version 1.0.12)
  4. JFree JCommon library (I use version 1.0.15)
The first link above is a modified subset of Piet Blok's generic ZoomUI released on January 11th, 2009.


11 comments:

  1. Chart in the above applet appears very clear when zoomed in by wheel. Like work "Engineering" is very clear even in large size. But the same applet code run locally display blur chart when zoomed in. What could be the reason?

    ReplyDelete
  2. Anonymous:

    I suspect that anti-aliasing is the issue with words not appearing clear when zoomed in. If you right-click and select Properties...->Other and then uncheck "Draw anti-aliased," you can see the effect of disabling anti-aliasing. But this option only applies to the main line chart, not the embedded bar and pie charts (which contain the word "Engineering"). On my systems, the embedded charts always have anti-aliasing enabled, and so the word "Engineering" is always very clear. Other operating systems may not handle anti-aliasing the same. Note that the graphics objects are also smoothed by anti-aliasing, not just the text.

    ReplyDelete
  3. Thank you for replying.

    I still see the same problem. When I run applet in browser, its perfect, but when I run it inside eclipse, the text gets blurry when zoomed in.

    ReplyDelete
  4. Hi, I'm getting compilation problems with the code in this clases,

    GeneralLayerUI
    ZoomPort
    ZoomUI

    in some ovverride anotations and missing methods.

    I'm Working with eclipse and 1.6 builder configured.

    I don't Know What i'm doing wrong, please may you help me?

    ReplyDelete
  5. hi i'm eros from japan, and would like to use the zoom/scale functionality of yours.. may i use it with freedom?

    may i see the runnable source file... because I really want to see it live while debugging..

    ReplyDelete
  6. I successfully build and run from your given Resources but I did several changes, especially in JXLayer datatype (e.g. JXLayer to JXLayer)... and when I test the zooming/scaling functionality... strange behavior and totally different with your sample..

    i think, the problem is the version of JXLayer..
    I'm using JXLayer3.0.4..

    May I know the version you used here..


    Eros, Japan

    ReplyDelete
  7. when I downgrade the JXLayer version from 3.0.4 to 3.0,, your given resources are perfect..

    but still with the blurred image..
    any suggest please..

    thanks,

    Eros,Japan

    ReplyDelete
  8. @Eros:

    Thank you for discovering that JXLayer versions above 3.0 will not work with my code. I modified the post to indicate that I use JXLayer 3.0 and have linked directly to the version 3.0 Maven repository in the Resources section.

    ReplyDelete
  9. I too am getting the blur issue. Anything new on that?

    ReplyDelete
  10. I am able to reproduce the blurry text issues on certain browsers and with certain versions of JFreeChart.

    On Google Chrome 10.0.648.205 Mac, the text is blurry when zoomed out, and the y-axis label is garbled at all zoom levels. But on Safari 5.0.5 and Firefox 3.6.16, everything looks great at all zoom levels.

    Also, when running the applet through a NetBeans 7.0 build, version 1.0.13 of JFreeChart produces blurry text at all levels, but the y-axis label is not garbled. But linking to version 1.0.12 of JFreeChart makes everything look great at all zoom levels.

    I tested using Java SE 6 Update 24 and OpenJDK 1.7_20.0-b06 on Mac OS X 10.6.6.

    So if you experience blurry text, try linking to the older JFreeChart 1.0.12 or try multiple browsers.

    ReplyDelete
  11. Greetings for the Patrick Webster,
    This is Sai,

    In my web application as a user I will put some points on the bar chart(which contains X and Y axis) and then when I click on button all the points should join & form a shape and when I drag and drop that particular shape to other place in that bar chart it should able to read the chart coordinates and I can able to resize that shape.
    Can we able to do this using Jfreecharts.
    Please anyone can open up your suggestions.


    ReplyDelete