Matlab Utilities
Usage Information
All of the Matlab tools described here, along with their supporting JAR files, are distributed with Figure Composer. Note that you must have Matlab 7.x (R2004) or later to use these functions. For full details on how to use each function, make sure the requisite M-file is in your Matlab command path, then type help function-name at the command prompt. We strongly recommend that you read the help information carefully.
The supporting JAR files must be in Matlab's Java class path or most of these functions will not work; instead, you will see some mysterious error messages in the command console when you try to execute a function. To add the JAR files to the dynamic class path, use the Matlab command javaclasspath({'jarpath1', 'jarpath2', ...}), where 'jarpath1', 'jarpath2', ... are full file system pathnames identifying the required JAR files. Of course, you have to remember to execute this command each time you start Matlab. A more convenient approach is to use the javaaddpath command in your startup.m file: javaaddpath jarpath1, javaaddpath jarpath2, ...
Here are the supporting JAR files required to run the FigureComposer-related Matlab utilties:
hhmi-ms-common.jar : This JAR includes utility code used not only by the FC-specific M-functions, but also by the Maestro-specific M-function maestrodoc().
hhmi-ms-datanav.jar : This JAR includes all FC-specific code required by most of the M-functions distributed in the Matlab support package.
xpp3-1.3.4.D.jar : This third-party JAR is needed to read and write FypML figure files. As of V5.2.0, this JAR is no longer included, as FC's dependency on this library has been removed.
itextpdf-5.5.0.jar : This third-party JAR is required only because of a dependency in the FypML figure rendering code. It was introduced to provide PDF export support in Figure Composer v4.4.3.
Once you've downloaded and installed Figure Composer for your OS, locate the file system directory containing the Matlab support package. You should find each of the above JAR files in that directory, and each must be added to Matlab's Java class path. The best way to do this is to append the line javaaddpath path_to_JAR to your Matlab startup.m file for each required JAR file; here, path_to_JAR is the full file system pathname locating the JAR. Alternatively, you could call javaclasspath(path_to_JAR) from the Matlab command line, but then you have to remember to do this for each JAR every time you run the program.
NOTE 1: Delete datanavsrc.jar. In versions prior to V4.2, the required JARs were xpp3-1.1.3.4.D.jar and datanavsrc.jar. The former is unchanged, but the latter is now replaced by hhmi-ms-common.jar and hhmi-ms-datanav.jar. Be sure to remove datanavsrc.jar from your Matlab Java class path, or the Matlab support functions will fail with strange errors!
NOTE 2: Always use the latest version of hhmi-ms-common.jar. If you have configured your Matlab installation to run both Maestro-specific and FC-specific Matlab support functions, understand that hhmi-ms-common.jar is shared by all. Whenever you update this particular JAR file, whether you're retrieving it from this download page or the download page for Maestro's online guide, check the modification date to ensure that the new hhmi-ms-common.jar is more recent than the one you're replacing. If you accidentally overwrite hhmi-ms-common.jar with an older version of the file, you again may encounter strange Java class-loading errors when you run any of the Matlab support functions.
NOTE 3: Be sure that the Java version of the supporting JARs <= the version of the Java Runtime Environment (JRE) that Matlab is using, or once again you'll see "Java class not found" errors.
FC 4.x - 5.0.2: Java 6 or better JRE required.
FC 5.1.x: Java 7 or better JRE required.
FC 5.2.x - 5.4.x: Java 8 or better JRE required.
FC 5.5.x or laterr: Java 11 or better JRE required.
The Matlab command jenv will print information on the JRE in use, and the same command can change the JRE. Note that the default JRE bundled with Matlab since R2019b is Java 8-compliant, and that is unlikely to change. However, since R2023a, Matlab supports using an OpenJDK 8 or 11-compliant runtime like Amazon Corretto. If you want to use the supporting JARs that come with FC5.5.x or later, you must be running Matlab R2023a or later.
Convert a Matlab figure to a DataNav figure: matfig2fyp()
Perhaps the most difficult obstacle to composing scientific figures in Figure Composer is getting the actual raw data into the application in a manner that is convenient for users. Our first attempt to address this roadblock was the utility function putdatanavsrc(), which lets the Matlab user populate a file with any number of FC-compatible data sets. Then, the user can design the figure from scratch in FC and load the necessary data from that file. However, this strategy requires a certain familiarity with the different data set formats supported by the program, learning how to use the relatively complex putdatanavsrc() function, and then the additional work of creating a figure from scratch and loading data into it.
Since so many researchers in neuroscience (and other areas of study) are accustomed to analyzing their data and preparing figures in Matlab, it would be extremely helpful if a Matlab figure could be translated directly -- data and all -- into a Figure Composer FypML figure. That is exactly what matfig2fyp(H, F) does. It takes the handle H of an open Matlab figure, converts it to an equivalent FypML figure, and saves it to the file F. This file can then be opened in Figure Composer, and the figure modified and embellished as needed to prepare it for scientific publication.
Of course, many Matlab figures cannot be converted because Figure Composer does not support all the features and specialized plotting functions that Matlab offers. Nevertheless, in its current state, the function can convert a Matlab surface or image object to a FypML contour node (in the "heatMap" display mode); it can generate trace nodes rendering ptset, series, mset, or mseries data from Matlab line series plots; it can convert specialized 2D plots generated by Matlab's polar(), rose(), compass(), bar(), area(), pie(), scatter(), contour(), and contourf() functions; and it can convert raster train plots generated by the dn_rasterplot() function, described below. As of Version 5.0.0, it can translate most 3D plots generated by Matlab's surf(), scatter3(), and stem3() functions. It translates each Matlab axes in the figure into a FypML graph node -- or graph3d node for 3D plots --, preserving the axis ranges, labels, and major tick marks if possible, and it will replicate any legends and color bars present in the figure. We will continue to enhance its capabilities as users provide feedback.
On polar plots: Until the release of Matlab R2016, the polar plots generated by the polar(), rose(), and compass() functions were "axes" objects specially configured to look like polar plots. R2016 introduced a true polar plot object, "polaraxes", along with new built-in functions that generate this object: polarplot() replaces polar(), polarhistogram() replaces rose(), and polarscatter() offers a scatter or bubble plot in polar coordinates.
Prior to FC 5.1.2, all such polar plots -- whether represented by an "axes" or "polaraxes" object -- were translated to a FypML graph node configured in polar coordinates. With the introduction of the specialized polar plot element pgraph in FC 5.1.2, the FIG-to-FypML import engine and matfig2fyp() now convert any Matlab polar plot to a pgraph. The same is true for the pie charts generated by Matlab's pie() function.
Save a Matlab FIG file to be imported by FC: savefigasis()
As of Version 4.2.3, Figure Composer can import a Matlab figure saved as a MAT file with the .fig extension. The conversion code is the same as that used by the matfig2fyp() utility. However, there are some issues with the FIG file format that lead to problems when the FIG file is imported directly into FC. The savefigasis() utility is provided to help skirt these issues:
Newer versions of Matlab may be configured to save the FIG file in the HDF storage format, which FC is unable to read at this time. You can set your Matlab preferences to use the older "-v7" storage format, or you can specify that format when you save a particular figure, OR you can use savefigasis() instead. It invokes the built-in function hgsave() with the "-v7" argument to ensure the FIG file is saved in the storage format (MAT Level 5 with compression) that FC can handle.
When a Matlab figure is saved to a FIG file, the axis ranges and tick marks for a Handle Graphics "axes" or "scribe.colorbar" object may not be saved. When such a FIG file is opened in Composer, the import engine configures the corresponding FypML "graph" or "color axis" to be auto-ranged. Generally, this means that the graph will likely have somewhat different axis ranges and tick mark sets compared to the original Matlab figure. To address this problem, savefigasis() sets select properties on these objects to ensure that the exact axis ranges and tick mark sets are explicitly stored in the FIG file.
The "Title" property of Matlab's "polaraxes" object -- a true polar plot introduced in R2016 -- is saved in the FIG file in an opaque format that FC cannot access. To workaround this issue, savefigasis() will store the title string and color in a cell array in the "UserData" property of the "polaraxes" object. FC's FIG-to-FypML import engine will examine that property to check for the title information.
For the best results, we recommend using savefigasis() rather than hgsave() whenever saving a Matlab figure you intend to import directly into Figure Composer.
Limitations of matfig2fyp() and the FIG file imports
Both the matfig2fyp() utility and Figure Composer's FIG file import engine must analyze a Matlab figure's Handle Graphics (HG) object tree in order to prepare a Java version of that tree that contains enough information to translate the original Matlab figure into a FypML figure. Under the hood, matfig2fyp() relies on an undocumented built-in Matlab function, handle2struct(), which converts the HG tree to a structure array. This same function is used when saving a figure to a FIG file**. Undocumented changes in handle2struct() have led to compatibility issues with the FC's FIG import code:
Matlab release 2014b (R2014b) introduced a major overhaul in its figure graphics, including significant changes in its Handle Graphics (HG) object infrastructure that broke the FIG file import engine. These issues were resolved in FC 4.6.2.
Some newer Matlab graphics functions introduced since R2014b -- such as the boxchart() function added in R2020b -- generate an HG tree that is not correctly converted to an array of structures by handle2struct(). As a result, even though FC added support for box plots in version 5.4.1, a Matlab figure generated by boxchart() cannot be converted via matfig2fyp(), nor can a Matlab boxchart saved in a FIG file be imported directly into FC. The same is true for the piechart() and donutchart() functions added in R2023b.
Whenever you run into any problems using matfig2fyp(), or you are unable to import a FIG file directly into FC, feel free to send me the FIG file along with an explanation of what went wrong.
[**When Matlab's hgsave() (called by savefigasis()) or savefig() function writes a figure to a FIG file (which is really just a MAT file), the figure is saved in two different formats: (1) As an array of Matlab structures as prepared by handle2struct(); (2) as an opaque Matlab Class Object System (MCOS) object stream. FC relies on the JMatIO library to read the FIG file, and JMatIO cannot handle the second format. In order to process newer HG constructs that are only saved in the MCOS format, we would have to find an alternative to JMatIO that can handle the MCOS format.]
Inject a data set, text, or a graph into a FypML figure: put2fyp(), putdata2fyp()
Suppose you have a complete, nicely formatted figure in Figure Composer. It's ready for publication, but then you get comments back from reviewers that prompt you to revise your analysis somewhat. Now you need to replace several of the data sets in your figure, and you'd like to do that without having to rebuild the figure from scratch. This is where the Matlab utility function put2fyp(F, 'data', ID, X, Y, ...) may come in handy. It lets you inject a data set prepared in Matlab directly into an existing FypML figure without changing how that data is rendered. You can either overwrite the existing FypML file (the F argument) or save the updated figure to a different file.
The utility replaces the little-used and now-deprecated putdatatofig() function. Unlike that function, put2fyp() shields the user from the nitty-gritty details of the various FypML-supported data formats. The data to be injected is specified only by an x-coordinate vector and a y-coordinate vector or matrix, a natural way for Matlab users to present or prepare 2D data sets. The function examines the X,Y arguments and the data set being replaced (identified by the ID argument) in order to infer what the format and parameters of the new data set should be. Best of all, you can optionally preview the revised figure before saving it, which can save you time if you make a mistake in the script that generates the data. You no longer have to save the figure, open it in Figure Composer, discover your mistake, and then go back to Matlab to fix it! To make it simpler to use, put2fyp() only lets you inject one data set at a time into a figure. This should not be a significant limitation -- just invoke the function once for each data set you need to replace.
To use put2fyp() effectively, you need to know the character string identifier (the ID argument) of each raw data set you want to replace in your figure. This remains a barrier to the function's adoption for many users, who tend not to assign "memorable" set IDs to the data sets in their figures. Also, to specify less-intuitive data sets like raster-type data or data points with x- and/or y-standard deviations, you'll still have to dive into the detailed information provided in the Matlab command window by help put2fyp.
The put2fyp() utility is actually an enhanced version of the older putdata2fyp(). In addition to injecting raw data, the utility also lets you make other changes to an existing figure:
put2fyp(F, 'note', S) lets you set or update the note property of the root figure node. The figure note is for informational purposes only. It does appear in the figure preview in FC's file chooser dialog and workspace browser.
put2fyp(F, 'text', ID, S) or put2fyp(F, 'title', ID, S) lets you replace the text content of a label or textbox node, change the title of any 2D or 3D graph container, or change the title of any data presentation node. [You can use either operation code -- 'text' or 'title'; they're essentially synonymous.] The string argument ID identifies the graphic object to be modified, and the character string S is the replacement text. The location and styling of the object are unaffected. If you need to specify where the line breaks occur for text in a text box, the argument S should be cell array of strings, with each cell containing a separate line of text. For all other nodes, the replacement text will be a single-line string (even if S is a cell array). Note that any graph container, text label, or text box object is uniquely identified by assigning it an object ID (the id attribute introduced in version 4.6.2). For data presentation nodes, ID refers to the string identifier for the node's source data set. In rare cases, multiple data presentation nodes in a figure could share the same data set; in this scenario, the command would update the title of all such nodes.
put2fyp(F, 'legend', ID, POS, LBL) lets you change the legend label for any data group in a bar, area or pie chart. [These three types of data presentation nodes present groups of data, with a separate legend entry for each such group.] The string argument ID identifies the source data set of the affected data presentation node, POS is the index position of the data group to be updated (an integer), and LBL is its new legend label (a single-line character string). Again, in the unlikely event that multiple presentation nodes share the same data set, all of those nodes will be updated in the same manner.
put2fyp(F, 'graph', ID, H) replaces an entire graph in your figure. Again, ID identifies the graph to be replaced, while H is the handle of a Matlab 'axes' (or 'polaraxes', as of FC 5.1.2) object currently on display in the Matlab environment. This 'axes' defines the replacement graph. The original graph can be a 2D graph, 2D polar plot, or 3D graph; same for the replacement graph. Typically, both graphs will be 2D or both 3D, but that is not a requirement. Note that the location, dimensions, and overall style of the original graph is preserved -- in an effort to make the replacement graph "fit in" with the rest of the figure. [However, if the original graph is 2D and its replacement is 3D -- or vice versa --, you will definitely need to make some changes in the resulting figure. The location and dimensions of a 2D graph have a very different interpretation versus a 3D graph.
These commands became possible with FypML schema changes in versions 4.6.2 and 4.7.0, particularly the introduction of the id attribute for selected graphic elements. This attribute is similar to the data set ID mentioned above, except that it is optional (typically, you'll "ID" only those graphic objects that you might want to change via script) and can contain any Unicode character supported in FC.
As of version 4.7.3, two additional operations give you some control over layout of the graphs within a FypML figure:
put2fyp(F, 'figure', L) creates an empty figure and saves it in the FypML file F. L = [x y w h] indicates the location and size of the figure on the printed page.
put2fyp(F, 'addgraph', H, L) converts an 'axes' (or 'polaraxes', as of FC 5.1.2) object in an open Matlab figure to a FypML graph, pgraph, or graph3d node, which is then inserted into an existing FypML figure defined in file F. H is the handle of the axes object, and L specifies the location and dimensions of the graph in inches. For a 2D graph or polar plot, L = [x y w h], where (x, y) are the coordinates of the bottom-left corner of the graph's bounding box with respect to the bottom-left corner of the parent figure, and (w,h) are its width and height. For a 3D graph, L = [x y w h d], where (x,y) are the coordinates of the 3D coordinate system's origin WRT the parent figure, and (w, h, d) are its X-axis, Y-axis, and Z-axis extents in 3D space.
When using the 'addgraph' operation to layout 2D graphs in a multi-plot figure, keep in mind that the argument L defines the bounding box for the graph, and the graph's axes are typically drawn outside that rectangle. (Similarly, for a 2D polar plot -- pgraph -- the theta grid labels typically appear outside the bounding box.) The amount of space occupied by an axis will depend on a number of graph properties (axis label offset, axis offset from data window, tick mark size, font size). In the 3D case, the graph will be roughly centered around (x,y), but its actual size depends on its backdrop style, the values of (w,h,d), and the graph's rotation and elevation angles. It is reasonable to expect that you will have to "tweak" the locations and sizes of graphs within a multi-plot figure created via put2fyp().
[NOTE: While put2fyp() is intended to replace putdata2fyp(), the latter function is still distributed with FC so that existing user scripts need not be changed. However, putdata2fyp() is considered "obsolete" and will be removed in a future release.]
Create a "raster train" plot in Matlab: dn_rasterplot()
A common figure in neuroscience circles is the "raster plot", which presents spike occurrence times as a series of short vertical lines along a common baseline parallel to the X-axis. Typically, a number of these "raster trains" are presented in the same graph, spaced vertically as in the example below:
Since Matlab does not offer a plotting function for this kind of presentation of 1D discrete-time event data, we developed a utility function that does: dn_rasterplot(R, ...). It prepares a specialized polyline that draws a single vertical hash mark for each event in R, using (NaN, NaN) for every third point in the polyline so that the hash marks are not connected to each other. The argument R can take two forms:
An NxM matrix. Each column represents a separate raster train, while the rows represent time elapsed. A nonzero value in R(n,m) indicates that an "event occurred" at time (n-1) in the m-th raster train.
An Mx1 cell array of vectors. Each vector contains the event occurrence times for the m-th raster train.
For a complete description of the function and its other arguments, download dn_rasterplot.m, install it in your Matlab command path, and type help dn_rasterplot on the command line.
NOTE that a Matlab figure containing raster trains generated with dn_rasterplot() can be translated into a similar FypML figure using matfig2fyp(). Each raster train will be presented in a raster node, and the actual event data will be drawn from the polyline's X- and Y-vectors and stored in the FypML figure as a data set in raster1d format. In fact, the graph above is an image of a FypML figure prepared by first using dn_rasterplot() to create the Matlab figure, then matfig2fyp() to convert it to FypML format.
Read/write Figure Composer data set source files in Matlab: getdatanavsrc(), putdatanavsrc()
While the Dataset Editor Dialog is reasonably easy to use and flexibly supports cut-and-paste from applications like Excel, it remains a relatively laborious and time-consuming method for pushing data into a figure in FC. It is really intended more as an avenue for viewing and editing the raw data, or for creating very small data sets from scratch. What is needed is an automated means of storing one or more data sets into a source file that Figure Composer can read.
Data set source files in Phyplot -- the predecessor to Figure Composer -- were plain-text files containing columns of floating-point numbers with little or no "metadata" describing the data (ID, format type, parameters like the sample interval dx). A blank line in the file separated one data set from the next. The advantage of such a simple format was that it was easy for any user -- regardless what tools they used to generate their data -- to repackage it in a form that Phyplot could handle.
However, this simple format has a number of serious disadvantages that make it ill-suited for use in Figure Composer:
It is very inefficient. Storing a single-precision floating-point value always requires 4 bytes in a binary file, possibly fewer if compression is used. In a plain-text file, the number of bytes can be as little as 2 (e.g., "0" plus a delimiter character), but will typically be much larger than 4 (e.g., "-100.68" takes 8 bytes, 7 characters plus the delimiter). If the source file will contain many large data sets, a binary format is a much better solution.
It does not support randomly accessing a selected data set within the file. Authors using Figure Composer to prepare the figures for a journal paper can benefit from being able to store all the figure data in a single file. This introduces a new problem -- extracting a single data set from such a file. With the old Phyplot numbers-only format, this was impossible because there was no identifying information (except order within the file). Even with the simple annotated format, we would still have to read the file line-by-line until we found the right data set.
Figure Composer addresses these problems by supporting a binary data source file format with a table of contents at the beginning of the file that includes summary information and file offsets for each data set stored in the file. In addition, it supports an annotated plain-text format with a table of contents. This format is less compact than the binary format, but it is better suited for transmission over the Internet. The recommended (but not required, since each format includes a header for identification purposes) file extension for the binary format is .dnr (formerly, .dnb); for the annotated format, .dna or .txt.
Of course, introducing an opaque "proprietary" format means we need to provide tools for reading and writing these files. Since members of the Lisberger laboratory use Matlab extensively to analyze their data, we developed the complementary Matlab M-functions, putdatanavsrc() and getdatanavsrc(). Use putdatanavsrc() to write Matlab-generated data in FC-compatible data source files in either format -- although you are encouraged to use the binary format exclusively unless you have a really good reason not to do so! With it you can store any number of data sets in a single file, as long as each data set has a unique identifier. Use getdatanavsrc() to read datasets from the source file back into Matlab. The source file may have been generated by Figure Composer, by the Lisberger lab's analysis program JMWork, or by a previous call to putdatanavsrc().