Quantcast
Channel: Undocumented Matlab
Viewing all articles
Browse latest Browse all 219

Waterloo graphics animation and web deployment

$
0
0

Once again I’d like to welcome guest blogger Malcolm Lidierth of King’s College London. Last week Malcolm wrote about integrating the Waterloo graphing library in Matlab figures. Today, Malcolm discusses advanced features in this library.

Readers who are interested in high-quality plotting in Matlab may also be interested in the JFreeChart and MTEX open-source packages. JFreeChart provides chart types that are unavailable in Matlab, while MTEX builds upon existing Matlab plots using sophisticated data analysis and visualization. Unlike JFreeChart, I haven’t mentioned MTEX here before. MTEX is definitely worth a good mention, perhaps I’ll write about it someday. If you are using any other good charting packages for Matlab, please add a comment below.

A feature request received from several of Yair’s readers for the Waterloo graphics project was for fast updating and animation of plots. This week, I’ll describe features supporting this. As those need to be illustrated here on a web page, I’ll also describe Waterloo’s new deploy-to-web feature.

Waterloo animated plot

Waterloo animated plot


Updating Waterloo plots

Recall that Waterloo provides a Matlab OOP class called GXPlot, which wraps the underlying Waterloo plot type and can be retrieved using the getObject() method. For a GXPlot named p, the data can easily be updated from Matlab by passing it a new data array of values:

p.getObject().getXData().setDataBufferData(XData);
p.getObject().getYData().setDataBufferData(YData);

This will be fast enough for most purposes, although the exchange of arrays between Matlab and Java is done by copying the data rather than by reference (a limitation imposed by the Java Native Interface rather than Matlab).

If only a few data points are to be changed at any one time, an alternative approach is available of setting individual data points (referenced by their index, 0 being the first):

p.getObject().getXData().setEntry(index, value);
p.getObject().getYData().setEntry(index, value);

These methods will update the plot p, as well as all other plots that share its underlying data buffer.

All that is then needed is to update the display. This means:

  1. Optionally, checking and rescaling the axes as needed
  2. Calling repaint on the graph or its container

The best way will often be to make the container a listener on the relevant data object (the container houses the axes, so these will then be repainted if the scale changes). For example, for the YData:

% get a reference to the plot's graph container
container = p.getObject().getParentGraph().getGraphContainer();
 
% attach the graph as a listener to the ydata buffer 
dataObject = p.getObject().getYData();
dataObject.addPropertyChangeListener(p.getObject().getParentGraph());

Now any change to the YData object will fire a property change event causing the display to update.

A simple example

Waterloo animated bar plot

Waterloo animated bar plot

f = GXFigure();
set(gcf, 'Units','normalized', 'Position',[0.3 0.3 0.4 0.4], 'Name','TestUpdate1');
 
% Create some data and plot a bar chart
Y = hist(randn(1,100), 50);
 
container = gxgca();
p = bar(container, -1:0.0408:1, Y, 'BarWidth',0.0408, 'Fill','LIGHTCORAL');
container.getView().setAxesBounds(-1,0,2,225);
container.getView().setBackground(kcl.waterloo.defaults.Colors.getColor('WHEAT'));
container.getView().setBackgroundPainted(true);
refresh();
 
% Attach the graph as a listener to the ydata buffer 
p.getObject().getYData().addPropertyChangeListener(p.getObject().getParentGraph());
 
% Now update the ydata in a loop - pausing for 0.1s on each iteration.
% The listener attached above will cause a repaint
for k = 1 : 50
    Y = hist(randn(1,100),50);
    Y = Y + p.getObject().getYData().getRawDataValues().';
    p.getObject().getYData().setDataBufferData(Y);
    pause(0.1);
end

Note that the repaints will be done on the Event Dispatch Thread (EDT). The call to the Matlab pause above causes EDT to be flushed so a full repaint is done for each iteration of the loop. Using a timer, as in the example below, can avoid this and allows the Swing repaint mechanisms to collapse multiple repaint calls into a single rendering operation. This will almost always be faster: use drawnow and pause sparingly with Waterloo code.

Improving speed

Refreshing the display as above requires that

  1. the graph container is repainted together with the axes – using internal axes that are rendered as part of step [2] will speed this up
  2. the graph is repainted including grids and internal axes – speed this up by turning off, or reducing the number of, grid lines. Switch off background painting – the container’s background will suffice (this is the default).
  3. the plot displays are updated – to speed this up:
    • Use objects that are easily translated into a square pixel-grid on screen. e.g. use squares instead of circles in a scatter plot
    • Use solid lines, not dashed or dotted ones
    • Turn off anti-aliasing of the plot

As painting is done on the EDT it will not block the Matlab thread. The time taken to update a plot will be variable, but it typically takes 5-20 msecs.

Fast plot updates

In the beta release, new methods have been added to the plots: plotRedraw() and plotUpdate() both redraw a plot without refreshing the background, grids or axes.

In the default implementation, plotUpdate simply calls plotRedraw: there actions are identical. plotUpdate is intended to be overridden in custom user subclasses to paint only points that have been added to end of a plot since the last update and the logic to do that needs to be added to the plot methods in those subclasses.

This logic can also be synthesized on-the-fly (e.g. in Matlab, as in the example below) by adding NaNs in the data objects – Waterloo ignores these values. In the example below, a timer is used to update the plot.

function update2(thisDelay)
 
    if nargin == 0
        DELAY = 0.01;
    else
        DELAY = thisDelay;
    end
 
    % Set up and create some data
    f = GXFigure();
    set(gcf, 'Units','normalized', 'Position',[0.3 0.3 0.4 0.4], 'Name','TestAnimation');
    x = linspace(-pi*4, pi*4, 50);
    y = cos(x);
 
    % Now  the plot
    gr1 = subplot(f, 1,1, 1);
    p1 = line(gxgca, [], [], 'LineSpec', '-og');
    gr1.getObject().getView().setAxesBounds(-pi*4,-2.5,pi*8,5);
 
    % We'll draw 2 points only in each timer call below (2 points needed for interconnecting line)
    % This plot will therefore show only these points when the normal paint mechanism is
    % used unless all the data are added at the end: which the timer callback below does
    p1.getObject().setXData(x(1:2));
    p1.getObject().setYData(y(1:2)*2);
 
    p1.getObject().getParentGraph().setLeftAxisPainted(false);
    p1.getObject().getParentGraph().setRightAxisPainted(false);
    p1.getObject().getParentGraph().setTopAxisPainted(false);
    p1.getObject().getParentGraph().setBottomAxisPainted(false);
    p1.getObject().getParentGraph().setInnerAxisPainted(true);
    p1.getObject().getParentGraph().setInnerAxisLabelled(true);
 
    p1.getObject().getParentGraph().getGraphContainer().repaint();
    drawnow();
 
    t = timer('ExecutionMode','fixedSpacing', 'Period',DELAY, 'TimerFcn', {@localTimer, p1.getObject(), x, y});
    start(t);
 
    function localTimer(t, EventData, p1, x, y)
        k = get(t,'TasksExecuted');
        if k > numel(x)
            % Finished
            stop(t);
            p1.setXData(x);
            p1.setYData(y*2);
            p1.plotRedraw();
        elseif k > 1
            % Add 2 new data points to the plot
            p1.setXData(x(k-1:k));
            p1.setYData(y(k-1:k)*2);
            p1.plotRedraw();
        end
    end  % localTimer
 
end  % update2

Calls to plotRedraw and plotUpdate are extremely fast, typically 1-2 msecs. Each call to plotRedraw() adds two new data points to the existing plot. The result is the animated plot shown at the very top of this article.

Notes:

  • this example used the same timer callback to update both data and display; it will often be best to do this is separate callbacks – the refresh rates for data update and display animation can then be set independently.
  • readers who need plot animation may find interest in the related Matlab functions comet and comet3, or this week’s POTW multicomet.
  • plot animation in standard Matlab typically relies on the EraseMode property. In the upcoming HG2, this mechanism will no longer work, at least as seen in the HG2 prequel that is available today. So if your code uses any plot animation, you should expect it to break when HG2 is released (2014?), and you will need to convert your code to use HG2′s new animation functionality. Waterloo graphs and animation, being independent Java-based, are not expected to be affected, and will run the same way in both HG1 and HG2.

Deploying graphics to the web

Waterloo enables export of static graphics to a variety of vector and bit-mapped formats (PNG, PDF, SVG etc.) that can be included in web pages. In the beta version, deployment to web is also supported directly, with automatic generation of the graphics files together with accompanying HTML files and optional CSS styling sheets and supporting Javascript. These are available from the Graph Editor, which is activated by double-clicking a graph and selecting the world (Waterloo world icon) button.

Two formats are presently supported:

  • Scalable Vector Graphics (SVG). By default, the generated files provide conversion of the SVG to HTML5 canvas commands when loaded into a browser via the canvg.js JavaScript by Gabe Lerner. Use of canvg provides better cross-browser consistency in the rendering of the graphics, particularly for text. Note that only static graphics are presently supported with SVG.
  • Through the Processing script language for visual arts and processing.js JavaScript.

Processing script output supports animations using a new class developed for Waterloo (PDEGraphics2D), which also supports an AWT/Swing container. The generated script files can be loaded and customized using the Process.app file available at the web site above.

In addition, experimental (and presently quirky) support for generating animated GIFs is included. The animations for this blog were generated using this. Animated GIFs are to be preferred when vector graphics are not required because GIFs

  • are universally and consistently supported in browsers
  • are smaller and less computationally intensive. They therefore consume less power and are environmentally friendlier. Visitors to a website, especially those using battery-powered devices, are likely to stay longer on the site.

For the present, only static graphics are supported through the GUIs, so animations need to be created programmatically. A “record” button will be added to the GUIs in future Waterloo releases.

Users can edit the default settings for deployment using the preferences editor, now available from the Graph Editor using the key (Waterloo key icon) button. For SVG, the options are shown below: allowing a choice of whether SVG is to be embedded in the generated HTML file; whether canvg.js should be used and selection of a styling sheet. Customize the file addresses relative to the target folder for the HTML file or use a URL. The “httpd.py” file here is a Python 2.7 script that will set up a local server and display the HTML file in your system browser – it is needed only if the security rules for your browser prevent the files being loaded directly from the local file system.

Waterloo preferences window

Waterloo preferences window

By default, the deploy tool uses a template index.html file that is embedded in the distribution. You can specify your own instead, although I have not yet added that to the Preferences GUI.

 
Related posts:
  1. Waterloo graphics examples Some Matlab usage examples for the open-source Waterloo graphics package. ...
  2. Waterloo graphics beta The Waterloo graphics library extends Matlab graphics with numerous customizable plots that can be embedded in Matlab figures. ...
  3. Waterloo graphics Waterloo is an open-source library that can significantly improve Matlab GUI. ...
  4. Handle Graphics Behavior HG behaviors are an important aspect of Matlab graphics that enable custom control of handle functionality. ...
 

Viewing all articles
Browse latest Browse all 219

Trending Articles