Handle graphics – Undocumented Matlab https://undocumentedmatlab.com Charting Matlab's unsupported hidden underbelly Fri, 20 Oct 2017 09:57:44 +0000 en-US hourly 1 https://wordpress.org/?v=4.4.1 Tips for accelerating Matlab performancehttps://undocumentedmatlab.com/blog/tips-for-accelerating-matlab-performance https://undocumentedmatlab.com/blog/tips-for-accelerating-matlab-performance#comments Thu, 05 Oct 2017 18:25:06 +0000 http://undocumentedmatlab.com/?p=7099
 
Related posts:
  1. Performance: scatter vs. line In many circumstances, the line function can generate visually-identical plots as the scatter function, much faster...
  2. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  3. Plot performance Undocumented inner plot mechanisms can significantly improve plotting performance ...
  4. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
 
]]>
I’m proud to report that MathWorks has recently posted my article “Tips for Accelerating MATLAB Performance” in their latest newsletter digest (September 2017). This article is an updated and expanded version of my post about consulting work that I did for the Crustal Dynamics Research Group at Harvard University, where I helped speed-up a complex Matlab-based GUI by a factor of 50-500 (depending on the specific feature).

Crustal dynamics visualization GUI

Crustal dynamics visualization GUI

Featuring an article on the official newsletter by a non-MathWorker is rare. Doing this with someone like myself who has a reputation for undocumented aspects, and a consultancy business that potentially competes with theirs, is certainly not obvious. I take this to be a sign that despite the possible drawbacks of publishing my article, MathWorks felt that it provided enough value to the Matlab user community to merit the risk. I applaud MathWorks for this, and thank them for the opportunity of being featured in their official newsletter and conferences. I do not take it for granted in the least.

The newsletter article provides multiple ideas of improving the run-time performance for file I/O and graphics. Many additional techniques for improving Matlab’s performance can be found under the Performance tag in this blog, as well as in my book “Accelerating MATLAB Performance” (CRC Press, 2014, ISBN 978-1482211290).

Next week I will present live online webinars about various ways to improve Matlab’s run-time performance:

These live webinars will be 3.5 hours long, starting at 10am EDT (7am PDT, 3pm UK, 4pm CET, 7:30pm IST, time in your local timezone), with a short break in the middle. The presentations content will be based on onsite training courses that I presented at multiple client locations (details). A recording of the webinars will be available for anyone who cannot join the live events.

 Email me if you would like additional information on the webinars or my consulting, or to inquire regarding an onsite training course.

]]>
https://undocumentedmatlab.com/blog/tips-for-accelerating-matlab-performance/feed 2
Customizing axes part 5 – origin crossover and labelshttps://undocumentedmatlab.com/blog/customizing-axes-part-5-origin-crossover-and-labels https://undocumentedmatlab.com/blog/customizing-axes-part-5-origin-crossover-and-labels#comments Wed, 27 Jul 2016 17:00:02 +0000 http://undocumentedmatlab.com/?p=6564
 
Related posts:
  1. Customizing axes rulers HG2 axes can be customized in numerous useful ways. This article explains how to customize the rulers. ...
  2. Customizing axes part 2 Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  3. Undocumented scatter plot jitter Matlab's scatter plot can automatically jitter data to enable better visualization of distribution density. ...
  4. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
 
]]>
When HG2 graphics was finally released in R2014b, I posted a series of articles about various undocumented ways by which we can customize Matlab’s new graphic axes: rulers (axles), baseline, box-frame, grid, back-drop, and other aspects. Today I extend this series by showing how we can customize the axes rulers’ crossover location.

Non-default axes crossover location

Non-default axes crossover location


The documented/supported stuff

Until R2015b, we could only specify the axes’ YAxisLocation as 'left' (default) or 'right', and XAxisLocation as 'bottom' (default) or 'top'. For example:

x = -2*pi : .01 : 2*pi;
plot(x, sin(x));
hAxis = gca;
hAxis.YAxisLocation = 'left';    % 'left' (default) or 'right'
hAxis.XAxisLocation = 'bottom';  % 'bottom' (default) or 'top'

Default axis locations: axes crossover is non-fixed

Default axis locations: axes crossover is non-fixed

The crossover location is non-fixed in the sense that if we zoom or pan the plot, the axes crossover will remain at the bottom-left corner, which changes its coordinates depending on the X and Y axes limits.

Since R2016a, we can also specify 'origin' for either of these properties, such that the X and/or Y axes pass through the chart origin (0,0) location. For example, move the YAxisLocation to the origin:

hAxis.YAxisLocation = 'origin';

Y-axis location at origin: axes crossover at 0 (fixed), -1 (non-fixed)

Y-axis location at origin: axes crossover at 0 (fixed), -1 (non-fixed)

And similarly also for XAxisLocation:

hAxis.XAxisLocation = 'origin';

X and Y-axis location at origin: axes crossover fixed at (0,0)

X and Y-axis location at origin: axes crossover fixed at (0,0)

The axes crossover location is now fixed at the origin (0,0), so as we move or pan the plot, the crossover location changes its position in the chart area, without changing its coordinates. This functionality has existed in other graphic packages (outside Matlab) for a long time and until now required quite a bit of coding to emulate in Matlab, so I’m glad that we now have it in Matlab by simply updating a single property value. MathWorks did a very nice job here of dynamically updating the axles, ticks and labels as we pan (drag) the plot towards the edges – try it out!

The undocumented juicy stuff

So far for the documented stuff. The undocumented aspect is that we are not limited to using the (0,0) origin point as the fixed axes crossover location. We can use any x,y crossover location, using the FirstCrossoverValue property of the axes’ hidden XRuler and YRuler properties. In fact, we could do this since R2014b, when the new HG2 graphics engine was released, not just starting in R2016a!

% Set a fixed crossover location of (pi/2,-0.4)
hAxis.YRuler.FirstCrossoverValue = pi/2;
hAxis.XRuler.FirstCrossoverValue = -0.4;

Custom fixed axes crossover location at (π/2,-0.4)

Custom fixed axes crossover location at (π/2,-0.4)

For some reason (bug?), setting XAxisLocation/YAxisLocation to ‘origin’ has no visible effect in 3D plots, nor is there any corresponding ZAxisLocation property. Luckily, we can set the axes crossover location(s) in 3D plots using FirstCrossoverValue just as easily as for 2D plots. The rulers also have a SecondCrossoverValue property (default = -inf) that controls the Z-axis crossover, as Yaroslav pointed out in a comment below. For example:

N = 49;
x = linspace(-10,10,N);
M = peaks(N);
mesh(x,x,M);

Default crossover locations at (-10,±10,-10)

Default crossover locations at (-10,±10,-10)

hAxis.XRuler.FirstCrossoverValue  = 0; % X crossover with Y axis
hAxis.YRuler.FirstCrossoverValue  = 0; % Y crossover with X axis
hAxis.ZRuler.FirstCrossoverValue  = 0; % Z crossover with X axis
hAxis.ZRuler.SecondCrossoverValue = 0; % Z crossover with Y axis

Custom fixed axes crossover location at (0,0,-10)

Custom fixed axes crossover location at (0,0,-10)

hAxis.XRuler.SecondCrossoverValue = 0; % X crossover with Z axis
hAxis.YRuler.SecondCrossoverValue = 0; % Y crossover with Z axis

Custom fixed axes crossover location at (0,0,0)

Custom fixed axes crossover location at (0,0,0)

Labels

Users will encounter the following unexpected behavior (bug?) when using either the documented *AxisLocation or the undocumented FirstCrossoverValue properties: when setting an x-label (using the xlabel function, or the internal axes properties), the label moves from the center of the axes (as happens when XAxisLocation=’top’ or ‘bottom’) to the right side of the axes, where the secondary label (e.g., exponent) usually appears, whereas the secondary label is moved to the left side of the axis:

Unexpected label positions

Unexpected label positions

In such cases, we would expect the labels locations to be reversed, with the main label on the left and the secondary label in its customary location on the right. The exact same situation occurs with the Y labels, where the main label unexpectedly appears at the top and the secondary at the bottom. Hopefully MathWorks will fix this in the next release (it is probably too late to make it into R2016b, but hopefully R2017a). Until then, we can simply switch the strings of the main and secondary label to make them appear at the expected locations:

% Switch the Y-axes labels:
ylabel(hAxis, '\times10^{3}');  % display secondary ylabel (x10^3) at top
set(hAxis.YRuler.SecondaryLabel, 'Visible','on', 'String','main y-label');  % main label at bottom
 
% Switch the X-axes labels:
xlabel(hAxis, '2^{nd} label');  % display secondary xlabel at right
set(hAxis.XRuler.SecondaryLabel, 'Visible','on', 'String','xlabel');  % main label at left

As can be seen from the screenshot, there’s an additional nuisance: the main label appears a bit larger than the axes font size (the secondary label uses the correct font size). This is because by default Matlab uses a 110% font-size for the main axes label, ostensibly to make them stand out. We can modify this default factor using the rulers’ hidden LabelFontSizeMultiplier property (default=1.1). For example:

hAxis.YRuler.LabelFontSizeMultiplier = 1;   % use 100% font-size (same as tick labels)
hAxis.XRuler.LabelFontSizeMultiplier = 0.8; % use 80% (smaller than standard) font-size

Note: I described the ruler objects in my first article of the axes series. Feel free to read it for more ideas on customizing the axes rulers.

]]>
https://undocumentedmatlab.com/blog/customizing-axes-part-5-origin-crossover-and-labels/feed 5
Customizing uifigures part 1https://undocumentedmatlab.com/blog/customizing-uifigures-part-1 https://undocumentedmatlab.com/blog/customizing-uifigures-part-1#comments Thu, 21 Jul 2016 10:32:51 +0000 http://undocumentedmatlab.com/?p=6554
 
Related posts:
  1. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
  2. Customizing print setup Matlab figures print-setup can be customized to automatically prepare the figure for printing in a specific configuration...
  3. Plot LineSmoothing property LineSmoothing is a hidden and undocumented plot line property that creates anti-aliased (smooth unpixelized) lines in Matlab plots...
  4. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
 
]]>
Last month, I posted an article that summarized a variety of undocumented customizations to Matlab figure windows. As I noted in that post, Matlab figures have used Java JFrames as their underlying technology since R14 (over a decade ago), but this is expected to change a few years from now with the advent of web-based uifigures. uifigures first became available in late 2014 with the new App Designer preview (the much-awaited GUIDE replacement), and were officially released in R2016a. AppDesigner is actively being developed and we should expect to see exciting new features in upcoming Matlab releases.

Matlab's new AppDesigner (a somewhat outdated screenshot)

Matlab's new AppDesigner (a somewhat outdated screenshot)

However, while AppDesigner has become officially supported, the underlying technology used for the new uifigures remained undocumented. This is not surprising: MathWorks did a good job of retaining backward compatibility with the existing figure handle, and so a new uifigure returns a handle that programmatically appears similar to figure handles, reducing the migration cost when MathWorks decides (presumably around 2018-2020) that web-based (rather than Java-based) figures should become the default figure type. By keeping the underlying figure technology undocumented and retaining the documented top-level behavior (properties and methods of the figure handle), Matlab users who only use the documented interface should expect a relatively smooth transition at that time.

So does this mean that users who start using AppDesigner today (and especially in a few years when web figures become the default) can no longer enjoy the benefits of figure-based customization offered to the existing Java-based figure users (which I listed in last month’s post)? Absolutely not! All we need is to get a hook into the uifigure‘s underlying object and then we can start having fun.

The uifigure Controller

One way to do this is to use the uifigure handle’s hidden (private) Controller property (a matlab.ui.internal.controller.FigureController MCOS object whose source-code appears in %matlabroot%/toolbox/matlab/uitools/uicomponents/components/+matlab/+ui/+internal/+controller/).

Controller is not only a hidden but also a private property of the figure handle, so we cannot simply use the get function to get its value. This doesn’t stop us of course: We can get the controller object using either my getundoc utility or the builtin struct function (which returns private/protected properties as an undocumented feature):

>> hFig = uifigure('Name','Yair', ...);
 
>> figProps = struct(hFig);  % or getundoc(hFig)
Warning: Calling STRUCT on an object prevents the object from hiding its implementation details and should thus be
avoided. Use DISP or DISPLAY to see the visible public details of an object. See 'help struct' for more information.
(Type "warning off MATLAB:structOnObject" to suppress this warning.)
 
Warning: figure JavaFrame property will be obsoleted in a future release. For more information see
the JavaFrame resource on the MathWorks web site.
(Type "warning off MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame" to suppress this warning.)
 
figProps = 
                      JavaFrame: []
                    JavaFrame_I: []
                       Position: [87 40 584 465]
                   PositionMode: 'auto'
                            ...
                     Controller: [1x1 matlab.ui.internal.controller.FigureController]
                 ControllerMode: 'auto'
                            ...
 
>> figProps.Controller
ans = 
  FigureController with properties:
 
       Canvas: []
    ProxyView: [1x1 struct]
 
>> figProps.Controller.ProxyView
ans = 
            PeerNode: [1x1 com.mathworks.peermodel.impl.PeerNodeImpl]
    PeerModelManager: [1x1 com.mathworks.peermodel.impl.PeerModelManagerImpl]
 
>> struct(figProps.Controller)
Warning: Calling STRUCT on an object prevents the object from hiding its implementation details and should thus be
avoided. Use DISP or DISPLAY to see the visible public details of an object. See 'help struct' for more information.
(Type "warning off MATLAB:structOnObject" to suppress this warning.)
 
ans = 
               PositionListener: [1x1 event.listener]
    ContainerPositionCorrection: [1 1 0 0]
                      Container: [1x1 matlab.ui.internal.controller.FigureContainer]
                         Canvas: []
                  IsClientReady: 1
              PeerEventListener: [1x1 handle.listener]
                      ProxyView: [1x1 struct]
                          Model: [1x1 Figure]
               ParentController: [0x0 handle]
      PropertyManagementService: [1x1 matlab.ui.internal.componentframework.services.core.propertymanagement.PropertyManagementService]
          IdentificationService: [1x1 matlab.ui.internal.componentframework.services.core.identification.WebIdentificationService]
           EventHandlingService: [1x1 matlab.ui.internal.componentframework.services.core.eventhandling.WebEventHandlingService]

I will discuss all the goodies here in a future post (if you are curious then feel free to start drilling in there yourself, I promise it won’t bite you…). However, today I wish to concentrate on more immediate benefits from a different venue:

The uifigure webwindow

uifigures are basically webpages rather than desktop windows (JFrames). They use an entirely different UI mechanism, based on HTML webpages served from a localhost webserver that runs CEF (Chromium Embedded Framework version 3.2272 on Chromium 41 in R2016a). This runs the so-called CEF client (apparently an adaptation of the CefClient sample application that comes with CEF; the relevant Matlab source-code is in %matlabroot%/toolbox/matlab/cefclient/). It uses the DOJO Javascript toolkit for UI controls visualization and interaction, rather than Java Swing as in the existing JFrame figures. I still don’t know if there is a way to combine the seemingly disparate sets of GUIs (namely adding Java-based controls to web-based figures or vice-versa).

Anyway, the important thing to note for my purposes today is that when a new uifigure is created, the above-mentioned Controller object is created, which in turn creates a new matlab.internal.webwindow. The webwindow class (%matlabroot%/toolbox/matlab/cefclient/+matlab/+internal/webwindow.m) is well-documented and easy to follow (although the non camel-cased class name escaped someone’s attention), and allows access to several important figure-level customizations.

The figure’s webwindow reference can be accessed via the Controller‘s Container‘s CEF property:

>> hFig = uifigure('Name','Yair', ...);
>> warning off MATLAB:structOnObject      % suppress warning (yes, we know it's naughty...)
>> figProps = struct(hFig);
 
>> controller = figProps.Controller;      % Controller is a private hidden property of Figure
>> controllerProps = struct(controller);
 
>> container = controllerProps.Container  % Container is a private hidden property of FigureController
container = 
  FigureContainer with properties:
 
    FigurePeerNode: [1x1 com.mathworks.peermodel.impl.PeerNodeImpl]
         Resizable: 1
          Position: [86 39 584 465]
               Tag: ''
             Title: 'Yair'
              Icon: 'C:\Program Files\Matlab\R2016a\toolbox\matlab\uitools\uicomponents\resources\images…'
           Visible: 1
               URL: 'http://localhost:31417/toolbox/matlab/uitools/uifigureappjs/componentContainer.html…'
              HTML: 'toolbox/matlab/uitools/uifigureappjs/componentContainer.html'
     ConnectorPort: 31417
         DebugPort: 0
     IsWindowValid: 1
 
>> win = container.CEF   % CEF is a regular (public) hidden property of FigureContainer
win = 
  webwindow with properties:
 
                             URL: 'http://localhost:31417/toolbox/matlab/uitools/uifigureappjs/component…'
                           Title: 'Yair'
                            Icon: 'C:\Program Files\Matlab\R2016a\toolbox\matlab\uitools\uicomponents\re…'
                        Position: [86 39 584 465]
     CustomWindowClosingCallback: @(o,e)this.Model.hgclose()
    CustomWindowResizingCallback: @(event,data)resizeRequest(this,event,data)
                  WindowResizing: []
                   WindowResized: []
                     FocusGained: []
                       FocusLost: []
                DownloadCallback: []
        PageLoadFinishedCallback: []
           MATLABClosingCallback: []
      MATLABWindowExitedCallback: []
             PopUpWindowCallback: []
             RemoteDebuggingPort: 0
                      CEFVersion: '3.2272.2072'
                 ChromiumVersion: '41.0.2272.76'
                   isWindowValid: 1
               isDownloadingFile: 0
                         isModal: 0
                  isWindowActive: 1
                   isAlwaysOnTop: 0
                     isAllActive: 1
                     isResizable: 1
                         MaxSize: []
                         MinSize: []
 
>> win.URL
ans =
http://localhost:31417/toolbox/matlab/uitools/uifigureappjs/componentContainer.html?channel=/uicontainer/393ed66a-5e34-41f3-8ac0-0b0f3b0738cd&snc=5C2353

An alternative way to get the webwindow is via the list of all webwindows stored by a central webwindowmanager:

webWindows = matlab.internal.webwindowmanager.instance.findAllWebwindows();  % manager method returning an array of all open webwindows
webWindows = matlab.internal.webwindowmanager.instance.windowList;           % equivalent alternative via manager's windowList property

Note that the controller, container and webwindow class objects, like most Matlab MCOS objects, have internal (hidden) properties/methods that you can explore. For example:

>> getundoc(win)
ans = 
                   Channel: [1x1 asyncio.Channel]
       CustomEventListener: [1x1 event.listener]
           InitialPosition: [100 100 600 400]
    JavaScriptReturnStatus: []
     JavaScriptReturnValue: []
     NewWindowBeingCreated: 0
          NewWindowCreated: 1
           UpdatedPosition: [86 39 584 465]
              WindowHandle: 2559756
                    newURL: 'http://localhost:31417/toolbox/matlab/uitools/uifigureappjs/componentContai…'

Using webwindow for figure-level customizations

We can use the methods of this webwindow object as follows:

win.setAlwaysOnTop(true);   % always on top of other figure windows (a.k.a. AOT)
 
win.hide();
win.show();
win.bringToFront();
 
win.minimize();
win.maximize();
win.restore();
 
win.setMaxSize([400,600]);  % enables resizing up to this size but not larger (default=[])
win.setMinSize([200,300]);  % enables resizing down to this size but not smaller (default=[])
win.setResizable(false);
 
win.setWindowAsModal(true);
 
win.setActivateCurrentWindow(false);  % disable interaction with this entire window
win.setActivateAllWindows(false);     % disable interaction with *ALL* uifigure (but not Java-based) windows
 
result = win.executeJS(jsStr, timeout);  % run JavaScript

In addition to these methods, we can set callback functions to various callbacks exposed by the webwindow as regular properties (too bad that some of their names [like the class name itself] don’t follow Matlab’s standard naming convention, in this case by appending “Fcn” or “Callback”):

win.FocusGained = @someCallbackFunc;
win.FocusLost = @anotherCallbackFunc;

In summary, while the possible customizations to Java-based figure windows are more extensive, the webwindow methods appear to cover most of the important ones. Since these functionalities (maximize/minimize, AOT, disable etc.) are now common to both the Java and web-based figures, I really hope that MathWorks will create fully-documented figure properties/methods for them. Now that there is no longer any question whether these features will be supported by the future technology, and since there is no question as to their usefulness, there is really no reason not to officially support them in both figure types. If you feel the same as I do, please let MathWorks know about this – if enough people request this, MathWorks will be more likely to add these features to one of the upcoming Matlab releases.

Warning: the internal implementation is subject to change across releases, so be careful to make your code cross-release compatible whenever you rely on one of Matlab’s internal objects.

Note that I labeled this post as “part 1” – I expect to post additional articles on uifigure customizations in upcoming years.

]]>
https://undocumentedmatlab.com/blog/customizing-uifigures-part-1/feed 5
Faster findjobjhttps://undocumentedmatlab.com/blog/faster-findjobj https://undocumentedmatlab.com/blog/faster-findjobj#comments Mon, 11 Apr 2016 09:18:14 +0000 http://undocumentedmatlab.com/?p=6376
 
Related posts:
  1. FindJObj – find a Matlab component’s underlying Java object The FindJObj utility can be used to access and display the internal components of Matlab controls and containers. This article explains its uses and inner mechanism....
  2. FindJObj GUI – display container hierarchy The FindJObj utility can be used to present a GUI that displays a Matlab container's internal Java components, properties and callbacks....
  3. Customizing Matlab labels Matlab's text uicontrol is not very customizable, and does not support HTML or Tex formatting. This article shows how to display HTML labels in Matlab and some undocumented customizations...
  4. Continuous slider callback Matlab slider uicontrols do not enable a continuous-motion callback by default. This article explains how this can be achieved using undocumented features....
 
]]>
My findjobj utility, created in 2007 and updated over the years, has received wide recognition and is employed by numerous Matlab programs, including a few dozen utilities in the Matlab File Exchange. I am quite proud of this utility and find it extremely useful for customizing Matlab controls in many ways that are impossible using standard Matlab properties. I have shown many examples of this in this blog over the past years.

I am happy to announce that I have just uploaded a new version of findjobj to the Matlab File Exchange, which significantly improves the utility’s performance for the most common use-case of a single input and a single output, namely finding the handle of the underlying Java component (peer) of a certain Matlab control:

>> hButton = uicontrol('String','click me!');
 
>> tic, jButton = findjobj(hButton); toc  % old findjobj
Elapsed time is 1.513217 seconds.
 
>> tic, jButton = findjobj(hButton); toc  % new findjobj
Elapsed time is 0.029348 seconds.

The new findjobj is backward-compatible with the old findjobj and with all prior Matlab releases. It is a drop-in replacement that will significantly improve your program’s speed.

The new version relies on several techniques:

First, as I showed last year, in HG2 (R2014 onward), Matlab uipanels have finally become full-featured Java JPanels, that can be accessed and customized in many interesting manners. More to the point here, we can now directly access the underlying JPanel component handle using the uipanel‘s hidden JavaFrame property (thanks to MathWorks for supplying this useful hook!). The new findjobj version detects this and immediately returns this handle if the user specified a uipanel input.

I still do not know of any direct way to retrieve the underlying Java component’s handle for Matlab uicontrols, this has been a major frustration of mine for quite a few years. So, we need to find the containing Java container in which we will recursively search for the control’s underlying Java handle. In the old version of finjobj, we retrieve the containing figure’s JFrame reference and from it the ContentPane handle, and use this handle as the Java container that is recursively searched. This is quite slow when the figure window is heavily-laden with multiple controls. In the new version, we try to use the specified Matlab uicontrol‘s direct parent, which is very often a uipanel. In this case, we can directly retrieve the panel’s JPanel reference as explained above. This results in a must smaller and faster search since we need to recursively search far fewer controls within the container, compared to the figure’s ContentPane.

In addition, I used a suggestion by blog reader Hannes for a faster recursive search that uses the control’s tooltip rather than its size, position and class. Finally, the search order is reversed to search backward from the last child component, since this is the component that will most often contain the requested control peer.

Feel free to download and use the new findjobj version. The code for the fast variant can be found in lines #190-205 and #3375-3415.

Enjoy!

p.s. – as I explained last week, today’s discussion, and in general anything that has to do with Java peers of GUI controls, only relates to the existing JFrame-based figure windows, not to the new web-based uifigure.

]]>
https://undocumentedmatlab.com/blog/faster-findjobj/feed 11
Graphic sizing in Matlab R2015bhttps://undocumentedmatlab.com/blog/graphic-sizing-in-matlab-r2015b https://undocumentedmatlab.com/blog/graphic-sizing-in-matlab-r2015b#comments Wed, 20 Jan 2016 18:00:31 +0000 http://undocumentedmatlab.com/?p=6244
 
Related posts:
  1. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
  2. Modifying default toolbar/menubar actions The default Matlab figure toolbar and menu actions can easily be modified using simple pure-Matlab code. This article explains how....
  3. FIG files format FIG files are actually MAT files in disguise. This article explains how this can be useful in Matlab applications....
  4. A couple of internal Matlab bugs and workarounds A couple of undocumented Matlab bugs have simple workarounds. ...
 
]]>
I would like to introduce Daniel Dolan of Sandia National Laboratories. Dan works on a variety of data analysis projects in Matlab, and is an active lurker on MATLAB Central. Dan has a habit of finding interesting bugs for the Mac version of Matlab. Today he will discuss graphic sizing in Matlab and important changes that occurred in release R2015b.

Matlab-generated graphics are often not displayed at their requested size. This problem has been known for some time and has a well-known solution: setting the root object’s ScreenPixelsPerInch property to the display’s actual DPI (dots per inch) value. Release R2015b no longer supports this solution, creating problems for publication graphics and general readability.

Physical sizing in R2015a vs. R2015b (click for full-size)

Physical sizing in R2015a vs. R2015b (click for full-size)


Physical sizing

Matlab supports graphic sizing in various physical units: inches, centimeters, and points. For example:

figure; axes('Box','on', 'Units','inches','Position',[0.3 0.3 4 4]);

requests to display an axes having square sizes measuring exactly 4″ (101.6 mm) each. It is evident, however, that the displayed axes is smaller than 4″. The mismatch between requested and physical size depends on the display and operating system — go ahead, try it on your system. The problem is particularly severe on Mac laptops, presumably even worse for those with Retina displays.

The problem is that Matlab cannot determine pixel size, which varies from one display to the other. Generating a figure spanning a particular number of pixels (e.g., 1024 x 768) is easy, but absolute physical units requires a conversion factor called ScreenPixelsPerInch, which is a root property (see related post on setting/getting default graphics property values):

DPI = 110;                             % dots per inch for my 27" Apple Cinema Display
set(0,    'ScreenPixelsPerInch',DPI);  % all releases prior to R2015b
set(groot,'ScreenPixelsPerInch',DPI);  % R2014b through R2015a

DPI values tend to be higher for laptops, usually in the 120-130 range. Retina displays are supposed to be >300 DPI, but I have not been able to test that myself.

There are several ways to determine the correct DPI setting for a particular display. It may be available in the hardware specifications, and it can be calculated from the diagonal size and the number of pixels. Unfortunately these methods are not always reliable. If you really care about physical sizing, the best approach is to actually calibrate your display. There are tools for doing this at Matlab Central, but it’s not hard to do manually:

  • Create a figure.
  • Manually resize the figure to match a convenient width. I often use a piece of US letter paper as 8.5″ guide on the display.
  • Determine the width of the figure in pixels:
    set(gcf,'Units','pixels');
    pos = get(gcf,'Position');
    width = 8.5; % inches
    DPI = pos(3) / width;

I usually apply the DPI settings in my startup file so that Matlab begins with a calibrated display.

What changed in 2015b?

ScreenPixelsPerInch is a read-only property in R2015b, so display calibration no longer works. The following sequence of commands:

figure('Units','inches', 'PaperPositionMode','auto', 'Position',[0 0 4 4]);
set(gcf, 'MenuBar','none', 'ToolBar','none', 'DockControls','off', 'NumberTitle','off');
axes('FontUnits','points', 'FontSize',10);
image

now renders differently in R2015b than does for a calibrated display in R2015a. Differences between the two outputs are shown in the screenshot at the top of this post. The grid behind the figures was rendered at 8.5″ x 8.5″ inches on my display; if your browser’s zoom level isn’t 100%, it may appear larger or smaller.

A side effect of improper graphic sizing is that text is difficult to read — the uncalibrated axes labels are clearly smaller than 10 points. These examples were rendered on ~110 DPI display. Matlab assumes that Macs use 72 DPI (96 DPI on Windows), so graphics appear at 65% of the request size.

The loss of ScreenPixelsPerInch as an adjustable setting strongly affects anyone using Matlab for publication graphics. Scientific and engineering journals are extremly strict about figure widths. With a calibrated screen, figure appear exactly as they will when printed to a file (usually EPS or PDF). Figures are often made as small as possible to and densely packed to save journal space, and accurate sized display helps the author determine legibility. Displaying accurately sized graphics is very difficult in R2015b, which is unfortunate given the many enhancements in this release.

Developers who create graphical interfaces for other users should also care about this change. A common complaint I get is that text and control labels is too small to easily read. Screen calibration deals with this problem, but this option is no longer available.

Where do we go from here?

I reported the above issues to the Mathworks several months ago. It does not appear as a formal bug, but technical support is aware of the problem. The change is part of the “DPI aware” nature of release R2015b. So far I have found no evidence this release is any more aware of pixel size than previous releases, but my experience is limited to non-Retina Macs. I welcome input from users on other operating systems, particularly those with high-resolution displays.

To be fair, correct physical sizing is not an easy across the many platforms that Matlab runs on. Display resolution is particularly tricky when it changes during a Matlab session, such as when computer is connector to projector/television or a laptop is connected to a docking station.

Thankfully, printed graphic sizes are rendered correctly when a figure’s PaperPositionMode property is 'auto'. Many users can (and will) ignore the display problem if they aren’t dealing with strict size requirements and text legibility isn’t too bad. Some users may be willing to periodically print publication figures to externally verify sizing, but this breaks the interactive nature of Matlab figures.

A potential work around is the creating of a new figure class that oversizes figures (as needed) to account for a particular display. I started working on such a class, but the problem is more complicated than one might think:

  • Child objects (axes, uicontrols, etc.) also must be resized if they are based on physical units.
  • Resized objects must be temporarily restored to their original size for printing, and new objects must be tracked whenever they are added.
  • Figure resolution may need to be changed when moving to different computer systems.

These capabilities are quite possible to implement, but this is a complicated solution to problem that was once easy to fix.

Retina displays don’t suffer as badly as one might think from the DPI mismatch. Even though the display specification may be greater than 200 DPI, OS X and/or Matlab must perform some intermediate size transformations. The effective DPI in R2015a is 110-120 for 13-15″ MacBook Pro laptops (at the default resolution). Objected sized with physical units still appear smaller than they should (~72/110), but not as small as I expected (<72/200).

Effect pixel size can also be changed by switching between different monitor scalings. This isn’t entirely surprising, but it can lead to some interesting results because Matlab only reads these settings at startup. Changing the display scaling during a session can cause square figures to appear rectangular. Also, the effective DPI changes for setting: I could reach values of ~60-110 DPI on an Apple Cinema Display.

So where does this leave us? Display calibration was always a finicky matter, but at least in principle one could make graphics appear exactly the same size on two different displays. Now it seems that sizing is completely variable between operation systems, displays, and display settings. For publication graphics, there will almost always be a disconnect between figure size on the screen and the printed output; some iteration may be needed to ensure everything looks right in the finished output. For graphical interfaces, font sizes may need to generated in normalized units and then converted to pixels (to avoid resizing).

Physical accuracy may not be important for non-publication figures, but the issue of text legibility remains. Some text objects–such as axes and tick labels–can easily be resized because the parent axes automatically adjusts itself as needed. Free floating text objects and uincontrols are much more difficult to deal with. Controls are often sized around the extent of their text label, so changing font sizes may require changes to the control position; adjacent controls may overlap after resizing for text clarity. Normalized units partially solve this problem, but their effect on uicontrols is not always desirable: do you really want push buttons to get larger/smaller when the figure is resized?

Can you think of a better workaround to this problem? If so, then please post a comment below. I will be very happy to hear your ideas, as I’m sure others who have high resolution displays would as well.

(cross-reference: CSSM newsgroup post)

Addendum Dec 31, 2016: Dan Dolan just posted a partial workaround on the MathWorks File Exchange. Also see the related recent article on working with non-standard DPI values.

]]>
https://undocumentedmatlab.com/blog/graphic-sizing-in-matlab-r2015b/feed 8
Customizing contour plotshttps://undocumentedmatlab.com/blog/customizing-contour-plots https://undocumentedmatlab.com/blog/customizing-contour-plots#comments Wed, 18 Nov 2015 18:00:55 +0000 http://undocumentedmatlab.com/?p=6075
 
Related posts:
  1. Draggable plot data-tips Matlab's standard plot data-tips can be customized to enable dragging, without being limitted to be adjacent to their data-point. ...
  2. Matlab’s HG2 mechanism HG2 is presumably the next generation of Matlab graphics. This article tries to explore its features....
  3. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
  4. Handle Graphics Behavior HG behaviors are an important aspect of Matlab graphics that enable custom control of handle functionality. ...
 
]]>
One of my clients asked me last week whether it is possible to access and customize individual contour lines and labels in HG2 (Matlab’s new graphics system, R2014+). Today’s post will discuss how this could indeed be done.

Matlab contour plot

Matlab contour plot

In HG1 (R2014a and earlier), contour handles were simple hggroup objects that incorporated text and patch child handles. The contour labels, lines and fill patches could easily be accessed via these child handles (contour lines and fills use the same patch object: the lines are simply the patch edges; fills are their faces). The lines could then be customized, the label strings changed, and the patch faces (fills) recolored:

[X,Y,Z] = peaks;
[C,hContour] = contour(X,Y,Z,20, 'ShowText','on');
hChildren = get(hContour, 'Children');
set(hChildren(1), 'String','Yair', 'Color','b');  % 1st text (contour label)
set(hChildren(end), 'EdgeColor',[0,1,1]);         % last patch (contour line)

The problem is that in HG2 (R2014b onward), contour (and its sibling functions, contourf etc.) return a graphic object that has no accessible children. In other words, hContour.Children returns an empty array:

>> hContour.Children
ans = 
  0x0 empty GraphicsPlaceholder array.
>> allchild(hContour)
ans = 
  0x0 empty GraphicsPlaceholder array.
>> isempty(hContour.Children)
ans =
     1

So how then can we access the internal contour patches and labels?

HG2’s contour object’s hidden properties

Skipping several fruitless dead-ends, it turns out that in HG2 the text labels, lines and fills are stored in undocumented hidden properties called TextPrims, EdgePrims and (surprise, surprise) FacePrims, which hold corresponding arrays of matlab.graphics.primitive.world.Text, matlab.graphics.primitive.world.LineStrip and matlab.graphics.primitive.world.TriangleStrip object handles (the drawnow part is also apparently very important, otherwise you might get errors due to the Prim objects not being ready by the time the code is reached):

>> drawnow;  % very important!
>> hContour.TextPrims  % row array of Text objects
ans = 
  1x41 Text array:
 
  Columns 1 through 14
    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text
  Columns 15 through 28
    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text
  Columns 29 through 41
    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text    Text
 
>> hContour.EdgePrims  % column array of LineStrip objects
ans = 
  20x1 LineStrip array:
 
  ineStrip
  LineStrip
  LineStrip
  ...
 
>> hContour.FacePrims  % column array of TriangleStrip objects (empty if no fill)
ans = 
  0x0 empty TriangleStrip array.

We can now access and customize the individual contour lines, labels and fills:

hContour.TextPrims(4).String = 'Dani';
hContour.TextPrims(7).Visible = 'off';
hContour.TextPrims(9).VertexData = single([-1.3; 0.5; 0]);  % Label location in data units
 
hContour.EdgePrims(2).ColorData = uint8([0;255;255;255]);  % opaque cyan
hContour.EdgePrims(5).Visible = 'off';

Note that the LineStrip objects here are the same as those used for the axes Axles, which I described a few months ago. Any customization that we could do to the axle LineStrips can also be applied to contour LineStrips, and vice versa.

For example, to achieve the appearance of a topographic map, we might want to modify some contour lines to use dotted LineStyle and other lines to appear bold by having larger LineWidth. Similarly, we may wish to hide some labels (by setting their Visible property to ‘off’) and make other labels bold (by setting their Font.Weight property to ‘bold’). There are really numerous customization possibilities here.

Here is a listing of the standard (non-hidden) properties exposed by these objects:

>> get(hContour.TextPrims(1))
        BackgroundColor: []
              ColorData: []
              EdgeColor: []
                   Font: [1x1 matlab.graphics.general.Font]
          FontSmoothing: 'on'
       HandleVisibility: 'on'
                HitTest: 'off'
    HorizontalAlignment: 'center'
            Interpreter: 'none'
                  Layer: 'middle'
              LineStyle: 'solid'
              LineWidth: 1
                 Margin: 1
                 Parent: [1x1 Contour]
          PickableParts: 'visible'
               Rotation: 7.24591082075548
                 String: '-5.1541'
          StringBinding: 'copy'
             VertexData: [3x1 single]
      VerticalAlignment: 'middle'
                Visible: 'on'
 
>> get(hContour.EdgePrims(1))
          AlignVertexCenters: 'off'
             AmbientStrength: 0.3
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'on'
                     HitTest: 'off'
                       Layer: 'middle'
                     LineCap: 'none'
                    LineJoin: 'round'
                   LineStyle: 'solid'
                   LineWidth: 0.5
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 Contour]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: [1 18]
                     Texture: [0x0 GraphicsPlaceholder]
                  VertexData: [3x17 single]
               VertexIndices: []
                     Visible: 'on'
       WideLineRenderingHint: 'software'
 
>> get(hContour.FacePrims(1))
             AmbientStrength: 0.3
             BackFaceCulling: 'none'
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'on'
                     HitTest: 'off'
                       Layer: 'middle'
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 Contour]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: [1 4 13 16 33 37 41 44 51 54 61 64 71 74 87 91 94 103]
                     Texture: [0x0 GraphicsPlaceholder]
            TwoSidedLighting: 'off'
                  VertexData: [3x102 single]
               VertexIndices: []
                     Visible: 'on'

But how did I know these properties existed? The easiest way in this case would be to use my getundoc utility, but we could also use my uiinspect utility or even the plain-ol’ struct function.

p.s. – there’s an alternative way, using the Java bean adapter that is associated with each Matlab graphics object: java(hContour). Specifically, this object apparent has the public method browseableChildren(java(hContour)) which returns the list of all children (in our case, 41 text labels [bean adapters], 20 lines, and a single object holding a ListOfPointsHighlight that corresponds to the regular hidden SelectionHandle property). However, I generally dislike working with the bean adapters, especially when there’s a much “cleaner” way to get these objects, in this case using the regular EdgePrims, FacePrims, TextPrims and SelectionHandle properties. Readers who are interested in Matlab internals can explore the bean adapters using a combination of my getundoc and uiinspect utilities.

So far for the easy part. Now for some more challenging questions:

Customizing the color

First, can we modify the contour fill to have a semi- (or fully-) transparent fill color? – indeed we can:

[~, hContour] = contourf(peaks(20), 10);
drawnow;  % this is important, to ensure that FacePrims is ready in the next line!
hFills = hContour.FacePrims;  % array of TriangleStrip objects
[hFills.ColorType] = deal('truecoloralpha');  % default = 'truecolor'
for idx = 1 : numel(hFills)
   hFills(idx).ColorData(4) = 150;   % default=255
end

Contour plot in HG2, with and without transparency

Contour plot in HG2, with and without transparency

Similar transparency effects can also be applied to the LineStrip and Text objects. A discussion of the various combinations of acceptable color properties can be found here.

Mouse clicks

Next, how can we set a custom context-menu for individual labels and contour lines?

Unfortunately, Text, LineStrip and TriangleStrip objects do not posses a ButtonDownFcn or UIContextMenu property, not even hidden. I tried searching in the internal/undocumented properties, but nothing came up.

Mouse click solution #1

So the next logical step would be to trap the mouse-click event at the contour object level. We cannot simply click the contour and check the clicked object because that would just give us the hContour object handle rather than the individual Text or LineStrip. So the idea would be to set hContour.HitTest='off', in the hope that the mouse click would be registered on the graphic object directly beneath the mouse cursor, namely the label or contour line. It turns out that the labels’ and lines’ HitTest property is ‘off’ by default, so, we also need to set them all to ‘on’:

hContour.HitTest = 'off';
[hContour.TextPrims.HitTest] = deal('on');
[hContour.EdgePrims.HitTest] = deal('on');
[hContour.FacePrims.HitTest] = deal('on');
hContour.ButtonDownFcn = @(h,e)disp(struct(e));

This seemed simple enough, but failed spectacularly: it turns out that because hContour.HitTest='off', mouse clicks are not registered on this objects, and on the other hand we cannot set the ButtonDownFcn on the primitive objects because they don’t have a ButtonDownFcn property!

Who said life is easy?

One workaround is to set the figure’s WindowButtonDownFcn property:

set(gcf, 'WindowButtonDownFcn', @myMouseClickCallback);

Now, inside your myMouseClickCallback function you can check the clicked object. We could use the undocumented builtin hittest(hFig) function to see which object was clicked. Alternatively, we could use the callback eventData‘s undocumented HitObject/HitPrimitive properties (this variant does not require the HitTest property modifications above):

function myMouseClickCallback(hFig, eventData)
   hitPrimitive = hittest(hFig);  % undocumented function
 
   hitObject    = eventData.HitObject;     % undocumented property => returns a Contour object (=hContour)
   hitPrimitive = eventData.HitPrimitive;  % undocumented property => returns a Text or LineStrip object
   hitPoint     = eventData.Point;         % undocumented property => returns [x,y] pixels from figure's bottom-left corner
 
   if strcmpi(hFig.SelectionType,'alt')  % right-click
      if isa(hitPrimitive, 'matlab.graphics.primitive.world.Text')  % label
         displayTextContextMenu(hitPrimitive, hitPoint)
      elseif isa(hitPrimitive, 'matlab.graphics.primitive.world.LineStrip')  % contour line
         displayLineContextMenu(hitPrimitive, hitPoint)
      elseif isa(hitPrimitive, 'matlab.graphics.primitive.world.TriangleStrip')  % contour fill
         displayFillContextMenu(hitPrimitive, hitPoint)
      else
         ...
      end
   end
end
Mouse click solution #2

A totally different solution is to keep the default hContour.HitTest='on' (and the primitives’ as ‘off’) and simply query the contour object’s ButtonDownFcn callback’s eventData‘s undocumented Primitive property:

hContour.ButtonDownFcn = @myMouseClickCallback;

And in the callback function:

function myMouseClickCallback(hContour, eventData)
   hitPrimitive = eventData.Primitive;  % undocumented property => returns a Text or LineStrip object
   hitPoint     = eventData.IntersectionPoint;  % [x,y,z] in data units
 
   hFig = ancestor(hContour, 'figure');
   if strcmpi(hFig.SelectionType,'alt')  % right-click
      if isa(hitPrimitive, 'matlab.graphics.primitive.world.Text')  % label
         displayTextContextMenu(hitPrimitive, hitPoint)
      elseif isa(hitPrimitive, 'matlab.graphics.primitive.world.LineStrip')  % contour line
         displayLineContextMenu(hitPrimitive, hitPoint)
      elseif isa(hitPrimitive, 'matlab.graphics.primitive.world.TriangleStrip')  % contour fill
         displayFillContextMenu(hitPrimitive, hitPoint)
      else
         ...
      end
   end
end

This article should be a good start in how to code the displayTextContextMenu etc. functions to display a context menu.

Customizations reset

Finally, there are apparently numerous things that cause our customized labels and lines to reset to their default appearance: resizing, updating contour properties etc. To update the labels in all these cases in one place, simply listen to the undocumented MarkedClean event:

addlistener(hContour, 'MarkedClean', @updateLabels);

Where updateLabels is a function were you set all the new labels.

Prediction about forward compatibility

I am marking this article as “High risk of breaking in future Matlab versions“, not because of the basic functionality (being important enough I don’t presume it will go away anytime soon) but because of the property names: TextPrims, EdgePrims and FacePrims don’t seem to be very user-friendly property names. So far MathWorks has been very diligent in making its object properties have meaningful names, and so I assume that when the time comes to expose these properties, they will be renamed (perhaps to TextHandles, EdgeHandles and FaceHandles, or perhaps LabelHandles, LineHandles and FillHandles). For this reason, even if you find out in some future Matlab release that TextPrims, EdgePrims and FacePrims don’t exist, perhaps they still exist and simply have different names.

]]>
https://undocumentedmatlab.com/blog/customizing-contour-plots/feed 8
Adding dynamic properties to graphic handleshttps://undocumentedmatlab.com/blog/adding-dynamic-properties-to-graphic-handles https://undocumentedmatlab.com/blog/adding-dynamic-properties-to-graphic-handles#comments Wed, 16 Sep 2015 17:26:44 +0000 http://undocumentedmatlab.com/?p=6006
 
Related posts:
  1. New information on HG2 More information on Matlab's new HG2 object-oriented handle-graphics system...
  2. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
  3. uiundo – Matlab’s undocumented undo/redo manager The built-in uiundo function provides easy yet undocumented access to Matlab's powerful undo/redo functionality. This article explains its usage....
  4. Matlab’s HG2 mechanism HG2 is presumably the next generation of Matlab graphics. This article tries to explore its features....
 
]]>
A client recently asked me to extend one of Matlab’s built-in graphic containers (uiflowcontainer in this specific case) with automatic scrollbars that would enable the container to act as a scroll-panel. The basic idea would be to dynamically monitor the container’s contents and when it is determined that they overflow the container’s boundaries, then attach horizontal/vertical scrollbars to enable scrolling the contents into view:

Scrollable Matlab container

Scrollable Matlab container

This may sound simple, but there are actually quite a few undocumented hacks that make this possible, including listening to ObjectChildAdded/ObjectChildRemoved events, location/size/visibility events, layout changes etc. Maybe I’ll blog about it in some future article.

Today’s post is focused on a specific aspect of this project, attaching dynamic properties to the builtin uiflowcontainer, that would enable users to modify the container’s properties directly, as well as control aspects of the scrolling using the new properties: handles to the parent container, as well as the horizontal and vertical scrollbars, and even a new refresh() method.

The “textbook” approach to this would naturally be to create a new class that extends (inherits) uiflowcontainer and includes these new properties and methods. Unfortunately, for some reason that escapes my understanding, MathWorks saw fit to make all of its end-use graphic object classes Sealed, such that they cannot be extended by users. I did ask for this to be changed long ago, but the powers that be apparently decided that it’s better this way.

So the fallback would be to create our own dedicated class having all the new properties as well as those of the original container, and ensure that all the property values are synchronized in both directions. This is probably achievable, if you have a spare few days and a masochistic state of mind. Being the lazy bum and authority-rebel that I am, I decided to take an alternate approach that would simply add my new properties to the built-in container handle. The secret lies in the undocumented function schema.prop (for HG1, R2014a and older) and the fully-documented addprop function (for HG2, R2014b and newer).

In the examples below I use a panel, but this mechanism works equally well on any Matlab HG object: axes, lines, uicontrols, figures, etc.

HG2 – addprop function

The addprop function is actually a public method of the dynamicprops class. Both the dynamicprops class as well as its addprop function are fully documented. What is NOT documented, as far as I could tell, is that all of Matlab’s builtin handle graphics objects indirectly inherit dynamicprops, via matlab.graphics.Graphics, which is a high-level superclass for all HG objects. The bottom line is that we can dynamically add run-time properties to any HG object, without affecting any other object. In other words, the new properties will only be added to the handles that we specifically request, and not to any others. This suits me just fine:

hProp = addprop(hPanel, 'hHorizontalScrollBar');
hPanel.hHorizontalScrollBar = hMyScrollbar;
hProp.SetAccess = 'private';  % make this property read-only

The new property hHorizontalScrollBar is now added to the hPanel handle, and can be accessed just like any other read-only property. For example:

>> get(hPanel, 'hHorizontalScrollBar')
ans = 
    JavaWrapper

>> hPanel.hHorizontalScrollBar
ans = 
    JavaWrapper

>> hPanel.hHorizontalScrollBar = 123
You cannot set the read-only property 'hHorizontalScrollBar' of UIFlowContainer.

Adding new methods is more tricky, since we do not have a corresponding addmethod function. The trick I used was to create a new property having the requested new method’s name, and set its read-only value to a handle of the requested function. For example:

hProp = addprop(hPanel, 'refresh');
hPanel.refresh = @myRefreshFunc;
hProp.SetAccess = 'private';  % make this property read-only

We can then invoke the new refresh “method” using the familiar dot-notation:

hPanel.refresh();

Note: if you ever need to modify the initial value in your code, you should revert the property’s SetAccess meta-property to 'public' before Matlab will enable you to modify the value:

try
    % This will raise an exception if the property already exists
    hProp = addprop(hPanel, propName);
catch
    % Property already exists - find it and set its access to public
    hProp = findprop(hPanel, propName);
    hProp.SetAccess = 'public';
end
hPanel.(propName) = newValue;

HG1 – schema.prop function

In HG1 (R2014a and earlier), we can use the undocumented schema.prop function to add a new property to any HG handle (which is a numeric value in HG1). Donn Shull wrote about schema.prop back in 2011, as part of his series of articles on UDD (Unified Data Dictionary, MCOS’s precursor). In fact, schema.prop is so useful that it has its own blog tag here and appears in no less than 15 separate articles (excluding today). With HG2’s debut 2 years ago, MathWorks tried very hard to rid the Matlab code corpus of all the legacy schema-based, replacing most major functionalities with MCOS-based HG2 code. But so far it has proven impossible to get rid of schema completely, and so schema code is still used extensively in Matlab to this day (R2015b). Search your Matlab path for “schema.prop” and see for yourself.

Anyway, the basic syntax is this:

hProp = schema.prop(hPanel, propName, 'mxArray');

The 'mxArray' specifies that the new property can accept any data type. We can limit the property to only accept certain types of data by specifying a less-generic data type, among those recognized by UDD (details).

Note that the meta-properties of the returned hProp are somewhat different from those of HG2’s hProp. Taking this into account, here is a unified function that adds/updates a new property (with optional initial value) to any HG1/HG2 object:

function addProp(hObject, propName, initialValue, isReadOnly)
    try
        hProp = addprop(hObject, propName);  % HG2
    catch
        try
            hProp = schema.prop(hObject, propName, 'mxArray');  % HG1
        catch
            hProp = findprop(hObject, propName);
        end
    end
    if nargin > 2
        try
            hProp.SetAccess = 'public';  % HG2
        catch
            hProp.AccessFlags.PublicSet = 'on';  % HG1
        end
        hObject.(propName) = initialValue;
    end
    if nargin > 3 && isReadOnly
        try
            % Set the property as read-only
            hProp.SetAccess = 'private';  % HG2
        catch
            hProp.AccessFlags.PublicSet = 'off';  % HG1
        end
    end
end
]]>
https://undocumentedmatlab.com/blog/adding-dynamic-properties-to-graphic-handles/feed 4
Callback functions performancehttps://undocumentedmatlab.com/blog/callback-functions-performance https://undocumentedmatlab.com/blog/callback-functions-performance#comments Wed, 09 Sep 2015 23:36:25 +0000 http://undocumentedmatlab.com/?p=5996
 
Related posts:
  1. Matlab layout managers: uicontainer and relatives Matlab contains a few undocumented GUI layout managers, which greatly facilitate handling GUI components in dynamically-changing figures....
  2. Panel-level uicontrols Matlab's uipanel contains a hidden handle to the title label, which can be modified into a checkbox or radio-button control...
  3. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
  4. uicontextmenu performance Matlab uicontextmenus are not automatically deleted with their associated objects, leading to leaks and slow-downs. ...
 
]]>
Matlab enables a variety of ways to define callbacks for asynchronous events (such as interactive GUI actions or timer invocations). We can provide a function handle, a cell-array (of function handle and extra parameters), and in some cases also a string that will be eval‘ed in run-time. For example:

hButton = uicontrol(..., 'Callback', @myCallbackFunc);  % function handle
hButton = uicontrol(..., 'Callback', {@myCallbackFunc,data1,data2});  % cell-array
hButton = uicontrol(..., 'Callback', 'disp clicked!');  % string to eval

The first format, function handle, is by far the most common in Matlab code. This format has two variant: we can specify the direct handle to the function (as in @myCallbackFunc), or we could use an anonymous function, like this:

hButton = uicontrol(..., 'Callback', @(h,e) myCallbackFunc(h,e));  % anonymous function handle

All Matlab callbacks accept two input args by default: the control’s handle (hButton in this example), and a struct or object that contain the event’s data in internal fields. In our anonymous function variant, we therefore defined a function that accepts two input args (h,e) and calls myCallbackFunc(h,e).

These two variants are functionally equivalent:

hButton = uicontrol(..., 'Callback', @myCallbackFunc);             % direct function handle
hButton = uicontrol(..., 'Callback', @(h,e) myCallbackFunc(h,e));  % anonymous function handle

In my experience, the anonymous function variant is widely used – I see it extensively in many of my consulting clients’ code. Unfortunately, there could be a huge performance penalty when using this variant compared to a direct function handle, which many people are simply not aware of. I believe that even many MathWorkers are not aware of this, based on a recent conversation I’ve had with someone in the know, as well as from the numerous usage examples in internal Matlab code: see the screenshot below for some examples; there are numerous others scattered throughout the Matlab code corpus.

Part of the reason for this penalty not being well known may be that Matlab’s Profiler does not directly attribute the overheads. Here is a typical screenshot:

Profiling anonymous callback function performance

Profiling anonymous callback function performance

In this example, a heavily-laden GUI figure window was closed, triggering multiple cleanup callbacks, most of them belonging to internal Matlab code. Closing the figure took a whopping 8 secs. As can be seen from the screenshot, the callbacks themselves only take ~0.66 secs, and an additional 7.4 secs (92% of the total) is unattributed to any specific line. Think about it for a moment: we can only really see what’s happening in 8% of the time – the Profiler provides no clue about the root cause of the remaining 92%.

The solution in this case was to notice that the callback was defined using an anonymous function, @(h,e)obj.tableDeletedCallbackFcn(e). Changing all such instances to @obj.tableDeletedCallbackFcn (the function interface naturally needed to change to accept h as the first input arg) drastically cut the processing time, since direct function handles do not carry the same performance overheads as anonymous functions. In this specific example, closing the figure window now became almost instantaneous (<1 sec).

Conclusions

There are several morals that I think can be gained from this:

  1. When we see unattributed time in the Profiler summary report, odds are high that this is due to function-call overheads. MathWorks have significantly reduced such overheads in the new R2015b (released last week), but anonymous [and to some degree also class methods] functions still carry a non-negligible invocation overheads that should be avoided if possible, by using direct [possibly non-MCOS] functions.
  2. Use direct function handles rather than anonymous function handles, wherever possible
  3. In the future, MathWorks will hopefully improve Matlab’s new engine (“LXE”) to automatically identify cases of @(h,e)func(h,e) and replace them with faster calls to @func, but in any case it would be wise to manually make this change in our code today. It would immediately improve readability, maintainability and performance, while still being entirely future-compatible.
  4. In the future, MathWorks may also possibly improve the overheads of anonymous function invocations. This is more tricky than the straight-forward lexical substitution above, because anonymous functions need to carry the run-time workspace with them. This is a little known and certainly very little-used fact, which means that in practice most usage patterns of anonymous functions can be statically analyzed and converted into much faster direct function handles that carry no run-time workspace info. This is indeed tricky, but it could directly improve performance of many Matlab programs that naively use anonymous functions.
  5. Matlab’s Profiler should really be improved to provide more information about unattributed time spent in internal Matlab code, to provide users clues that would help them reduce it. Some information could be gained by using the Profiler’s -detail builtin input args (which was documented until several releases ago, but then apparently became unsupported). I think that the Profiler should still be made to provide better insights in such cases.

Oh, and did I mention already the nice work MathWorks did with 15b’s LXE? Matlab’s JIT replacement was many years in the making, possibly since the mid 2000’s. We now see just the tip of the iceberg of this new engine: I hope that additional benefits will become apparent in future releases.

For a definitive benchmark of Matlab’s function-call overheads in various variants, readers are referred to Andrew Janke’s excellent utility (with some pre-15b usage results and analysis). Running this benchmark on my machine shows significant overhead reduction in function-call overheads in 15b in many (but not all) invocation types.

For those people wondering, 15b’s LXE does improve HG2’s performance, but just by a small bit – still not enough to offset the large performance hit of HG2 vs. HG1 in several key aspects. MathWorks is actively working to improve HG2’s performance, but unfortunately there is still no breakthrough as of 15b.

Additional details on various performance issues related to Matlab function calls (and graphics and anything else in Matlab) can be found in my recent book, Accelerating MATLAB Performance.

]]>
https://undocumentedmatlab.com/blog/callback-functions-performance/feed 11
Using linkaxes vs. linkprophttps://undocumentedmatlab.com/blog/using-linkaxes-vs-linkprop https://undocumentedmatlab.com/blog/using-linkaxes-vs-linkprop#comments Wed, 22 Jul 2015 20:30:04 +0000 http://undocumentedmatlab.com/?p=5928
 
Related posts:
  1. Multi-column (grid) legend This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
  2. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  3. FIG files format FIG files are actually MAT files in disguise. This article explains how this can be useful in Matlab applications....
  4. Handle Graphics Behavior HG behaviors are an important aspect of Matlab graphics that enable custom control of handle functionality. ...
 
]]>
One of my clients recently asked me to solve a very peculiar problem: He had several axes and was using Matlab’s builtin linkaxes function to link their axis limits. However, it didn’t behave quite the way that he expected. His axes were laid out as 2×2 subplots, and he wanted the two columns to be independently linked in the X axis, and the two rows to be independently linked in the Y axis:

% Prepare the axes
ax(1,1) = subplot(2,2,1); 
ax(1,2) = subplot(2,2,2); 
ax(2,1) = subplot(2,2,3); 
ax(2,2) = subplot(2,2,4);
 
% Plot something
x = 0 : 0.01 : 10;
line(x, sin(x),   'Parent',ax(1,1));
line(x, sin(2*x), 'Parent',ax(1,2));
line(x, cos(x),   'Parent',ax(2,1));
line(x, cos(5*x), 'Parent',ax(2,2));
 
% Link the relevant axes
linkaxes(ax(:,1),'x');  % left column
linkaxes(ax(:,2),'x');  % right column
linkaxes(ax(1,:),'y');  % top row
linkaxes(ax(2,:),'y');  % bottom row

The problem was that the plots didn’t behave as expected: when zooming in on the bottom-left axes, for example, only the bottom-right axes was updated (Y-limits synced), whereas the top-left axes’ X-limits remained unchanged:

Badly-synced axes limits

Badly-synced axes limits


Apparently, the second set of two linkaxes commands (to sync the rows’ Y-limits) overrode the first set of two linkaxes commands (to sync the columns’ X-limits).

Analysis

The reason for this unexpected behavior is that under the hood, linkaxes attaches property-change listeners to the corresponding axes, and stores these listeners in the axes’ hidden ApplicationData property (which is typically accessible via the getappdata / setappdata / isappdata / rmappdata set of functions). Specifically, up to a certain Matlab release (R2013b?), the listeners were placed in a field called ‘listener__’, and since then in a field called ‘graphics_linkaxes’. In either case, the field name was constant.

Therefore, when we placed the first set of linkaxes commands, the axes were correctly synced vertically (ax(1,1) with ax(2,1) in their X-limits, and similarly ax(1,2) with ax(2,2)). But when we placed the second set of linkaxes commands, the internal field in the axes’ ApplicationData property was overriden with the new listeners (that synced the rows’ Y-limits).

It so happens that Matlab listeners have a very nasty feature of being deleted when they are no longer referenced anywhere (within a workspace variable or object property). So when we overrode the first set of listener handles, we effectively deleted them, as if they were never set in the first place.

Some people may possibly complain about both issues at this point:

  • That Matlab listeners get deleted so easily without so much as a console warning, and certainly against naive intuition.
  • That repeated calls to linkaxes should override (rather than complement) each other.

As a side note, the addlistener function creates a listener and then persists it in the object’s hidden AutoListeners__ property. However, unlike the linkaxes behavior, addlistener‘s listener handles are always appended to AutoListeners__‘s contents, rather than replacing it. This ensures that all listeners are accessible and active until their container is deleted or they are specifically modified/removed. I wish that linkaxes used this mechanism, rather than its current ApplicationData one.

Workaround: linkprop

Luckily, there is a very easy and simple workaround, namely to use linkprop rather than linkaxes. The linkprop function is a lower-level function that creates a property-change listener that syncs corresponding properties in any specified array of object handles. In fact, linkaxes uses linkprop in order to create the necessary listeners. In our case, we can use linkprop directly, to independently attach such listeners to the axes’ XLim and YLim properties. We just need to ensure that all these listeners remain accessible to Matlab throughout the corresponding objects’ life-cycle. This is easily done using ApplicationData, as is done by linkaxes.m but in a smarter manner that does not override the previous values. The benefit of this is that when the axes are deleted, then so are the listeners; as long as the axes are accessible then so are the listeners. We just need to ensure that we don’t override these listener values:

setappdata(ax(1,1), 'YLim_listeners', linkprop(ax(1,:),'YLim')); 
setappdata(ax(2,1), 'YLim_listeners', linkprop(ax(2,:),'YLim'));
setappdata(ax(1,1), 'XLim_listeners', linkprop(ax(:,1),'XLim'));
setappdata(ax(1,2), 'XLim_listeners', linkprop(ax(:,2),'XLim'));

This results in the expected behavior:

properly-linked axes

properly-linked axes

Conclusions

The design decision by MathWorks to automatically delete Matlab listeners as soon as their reference count is zeroed and they get garbage-collected, causes a myriad of unexpected run-time behaviors, one of which is exemplified in today’s post on linkaxes. This would still have not caused any problem had the developers of linkaxes been aware of this listener feature and taken care to store the linked listener handles in an accumulating repository (e.g., adding the listener handle to an array of existing handles, rather than replacing a scalar handle).

Luckily, now that we know how Matlab listeners behave, we can easily identify abnormal behavior that results from listener handles going out of scope, and can easily take steps to persist the handles somewhere, so that they will remain active.

I wish to stress here that the listeners’ limited scope is fully documented in several places in the documentation (e.g., here as well as the linkprop doc page). The non-additive behavior of linkaxes is also documented, albeit in an almost-invisible footnote on its doc-page.

However, I humbly contend that the fact that these behaviors are documented doesn’t meant that they are correct. After all, figure windows or timers aren’t deleted when their handle goes out of scope, are they? At the very least, I hope that MathWorks will improve the relevant doc pages, to highlight these non-intuitive behaviors, and in the case of linkaxes to present a linkprop usage example as a workaround.

If you are interested in the topic of Matlab listeners, note that I’ve written quite a few listener-related posts over the years (about property-change listeners as well as event listeners). I urge you to take a look at the list of related articles presented below, or to use the search box at the top of the page.

]]>
https://undocumentedmatlab.com/blog/using-linkaxes-vs-linkprop/feed 5
Persisting transparent colors in HG2https://undocumentedmatlab.com/blog/persisting-transparent-colors-in-hg2 https://undocumentedmatlab.com/blog/persisting-transparent-colors-in-hg2#comments Wed, 03 Jun 2015 18:00:49 +0000 http://undocumentedmatlab.com/?p=5820
 
Related posts:
  1. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
  2. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
  3. Customizing axes rulers HG2 axes can be customized in numerous useful ways. This article explains how to customize the rulers. ...
  4. Customizing axes part 2 Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
 
]]>
Several months ago, I showed how we can set semi- and fully-transparent colors in HG2 (Matlab’s new graphics engine, starting in R2014b) for multiple graphic objects, including plot lines, plot markers, and area charts:

hLine = plot([0,150], [-0.5,0.5], 'r');
box off; hold on;
ydata = sin(0:0.1:15);
hArea = area(ydata);
drawnow; pause(0.05);  % this is important!
hArea.Face.ColorType = 'truecoloralpha';
hArea.Face.ColorData(4) = 40;  % 40/255 = 0.16 opacity = 84% transparent

Unfortunately, these settings are automatically overridden by Matlab when the figure is printed, saved, or exported:

% These commands result in an opaque (non-transparent) plot
print(gcf);
saveas(gcf, 'plot.png');

Transparent area plot (ok)   Opaque area plot (not ok)

Area plot: transparent (ok) and opaque (not ok)


In some cases, the settings are lost even when the figure or axes is resized, or properties (e.g., Box) are changed. This is evident, for example, when the hLine plot line is not drawn, only the area plot.

The solution of one blog reader here was to simply set the undocumented color transparency settings at the very end of the graphics set-up. However, as noted, this still does not answer the need to preserve the color settings when the figure is printed, saved, exported, or resized, or when axes properties change.

Another reader, Richard de Garis of Collins Capital, found an undocumented feature that seems to solve the problem for good. It turns out that the solution is simply to set the color to a “legal” (documented, non-transparent) color before setting the transparency values:

hArea = area(ydata);
hArea.FaceColor = 'b';  % or any other non-transparent colordrawnow; pause(0.05);  % this is important!
hArea.Face.ColorType = 'truecoloralpha';
hArea.Face.ColorData(4) = 40;  % 40/255 = 0.16 opacity = 84% transparent

Now the transparency settings are preserved, even when the figure is printed, saved, exported, resized etc.

The obvious explanation is that by manually updating the graphic object’s FaceColor, Matlab automatically updates the hidden property FaceColorMode from ‘auto’ to ‘manual’. This signals the graphics engine not to override the Face‘s color when such an update would otherwise be called for. The mechanism of having a <PropName>Mode property associated with the <PropName> was used in HG1 (Matlab’s previous Matlab graphics engine, up to R2014a). In HG2, more properties have added such associated *Mode properties. In most cases, these additional *Mode properties are hidden, as FaceColorMode is. I find this justified, because in most cases users shouldn’t update these properties, and should let Matlab handle the logic.

Unfortunately, this explanation is apparently false, as evident by the fact that setting FaceColorMode from ‘auto’ to ‘manual’ does not have the same desired effect. So for now I don’t know how to explain this phenomenon. At least we know it works, even if we don’t fully understand why [yet]. Oh well, I guess we can’t win ’em all…

Have you discovered and used some other interesting undocumented feature of HG2? If so, please share it in a comment below, or email me the details.

]]>
https://undocumentedmatlab.com/blog/persisting-transparent-colors-in-hg2/feed 2
Undocumented HG2 graphics eventshttps://undocumentedmatlab.com/blog/undocumented-hg2-graphics-events https://undocumentedmatlab.com/blog/undocumented-hg2-graphics-events#comments Wed, 27 May 2015 17:20:10 +0000 http://undocumentedmatlab.com/?p=5806
 
Related posts:
  1. Matlab’s HG2 mechanism HG2 is presumably the next generation of Matlab graphics. This article tries to explore its features....
  2. Introduction to UDD UDD classes underlie many of Matlab's handle-graphics objects and functionality. This article introduces these classes....
  3. Multi-column (grid) legend This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
  4. Draggable plot data-tips Matlab's standard plot data-tips can be customized to enable dragging, without being limitted to be adjacent to their data-point. ...
 
]]>
R2014b brought a refreshing new graphics engine and appearance, internally called HG2 (the official marketing name is long and impossible to remember, and certainly not as catchy). I’ve already posted a series of articles about HG2. Today I wish to discuss an undocumented aspect of HG2 that I’ve encountered several times over the past months, and most recently today. The problem is that while in the previous HG1 system (R2014a and earlier) we could add property-change listener callbacks to practically any graphics object, this is no longer true for HG2. Many graphics properties, that are calculated on-the-fly based on other property values, cannot be listened-to, and so we cannot attach callbacks that trigger when their values change.

Property-change listeners in HG1

Take for example my post about setting axes tick labels format from 3 years ago: the idea there was to attach a Matlab callback function to the PropertyPostSet event of the XTick, YTick and/or ZTick properties, so that when they change their values (upon zoom/pan/resize), the corresponding tick-labels would be reformatted based on the user-specified format:

Formatted labels, automatically updated Formatted labels, automatically updated

Formatted labels, automatically updated


A simple HG1 usage might look as follows:

addlistener(handle(hAxes), 'YTick', 'PostSet', @reformatTickLabels);
 
function reformatTickLabels(hProperty, eventData)
    try
        hAxes = eventData.AffectedObject;
    catch
        hAxes = ancestor(eventData.Source,'Axes');
    end
    tickValues = get(hAxes, 'YTick');
    tickLabels = arrayfun(@(x)(sprintf('%.1fV',x)), tickValues, 'UniformOutput',false);
    set(hAxes, 'YTickLabel', tickLabels)
end

I prepared a utility called ticklabelformat that automates much of the set-up above. Feel free to download this utility from the Matlab File Exchange. Its usage syntax is as follows:

ticklabelformat(gca,'y','%.6g V')  % sets y axis on current axes to display 6 significant digits
ticklabelformat(gca,'xy','%.2f')   % sets x & y axes on current axes to display 2 decimal digits
ticklabelformat(gca,'z',@myCbFcn)  % sets a function to update the Z tick labels on current axes
ticklabelformat(gca,'z',{@myCbFcn,extraData})  % sets an update function as above, with extra data

Property-change listeners in HG2

Unfortunately, this fails in HG2 when trying to listen to automatically-recalculated (non-Observable) properties such as the Position or axes Tick properties. We can only listen to non-calculated (Observable) properties such as Tag or YLim. Readers might think that this answers the need, since the ticks change when the axes limits change. This is true, but does not cover all cases. For example, when we resize/maximize the figure, Matlab may decide to modify the displayed ticks, although the axes limits remain unchanged.

So we need to have a way to monitor changes even in auto-calculated properties. Luckily this can be done by listening to a set of new undocumented HG2 events. It turns out that HG2’s axes (matlab.graphics.axis.Axes objects) have no less than 17 declared events, and 14 of them are hidden in R2015a:

>> events(gca)   % list the non-hidden axes events
Events for class matlab.graphics.axis.Axes:
    ObjectBeingDestroyed
    PropertyAdded
    PropertyRemoved
 
>> mc = metaclass(gca)
mc = 
  GraphicsMetaClass with properties:
                     Name: 'matlab.graphics.axis.Axes'
              Description: 'TODO: Fill in Description'
      DetailedDescription: ''
                   Hidden: 0
                   Sealed: 1
                 Abstract: 0
              Enumeration: 0
          ConstructOnLoad: 1
         HandleCompatible: 1
          InferiorClasses: {0x1 cell}
        ContainingPackage: [1x1 meta.package]
             PropertyList: [414x1 meta.property]
               MethodList: [79x1 meta.method]
                EventList: [17x1 meta.event]    EnumerationMemberList: [0x1 meta.EnumeratedValue]
           SuperclassList: [7x1 meta.class]
 
>> mc.EventList(10)
ans = 
  event with properties:
                   Name: 'MarkedClean'
            Description: 'description'
    DetailedDescription: 'detailed description'
                 Hidden: 1
           NotifyAccess: 'public'
           ListenAccess: 'public'
          DefiningClass: [1x1 matlab.graphics.internal.GraphicsMetaClass]
 
>> [{mc.EventList.Name}; ...
    {mc.EventList.ListenAccess}; ...
    arrayfun(@mat2str, [mc.EventList.Hidden], 'Uniform',false)]'
ans = 
    'LocationChanged'             'public'       'true' 
    'SizeChanged'                 'public'       'true' 
    'ClaReset'                    'public'       'true' 
    'ClaPreReset'                 'public'       'true' 
    'Cla'                         'public'       'true' 
    'ObjectBeingDestroyed'        'public'       'false'  % not hidden
    'Hit'                         'public'       'true' 
    'LegendableObjectsUpdated'    'public'       'true' 
    'MarkedDirty'                 'public'       'true' 
    'MarkedClean'                 'public'       'true' 
    'PreUpdate'                   'protected'    'true' 
    'PostUpdate'                  'protected'    'true' 
    'Error'                       'public'       'true' 
    'Reparent'                    'public'       'true' 
    'Reset'                       'public'       'true' 
    'PropertyAdded'               'public'       'false'  % not hidden
    'PropertyRemoved'             'public'       'false'  % not hidden

Similar hidden events exist for all HG2 graphics objects. The MarkedDirty and MarkedClean events are available for practically all graphic objects. We can listen to them (luckily, their ListenAccess meta-property is defined as ‘public’) to get a notification whenever the corresponding object (axes, or any other graphics component such as a plot-line or axes ruler etc.) is being redrawn. We can then refresh our own properties. It makes sense to attach such callbacks to MarkedClean rather than MarkedDirty, because the property values are naturally stabled and reliable only after MarkedClean. In some specific cases, we might wish to listen to one of the other events, which luckily have meaningful names.

For example, in my ticklabelformat utility I’ve implemented the following code (simplified here for readability – download the utility to see the actual code), which listens to the MarkedClean event on the axes’ YRuler property:

try
    % HG1 (R2014a or older)
    hAx = handle(hAxes);
    hProp = findprop(hAx, 'YTick');
    hListener = handle.listener(hAx, hProp, 'PropertyPostSet', @reformatTickLabels);
    setappdata(hAxes, 'YTickListener', hListener);  % must be persisted in order to remain in effect
catch
    % HG2 (R2014b or newer)
    addlistener(hAx, 'YTick', 'PostSet', @reformatTickLabels);
 
    % *Tick properties don't trigger PostSet events when updated automatically in R2014b
    %addlistener(hAx, 'YLim', 'PostSet', @reformatTickLabels);  % this solution does not cover all use-cases
    addlistener(hAx.YRuler, 'MarkedClean', @reformatTickLabels);
end
 
% Adjust tick labels now
reformatTickLabels(hAxes);

In some cases, the triggered event might pass some useful information in the eventData object that is passed to the callback function as the second input parameter. This data may be different for different events, and is also highly susceptible to changes across Matlab releases, so use with care. I believe that the event names themselves (MarkedClean etc.) are less susceptible to change across Matlab releases, but they might.

Performance aspects

The MarkedClean event is triggered numerous times, from obvious triggers such as calling drawnow to less-obvious triggers such as resizing the figure or modifying a plot-line’s properties. We therefore need to be very careful that our callback function is (1) non-reentrant, (2) is not active too often (e.g., more than 5 times per sec), (3) does not modify properties unnecessarily, and in general (4) executes as fast as possible. For example:

function reformatTickLabels(hProperty, eventData)
    persistent inCallback
    if ~isempty(inCallback),  return;  end
    inCallback = 1;  % prevent callback re-entry (not 100% fool-proof)
 
    % Update labels only every 0.2 secs or more
    persistent lastTime
    try
        tnow = datenummx(clock);  % fast
    catch
        tnow = now;  % slower
    end
    ONE_SEC = 1/24/60/60;
    if ~isempty(lastTime) && tnow - lastTime < 0.2*ONE_SEC
        inCallback = [];  % re-enable callback
        return;
    end
    lastTime = tnow;
 
    % This is the main callback logic
    try
        hAxes = eventData.AffectedObject;
    catch
        hAxes = ancestor(eventData.Source,'Axes');
    end
    prevTickValues = getappdata(hAxes, 'YTick');
    tickValues = get(hAxes, 'YTick');
    if ~isequal(prevTickValues, tickValues)
        tickLabels = arrayfun(@(x)(sprintf('%.1fV',x)), tickValues, 'UniformOutput',false);
        set(hAxes, 'YTickLabel', tickLabels)
    end
 
    inCallback = [];  % re-enable callback
end

Unfortunately, it seems that retrieving some property values (such as the axes’s YTick values) may by itself trigger the MarkedClean event for some reason that eludes my understanding (why should merely getting the existing values modify the graphics in any way?). Adding callback re-entrancy checks as above might alleviate the pain of such recursive callback invocations.

A related performance aspect is that it could be better to listen to a sub-component’s MarkedClean than to the parent axes’ MarkedClean, which might be triggered more often, for changes that are entirely unrelated to the sub-component that we wish to monitor. For example, if we only monitor YRuler, then it makes no sense to listen to the parent axes’ MarkedClean event that might trigger due to a change in the XRuler.

In some cases, it may be better to listen to specific events rather than the all-encompassing MarkedClean. For example, if we are only concerned about changes to the Position property, we should listen to the LocationChanged and/or SizeChanged events (more details).

Additional graphics-related performance tips can be found in my Accelerating MATLAB Performance book.

Have you used MarkedClean or some other undocumented HG2 event in your code for some nice effect? If so, please share your experience in a comment below.

]]>
https://undocumentedmatlab.com/blog/undocumented-hg2-graphics-events/feed 14
copyobj behavior change in HG2https://undocumentedmatlab.com/blog/copyobj-behavior-change-in-hg2 https://undocumentedmatlab.com/blog/copyobj-behavior-change-in-hg2#respond Wed, 13 May 2015 16:00:49 +0000 http://undocumentedmatlab.com/?p=5797
 
Related posts:
  1. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
  2. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
  3. uicontextmenu performance Matlab uicontextmenus are not automatically deleted with their associated objects, leading to leaks and slow-downs. ...
  4. Graphic sizing in Matlab R2015b Matlab release R2015b's new "DPI-aware" nature broke some important functionality. Here's what can be done... ...
 
]]>
As a followup to last-week’s post on class-object and generic data copies, I would like to welcome back guest blogger Robert Cumming, who developed a commercial Matlab GUI framework. Today, Robert will highlight a behavior change of Matlab’s copyobj function in HG2.

One of the latest features that was introduced to the GUI Toolbox was the ability to undock or copy panels, that would be displayed in a standalone figure window, but remain connected to the underlying class object:

Panel copy in the GUI framework toolbox

Panel copy in the GUI framework toolbox

These panel copies had to remain fully functional, including all children and callbacks, and they needed to retain all connections back to the source data. In the example above I have altered the plot to show that it’s an actual copy of the data, but has separate behavior from the original panel.

To simply undock a uipanel to a new figure, we can simply re parent it by updating its Parent property to the new figure handle. To make a copy we need to utilize the copyobj function, rather than re-parenting. copyobj can be used to make a copy of all graphic objects that are “grouped” under a common parent, placing their copy in a new parent. In HG2 (R2014b onwards) the default operation of copyobj has changed.

When I started developing this feature everything looked okay and all the objects appeared copied. However, none of the callbacks were functional and all the information stored in the object’s ApplicationData was missing.

I had used copyobj in the past, so I knew that it originally worked ok, so I investigated what was happening. Matlab’s documentation for HG2 code transition suggests re-running the original code to create the second object to populate the callbacks. Unfortunately, this may not be suitable in all cases. Certainly in this case it would be much harder to do, than if the original callbacks had been copied directly. Another suggestion is to use the new ‘lagacy’ option’:

copyobj(___,’legacy’) copies object callback properties and object application data. This behavior is consistent with versions of copyobj before MATLAB® release R2014b.

So, instead of re-running the original code to create the second object to populate the callbacks, we can simply use the new ‘legacy’ option to copy all the callbacks and ApplicationData:

copyobj(hPanel, hNewParent, 'legacy')

Note: for some reason, this new ‘legacy’ option is mentioned in both the doc page and the above-mentioned HG2 code-transition page, but not in the often used help section (help copyobj). There is also no link to the relevant HG2 code-transition page in either the help section or the doc page. I find it unfortunate that for such a backward-incompatible behavior change, MathWorks has not seen fit to document the information more prominently.

Other things to note (this is probably not an exhaustive list…) when you are using copyobj:

  • Any event listeners won’t be copied
  • Any uicontextmenus will not be copied – it will in fact behave strangely due to the fact that it will have the uicontextmenu – but the parent is the original figure – and when you right-click on the object it will change the figure focus. For example:
    hFig= figure;
    ax = axes;
    uic = uicontextmenu ('parent', hFig);
    uim = uimenu('label','My Label', 'parent',uic);
    ax.UIContextMenu = uic;
     
    copyChildren = copyobj (ax, hFig, 'legacy');
     
    hFig2 = figure;
    copyChildren.Parent = hFig2;

Another note on undocked copies – you will need to manage your callbacks appropriately so that the callbacks manage whether they are being run by the original figure or in a new undocked figure.

Conclusions

  1. copyobj has changed in HG2 – but the “legacy” switch allows you to use it as before.
  2. It is unfortunate that backward compatibility was not fully preserved (nor documented enough) in HG2, but at least we have an escape hatch in this case.
  3. Take care with the legacy option as you may need to alter uicontextmenus and re-attach listeners as required.
]]>
https://undocumentedmatlab.com/blog/copyobj-behavior-change-in-hg2/feed 0
Undocumented view transformation matrixhttps://undocumentedmatlab.com/blog/undocumented-view-transformation-matrix https://undocumentedmatlab.com/blog/undocumented-view-transformation-matrix#comments Wed, 15 Apr 2015 21:21:51 +0000 http://undocumentedmatlab.com/?p=5711
 
Related posts:
  1. Multi-column (grid) legend This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
  2. Draggable plot data-tips Matlab's standard plot data-tips can be customized to enable dragging, without being limitted to be adjacent to their data-point. ...
  3. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  4. Customizing axes part 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
 
]]>
Everyone knows Matlab’s view function, right? You know, the function that can set a 3D plot to the proper orientation angles and/or return the current plot’s azimuth/elevation angles. I’ve used it numerous times myself in the past two decades. It’s one of Matlab’s earliest functions, dating back to at least 1984. Still, as often as I’ve used it, it was not until I came across Bruce Elliott’s post on CSSM last week that I realized that this seamingly-innocent stock Matlab function holds a few interesting secrets.

view()’s transformation matrix output

First, while view‘s 2-output syntax ([az,el]=view()) is well known and documented, there is also a single-output syntax (T=view()) that is neither. To be exact, this syntax is not mentioned in the official documentation pages, but it does appear in the help section of view.m, which is viewable (no pun intended…) if you type the following in your Matlab console (R2014a or earlier, note the highlighted lines):

>> help view
 view   3-D graph viewpoint specification.
    view(AZ,EL) and view([AZ,EL]) set the angle of the view from which an
    observer sees the current 3-D plot.  AZ is the azimuth or horizontal
    rotation and EL is the vertical elevation (both in degrees). Azimuth
    revolves about the z-axis, with positive values indicating counter-
    clockwise rotation of the viewpoint. Positive values of elevation
    correspond to moving above the object; negative values move below.
    view([X Y Z]) sets the view angle in Cartesian coordinates. The
    magnitude of vector X,Y,Z is ignored.
 
    Here are some examples:
 
    AZ = -37.5, EL = 30 is the default 3-D view.
    AZ = 0, EL = 90 is directly overhead and the default 2-D view.
    AZ = EL = 0 looks directly up the first column of the matrix.
    AZ = 180 is behind the matrix.
 
    view(2) sets the default 2-D view, AZ = 0, EL = 90.
    view(3) sets the default 3-D view, AZ = -37.5, EL = 30.
 
    [AZ,EL] = view returns the current azimuth and elevation.
 
    T = view returns the current general 4-by-4 transformation matrix. 
    view(AX,...) uses axes AX instead of the current axes.
 
    See also viewmtx, the axes Properties view, Xform. 
    Reference page in Help browser
       doc view
 
>> surf(peaks); T=view
T =
      0.79335     -0.60876            0    -0.092296
      0.30438      0.39668      0.86603     -0.78354
       0.5272      0.68706         -0.5       8.3031
            0            0            0            1

Note that the extra highlighted information is probably a documentation oversight by some MathWorker many years ago, since it was removed from the help section in R2014b and does not appear in the doc pages (not even in R2014a). Perhaps it was documented in the early years but then someone for who-knows-what-reason decided that it shouldn’t be, and then forgot to remove all the loose ends until R2014b. Or maybe it was this way from the very beginning, I don’t know.

In any case, just to be clear on this, the transformation matrix out is still returned by view in the latest Matlab release (R2015a), just as it has for the past who-knows-how-many releases.

There are several interesting things to note here:

view()’s vs. viewmtx()’s transformation matrices

First, MathWorks have still not done a good job of removing all loose ends. Specifically, the T=view syntax is discussed in the doc page (and help section) of the viewmtx function.

To make things worse (and even more confusing), the usage example shown in that doc page is wrong: it says that view(az,el); T=view returns the same transformation matrix T as T=viewmtx(az,el). Close, but not the same:

>> view(30,60); T=view
T =
      0.86603          0.5            0     -0.68301
     -0.43301         0.75          0.5     -0.40849
        -0.25      0.43301     -0.86603       9.0018
            0            0            0            1
>> T2=viewmtx(30,60)
T2 =
      0.86603          0.5            0            0
     -0.43301         0.75          0.5            0
         0.25     -0.43301      0.86603            0
            0            0            0            1

Tough luck I guess for anyone who relies on viewmtx‘s output for complex 3D graphics…

T and T2 appear to be related via a transformation matrix (XT=[1,0,0,0; 0,1,0,0; 0,0,-1,0; 0,0,0,1], we’ll use it again below) that fixes the signs of the first 3 columns, and another translation matrix (camera viewpoint?) that provides the 4th column of T.

HG1’s undocumented axes transformation properties

Another tidbit that should never have been placed in view‘s help section in the first place, is the reference to the axes property Xform (read: “transform”, not “X-Form”). Xform is a hidden undocumented property, and as far as I can tell has always been this way. It is therefore surprising to see it mentioned in the official help section of a highly-visible function such as view. In fact, I don’t remember any other similar case.

In HG1 (R2014a and earlier), the axes’ Xform property held the transformation matrix that view returns. Alongside Xform, the HG1 axes contained several additional transformation vectors (x_RenderOffset, x_RenderScale) and matrices (x_NormRenderTransform, x_ProjectionTransform, x_RenderTransform, x_ViewPortTransform, x_ViewTransform – the latter (x_ViewTransform) is the same as Xform) that could be used for various purposes (example, technical details). All of these properties were removed in HG2 (R2014b or newer).

A complete usage example for some of these properties can be found in MathWorker Joe Conti’s select3d utility, which was removed from the File exchange, but can still be found online (note that it croacks on HG2=R2014b+ due to the removal of the hidden properties):

function [p] = local_Data2PixelTransform(ax,vert)
% Transform vertices from data space to pixel space.
 
% Get needed transforms
xform  = get(ax,'x_RenderTransform');
offset = get(ax,'x_RenderOffset');
scale  = get(ax,'x_RenderScale');
 
% Equivalent: nvert = vert/scale - offset;
nvert(:,1) = vert(:,1)./scale(1) - offset(1);
nvert(:,2) = vert(:,2)./scale(2) - offset(2);
nvert(:,3) = vert(:,3)./scale(3) - offset(3);
 
% Equivalent xvert = xform*xvert;
w = xform(4,1) * nvert(:,1) + xform(4,2) * nvert(:,2) + xform(4,3) * nvert(:,3) + xform(4,4);
xvert(:,1) = xform(1,1) * nvert(:,1) + xform(1,2) * nvert(:,2) + xform(1,3) * nvert(:,3) + xform(1,4);
xvert(:,2) = xform(2,1) * nvert(:,1) + xform(2,2) * nvert(:,2) + xform(2,3) * nvert(:,3) + xform(2,4);
 
% w may be 0 for perspective plots 
ind = find(w==0);
w(ind) = 1; % avoid divide by zero warning
xvert(ind,:) = 0; % set pixel to 0
 
p(:,1) = xvert(:,1) ./ w;
p(:,2) = xvert(:,2) ./ w;

We could even set these hidden properties directly, as Bruno Luong showed back in 2009 (the bug he reported in the R2009b prerelease was temporary, it still worked ok in R2014a):

set(gca,'Xform',eye(4))

HG2’s transformations

In HG2 (R2014b onward), we no longer have access to the hidden properties above. I’m still not exactly sure how to get all the transformations above, but at least the following can be used to replicate the transformation matrix T:

% "standard" way to get the transformation matrix
T = view;
 
% internal way
XT = [1,0,0,0; 0,1,0,0; 0,0,-1,0; 0,0,0,1];
hCamera = get(gca, 'Camera');
T = XT * GetViewMatrix(hCamera);

I’m guessing there are probably similar ways to get the other transformation matrices, but I’ll leave that as an exercise to the reader. Anyone who is up to the task is welcome to leave a comment below. Don’t come asking for my help here – I’m off to solve another puzzle. After all, there’s only a week left before my next blog post is due, so I better get started.

In summary, MathWorks have apparently done some cleanup for the new HG2 in R2014b, but I guess there’s still some work left to do (at least on the documentation). More importantly, much more work is needed to provide simple documented/supported ways of doing 3D transformations without banging our heads at all these hidden corners. Or maybe there already is such a way and I’m simply not aware of it, there’s always that possibility…

]]>
https://undocumentedmatlab.com/blog/undocumented-view-transformation-matrix/feed 5
Plot legend titlehttps://undocumentedmatlab.com/blog/plot-legend-title https://undocumentedmatlab.com/blog/plot-legend-title#comments Wed, 01 Apr 2015 19:21:26 +0000 http://undocumentedmatlab.com/?p=5674
 
Related posts:
  1. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
  2. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
  3. Customizing axes rulers HG2 axes can be customized in numerous useful ways. This article explains how to customize the rulers. ...
  4. Customizing axes part 2 Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
 
]]>
This blog post was supposed to be a piece of cake: The problem description was that we wish to display a text title next to the legend box in plot axes. Sounds simple enough. After all, in HG1 (R2014a and earlier), a legend was a simple wrapper around a standard Matlab axes. Therefore, we can simply access the legend axes’s title handle, and modify its properties. This works very well in HG1:

hold all; 
hLine1 = plot(1:5); 
hLine2 = plot(2:6); 
hLegend = legend([hLine1,hLine2], 'Location','NorthWest');
hTitle = get(hLegend,'title');
set(hTitle, 'String','Plot types:', 'VerticalAlignment','middle', 'FontSize',8);

Matlab HG1 legend with title

Matlab HG1 legend with title

HG2

How hard then could a corresponding solution be in HG2 (R2014b+), right?

Well, it turns out that hard enough (at least for me)…

In this blog I’ve presented ~300 posts so far that discuss solutions to problems. Readers of this blog always hear the success stories, and might mistakenly think that every problem has a similarly simple solution that can be hacked away in a few lines of nifty code.

Well, the truth must be told that for each investigation that yields such a success story, there is at least one other investigation in which I failed to find a solution, no matter how hard I tried or countless hours spent digging (this is not to say that the success stories are easy – distilling a solution to a few lines of code often takes hours of research). In any case, maybe some of these problems for which I have not found a solution do have one that I have simply not discovered, and maybe they don’t – in most likelihood I will never know.

This is yet another example of such a spectacular failure on my part. Try as I may in HG2, I could find no internal handle anywhere to the legend’s axes or title handle. As far as I could tell, HG2’s legend is an standalone object of class matlab.graphics.illustration.Legend that derives from exactly the same superclasses as axes:

>> sort(superclasses('matlab.graphics.axis.Axes'))
ans = 
    'JavaVisible'
    'dynamicprops'
    'handle'
    'matlab.graphics.Graphics'
    'matlab.graphics.GraphicsDisplay'
    'matlab.graphics.internal.GraphicsJavaVisible'
    'matlab.mixin.CustomDisplay'
    'matlab.mixin.Heterogeneous'
    'matlab.mixin.SetGet'
>> sort(superclasses('matlab.graphics.illustration.Legend'))
ans = 
    'JavaVisible'
    'dynamicprops'
    'handle'
    'matlab.graphics.Graphics'
    'matlab.graphics.GraphicsDisplay'
    'matlab.graphics.internal.GraphicsJavaVisible'
    'matlab.mixin.CustomDisplay'
    'matlab.mixin.Heterogeneous'
    'matlab.mixin.SetGet'

This make sense, since they share many properties/features. But it also means that legends are apparently not axes but rather unrelated siblings. As such, if MathWorks chose to remove the Title property from the legend object, we will never find it.

So what can we do in HG2?

Well, we can always resort to the poor-man’s solution of an optical illusion: displaying a an invisible axes object having the same Position as the legend box, with an axes title. We attach property listeners on the legend’s Units, Position and Visible properties, linking them to the corresponding axes properties, so that the title will change if and when the legend’s properties change (for example, by dragging the legend to a different location, or by resizing the figure). We also add an event listener to destroy the axes (and its title) when the legend is destroyed:

% Create the legend
hLegend = legend(...);  % as before
 
% Create an invisible axes at the same position as the legend
hLegendAxes = axes('Parent',hLegend.Parent, 'Units',hLegend.Units, 'Position',hLegend.Position, ...
                   'XTick',[] ,'YTick',[], 'Color','none', 'YColor','none', 'XColor','none', 'HandleVisibility','off', 'HitTest','off');
 
% Add the axes title (will appear directly above the legend box)
hTitle = title(hLegendAxes, 'Plot types:', 'FontWeight','normal', 'FontSize',8);  % Default is bold-11, which is too large
 
% Link between some property values of the legend and the new axes
hLinks = linkprop([hLegend,hLegendAxes], {'Units', 'Position', 'Visible'});
% persist hLinks, otherwise they will stop working when they go out of scope
setappdata(hLegendAxes, 'listeners', hLinks);
 
% Add destruction event listener (no need to persist here - this is done by addlistener)
addlistener(hLegend, 'ObjectBeingDestroyed', @(h,e)delete(hLegendAxes));

Matlab HG2 legend with title

Matlab HG2 legend with title

Yes, this is indeed a bit of an unfortunate regression from HG1, but I currently see no other way to solve this. We can’t win ’em all… If you know a better solution, I’m all ears. Please shoot me an email, or leave a comment below.

Update: As suggested below by Martin, here is a more elegant solution, which attaches a text object as a direct child of the legend’s hidden property DecorationContainer (we cannot add it as a child of the legend since this is prevented and results in an error):

hLegend = legend(...);
hlt = text(...
    'Parent', hLegend.DecorationContainer, ...
    'String', 'Title', ...
    'HorizontalAlignment', 'center', ...
    'VerticalAlignment', 'bottom', ...
    'Position', [0.5, 1.05, 0], ...
    'Units', 'normalized');

The title appears to stay attached to the legend and the Parent property of the text object even reports the legend object as its parent:

hLegend.Location = 'southwest';  % Test the title's attachment
hlt.Parent % Returns hLegend

– thanks Martin!

Happy Passover/Easter everybody!

]]>
https://undocumentedmatlab.com/blog/plot-legend-title/feed 11
Transparent legendhttps://undocumentedmatlab.com/blog/transparent-legend https://undocumentedmatlab.com/blog/transparent-legend#respond Wed, 11 Mar 2015 19:43:27 +0000 http://undocumentedmatlab.com/?p=5617
 
Related posts:
  1. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  2. Customizing axes part 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  3. Plot line transparency and color gradient Static and interpolated (gradient) colors and transparency can be set for plot lines in HG2. ...
  4. Plot markers transparency and color gradient Matlab plot-line markers can be customized to have transparency and color gradients. ...
 
]]>
I’ve been working lately on Matlab program for a client, which attempts to mimic the appearance and behavior of MetaTrader charts, which are extensively used by traders to visualize financial timeseries and analysis indicators.

Such charts are often heavily laden with information, and a legend can be handy to understand the meaning of the various plot lines. Unfortunately, in such heavily-laden charts the legend box typically overlaps the data. We can of course move the legend box around (programmatically or by interactive dragging). But in such cases it might be more useful to have the legend background become semi- or fully-transparent, such that the underlying plot lines would appear beneath the legend:

Matlab chart with a semi-transparent legend (click for details)
Matlab chart with a semi-transparent legend (click for details)

A few months ago I explained the undocumented feature of setting the transparency level of plot lines by simply specifying a fourth numeric (alpha) value to the Color property. Unfortunately, this technique does not work for all graphic objects. For example, setting a 4th (alpha) value to the MarkerFaceColor property results in an error. In the case of legends, setting a 4th (alpha) value to the legend handle’s Color property does not result in an error, but is simply ignored.

The solution in the case of legends is similar in concept to that of the MarkerFaceColor property, which I explained here. The basic idea is to use one of the legend’s hidden properties (in this case, BoxFace) in order to access the low-level color properties (which I have already explained in previous posts):

>> hLegend = legend(...);
>> hLegend.Color = [0.5, 0.5, 0.5, 0.8];  % should be 20%-transparent gray, but in fact opaque gray
 
>> hLegend.BoxFace.get
             AmbientStrength: 0.3
             BackFaceCulling: 'none'
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'on'
                     HitTest: 'off'
                       Layer: 'back'
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 Group]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: []
                     Texture: []
            TwoSidedLighting: 'off'
                  VertexData: [3x4 single]
               VertexIndices: []
                     Visible: 'on'
 
>> hLegend.BoxFace.ColorData  % 4x1 uint8
ans =
  128
  128
  128
  255   % this is the alpha value

As can be seen from this code snippet, the RGB ingredients (but not the alpha value) of Color have passed through to the BoxFace‘s ColorData. The problem stems from BoxFace‘s default ColorType value of 'truecolor'. Once we set it to 'truecoloralpha', we can set ColorData‘s alpha value to a value between uint8(0) and uint8(255):

set(hLegend.BoxFace, 'ColorType','truecoloralpha', 'ColorData',uint8(255*[.5;.5;.5;.8]));  % [.5,.5,.5] is light gray; 0.8 means 20% transparent
Opaque (default) legend20% transparent legend50% transparent legend
0% transparent (default)20% transparent50% transparent
ColorData = [128;128;128;255][128;128;128;204][128;128;128;128]

Note 1: ColorData only accepts a column-vector of 4 uint8 values between 0-255. Attempting to set the value to a row vector or non-uint8 values will result in an error.

Note 2: once we update the BoxFace color, the legend’s standard Color property loses its connection to the underlying BoxFace.ColorData, so updating hLegend.Color will no longer have any effect.

BoxFace.ColorType also accepts 'colormapped' and 'texturemapped' values; the BoxFace.ColorBinding accepts values of 'none', 'discrete' and 'interpolated', in addition to the default value of 'object'. Readers are encouraged to play with these values for colorful effects (for example, gradient background colors).

Finally, note that this entire discussion uses Matlab’s new graphics engine (HG2), on R2014b or newer. For those interested in semi-transparent legends in R2014a or older (HG1), this can be done as follows:

% Prepare a fully-transparent legend (works in both HG1, HG2)
hLegend = legend(...);
set(hLegend, 'Color','none');  % =fully transparent
 
% This fails in HG2 since patch cannot be a child of a legend,
% but it works well in HG1 where legends are simple axes:
patch('Parent',hLegend, 'xdata',[0,0,1,1,0], 'ydata',[0,1,1,0,0], 'HitTest','off', 'FaceColor','y', 'FaceAlpha',0.2);  % 0.2 = 80% transparent

Note that making legends fully-transparent is easy in either HG1 or HG2: simply set the legend’s Color property to 'none'. In HG2 this causes a quirk that the legend background becomes non-draggable (you can only drag the legend box-frame – transparent HG2 backgrounds to not trap mouse evens in the same way that opaque backgrounds do (i.e., when the HG2 legend has Color='w', the background is draggable just like the box-frame). In HG1, this does not happen, so a transparent background is just as draggable as an opaque one.

In any case, as noted above, while making the legend fully-transparent is simple, making it semi-transparent is more problematic. Which is where this post could help.

If you’ve found any other interesting use of these undocumented/hidden legend properties, please share it in a comment below. If you’d like me to develop a custom GUI (such as the charting program above or any other GUI) for you, please contact me by email or using my contact form.

]]>
https://undocumentedmatlab.com/blog/transparent-legend/feed 0
Accessing hidden HG2 plot functionalityhttps://undocumentedmatlab.com/blog/hidden-hg2-plot-functionality https://undocumentedmatlab.com/blog/hidden-hg2-plot-functionality#comments Wed, 04 Feb 2015 20:14:20 +0000 http://undocumentedmatlab.com/?p=5563
 
Related posts:
  1. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
  2. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  3. Customizing axes part 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  4. Plot line transparency and color gradient Static and interpolated (gradient) colors and transparency can be set for plot lines in HG2. ...
 
]]>
I received two separate reader queries in the past 24 hours, asking how to access certain functionalities in HG2 (R2014b)’s new graphics system. These functionalities were previously accessible in HG1 (R2014a and earlier), but stopped being [readily] accessible in HG2. The functionalities have not disappeared, they have merely changed the way in which they can be accessed. Moreover, with the new graphics system they were even expanded in terms of their customizability.

In both cases, the general way in which I approached the problem was the same, and I think this could be used in other cases where you might need some HG1 functionality which you cannot find how to access in HG2. So try to read today’s article not as a specific fix to these two specific issues, but rather as a “how-to” guide to access seemingly inaccessible HG2 features.

Accessing contour fills

Contour fills were implemented in HG1 as separate HG objects that could be accessed using findall or allchild. This could be used to set various properties of the fills, such as transparency. This broke in HG2 as reported by reader Leslie: the contours are no longer regular HG children. No complaints there – after all, it was based on undocumented internal features of the data-brushing functionality.

It turns out that the solution for HG2 is not difficult, using the contour handle’s hidden FacePrims property:

[~, hContour] = contourf(peaks(20), 10);
drawnow;  % this is important, to ensure that FacePrims is ready in the next line!
hFills = hContour.FacePrims;  % array of matlab.graphics.primitive.world.TriangleStrip objects
for idx = 1 : numel(hFills)
   hFills(idx).ColorType = 'truecoloralpha';   % default = 'truecolor'
   hFills(idx).ColorData(4) = 150;   % default=255
end

Contour plot in HG2, with and without transparency

Contour plot in HG2, with and without transparency


The contour fills are now stored as an array of TriangleStrip objects, which can be individually customized:

>> get(hFills(1))
             AmbientStrength: 0.3
             BackFaceCulling: 'none'
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecoloralpha'
             DiffuseStrength: 0.6
            HandleVisibility: 'on'
                     HitTest: 'off'
                       Layer: 'middle'
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 Contour]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: [1 4 11 14 19 25 28 33]
                     Texture: []
            TwoSidedLighting: 'off'
                  VertexData: [3x32 single]
               VertexIndices: []
                     Visible: 'on'

Accessing plot brushed data

In October 2010 I published an article explaining how to programmatically access brushed data in Matlab plots (brushed data are highlighted data points using the interactive data-brushing tool on the figure toolbar). Apparently, data brushing was implemented as a data line having only the data-brushed points in its data, and using dedicated markers. This worked well in HG1, until it too broke in HG2, as reported by reader bash0r.

It turns out that in HG2, you can access the brushing data using the plot line’s hidden BrushHandles property, as follows:

hBrushHandles = hLine.BrushHandles;
hBrushChildrenHandles = hBrushHandles.Children;  % Marker, LineStrip

I described the new Marker objects here, and LineStrip objects here. The brushed vertex data can be retrieved from either of them. For example:

>> hBrushChildrenHandles(1).VertextData
ans = 
     1     2     3     4     % X-data of 4 data points
     1     2     3     4     % Y-data of 4 data points
     0     0     0     0     % Z-data of 4 data points

If you only need the brushed data points (not the handles for the Markers and LineStrip, you can get them directly from the line handle, using the hidden BrushData property:

>> brushedIdx = logical(hLine.BrushData);  % logical array (hLine.BrushData is an array of 0/1 ints)
>> brushedXData = hLine.XData(brushedIdx);
>> brushedYData = hLine.YData(brushedIdx)
brushedYData =
     1     2     3     4

Data brushing in HG2

Data brushing in HG2

A general “how-to” guide

In order to generalize these two simple examples, we see that whereas the HG objects in HG1 were relatively “flat”, in HG2 they became much more complex objects, and their associated functionality is now embedded within deeply-nested properties, which are in many cases hidden. So, if you find a functionality for which you can’t find a direct access via the documented properties, it is very likely that this functionality can be accessed by some internal hidden property.

In order to list such properties, you can use my getundoc utility, or simply use the built-in good-ol’ struct function, as I explained here. For example:

>> warning('off','MATLAB:structOnObject')  % turn off warning on using struct() on an object. Yeah, we know it's bad...
>> allProps = struct(hContour)
allProps = 
                 FacePrims: [11x1 TriangleStrip]
             FacePrimsMode: 'auto'
               FacePrims_I: [11x1 TriangleStrip]
                 EdgePrims: [10x1 LineStrip]
             EdgePrimsMode: 'auto'
               EdgePrims_I: [10x1 LineStrip]
                 TextPrims: []
             TextPrimsMode: 'auto'
               TextPrims_I: []
             ContourMatrix: [2x322 double]
         ContourMatrixMode: 'auto'
           ContourMatrix_I: [2x322 double]
             ContourZLevel: 0
         ContourZLevelMode: 'auto'
           ContourZLevel_I: 0
                      Fill: 'on'
                  FillMode: 'auto'
                    Fill_I: 'on'
                      Is3D: 'off'
                  Is3DMode: 'auto'
                    Is3D_I: 'off'
              LabelSpacing: 144
                       ...   % (many more properties listed)

This method can be used on ALL HG2 objects, as well as on any internal objects that are referenced by the listed properties. For example:

>> allProps = struct(hContour.FacePrims)
allProps = 
             StripDataMode: 'manual'
               StripData_I: [1 4 11 14 19 25 28 33]
                 StripData: [1 4 11 14 19 25 28 33]
       BackFaceCullingMode: 'auto'
         BackFaceCulling_I: 'none'
           BackFaceCulling: 'none'
      FaceOffsetFactorMode: 'manual'
        FaceOffsetFactor_I: 0
          FaceOffsetFactor: 0
        FaceOffsetBiasMode: 'manual'
          FaceOffsetBias_I: 0.0343333333333333
            FaceOffsetBias: 0.0343333333333333
      TwoSidedLightingMode: 'auto'
        TwoSidedLighting_I: 'off'
          TwoSidedLighting: 'off'
                   Texture: []
               TextureMode: 'auto'
                       ...   % (many more properties listed)

In some cases, the internal property may not be directly accessible as object properties, but you can always access them via the struct (for example, allProps.StripData).

Do you use any HG1 functionality in your code that broke in HG2? Did my “how-to” guide above help you recreate the missing functionality in HG2? If so, please share your findings with all of us in a comment below.

]]>
https://undocumentedmatlab.com/blog/hidden-hg2-plot-functionality/feed 17
export_fighttps://undocumentedmatlab.com/blog/export_fig https://undocumentedmatlab.com/blog/export_fig#comments Wed, 21 Jan 2015 18:00:02 +0000 http://undocumentedmatlab.com/?p=5470
 
Related posts:
  1. Introduction to UDD UDD classes underlie many of Matlab's handle-graphics objects and functionality. This article introduces these classes....
  2. Multi-column (grid) legend This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
  3. Undocumented scatter plot behavior The scatter plot function has an undocumented behavior when plotting more than 100 points: it returns a single unified patch object handle, rather than a patch handle for each specific...
  4. Hierarchical Systems with UDD UDD objects can be grouped in structured hierarchies - this article explains how...
 
]]>
I would like to introduce guest blogger Oliver Woodford. For the past several years Oliver has been a top contributor on the Matlab File Exchange, and several of his utilities have earned the prestigious distinction as “Pick of the Week”. This is no easy feat in a File Exchange that hosts ~23K utilities at latest count. For the past few years, excluding short spans of time, Oliver’s export_fig was the File Exchange’s most downloaded utility, by a wide margin. export_fig has improved the output quality of figures for so many numerous Matlab users that it is hard to imagine a Matlab File Exchange without it. Today, Oliver describes the basic technical mechanisms underlying export_fig.

Yair has very kindly agreed to take over maintenance of export_fig. For his benefit, and anyone else’s interest, I will briefly describe the layout and functionality of the toolbox.

Before starting, I always recommend that new users read the README file, to get a better understanding of the available options, how the functions perform, how to do certain frequently-asked things, and what problems still remain. The following excerpt comes from the README file (please do read the entire file):

If you’ve ever wondered what’s going on in the icon on the export_fig download page (reproduced below), then this explanation is for you: The icon is designed to demonstrate as many of export_fig‘s features as possible. Given a figure containing a translucent mesh (top right), export_fig can export to pdf (bottom center), which allows the figure to be zoomed in without losing quality (because it’s a vector graphic), but isn’t able to reproduce the translucency, and also, depending on the viewer, creates small gaps between the patches, which are seen here as thin white lines. By contrast, when exporting to png (top left), translucency is preserved (see how the graphic below shows through), the figure is anti-aliased, but zooming-in does not reveal more detail.

export_fig demo usage (click for details)

export_fig demo usage (click for details)


Goals of export_fig

  1. Publication quality

    I wrote export_fig to produce nice looking figures for my PhD thesis and journal papers. The two standard MATLAB functions for exporting figures are saveas and print. Unfortunately, the quality of their default outputs just wasn’t good enough. For example, images in PDF outputs would be heavily compressed and I wanted control over image quality.

    eps2pdf (which is part of the export_fig package) takes a quality setting as input, and converts this into suitable settings for ghostscript to convert the EPS file to a PDF, allowing users to get much higher fidelity output. In addition, ghostscript also crops the figure border to the EPS bounding box, and embeds fonts in the PDF. This last feature is obligatory for some publications, and export_fig is sometimes recommended by conferences or journals for this reason. Control of JPEG quality is achieved by passing the quality option to imwrite in export_fig.m.

    In export_fig.m, I also added anti-aliasing for raster outputs (by exporting at a higher resolution and downsizing), and alpha-matting (by exporting with both black and white backgrounds to compute transparency) for PNG outputs. The alpha-matting feature allows textured backgrounds to show through the exported figure, which can be useful for presentation slides. Here is an image that demonstrates such alpha-matting:

    Transparency alpha-matting

    Transparency alpha-matting

  2. WYSIWYG (what you see is what you get)

    Another issue with saveas and print is that, by default, they do not reproduce a figure exactly as it appears on screen. The dimensions change, the aspect ratio changes, the amount of whitespace changes, the background color changes, the axes ticks change, line dash lengths change, the fonts change, etc. All these changes are generally-undesirable side-effects of those functions.

    export_fig avoids these changes to the figure, ensuring that it gets exported as faithfully as possible to the on screen visualization. export_fig uses print under the hood, but changes various figure and axes settings to get the desired result. For example, in export_fig.m I call set(fig,'InvertHardcopy','off'), which ensures that the background color doesn’t change. I also set the axes Limits and Tick modes to 'manual', to ensure that axes don’t change. Similarly, in print2eps.m, I call set(fig, 'PaperPositionMode','auto', 'PaperOrientation','portrait') to ensure that the figure dimensions don’t change for vector outputs.

    Two of the changes, line dash lengths and fonts, are caused by the Painters renderer, and so they only occur in vector output. The only way to fix these issues was to edit the EPS file that print generates: fix_lines.m changes the definition of dash lengths in the EPS file, so that the dash lengths depend on the line widths. Likewise, print2eps.m changes unsupported fonts in the figure to supported ones, then reinserts the unsupported font into the EPS. Unfortunately this doesn’t work so well when the two fonts have different character widths.

    In addition to these features, the MATLAB rendering pipeline (in both HG1 and HG2 versions) is full of bugs. Many lines of code in export_fig are dedicated to circumventing these undocumented bugs. For example, lines 249-273 of today’s export_fig.m:

    % MATLAB "feature": black colorbar axes can change to white and vice versa!
    hCB = findobj(fig, 'Type', 'axes', 'Tag', 'Colorbar');
    if isempty(hCB)
        yCol = [];
        xCol = [];
    else
        yCol = get(hCB, 'YColor');
        xCol = get(hCB, 'XColor');
        if iscell(yCol)
            yCol = cell2mat(yCol);
            xCol = cell2mat(xCol);
        end
        yCol = sum(yCol, 2);
        xCol = sum(xCol, 2);
    end
     
    % MATLAB "feature": apparently figure size can change when changing colour in -nodisplay mode
    pos = get(fig, 'Position');
     
    % Set the background colour to black, and set size in case it was changed internally
    tcol = get(fig, 'Color');
    set(fig, 'Color', 'k', 'Position', pos);
     
    % Correct the colorbar axes colours
    set(hCB(yCol==0), 'YColor', [0 0 0]);
    set(hCB(xCol==0), 'XColor', [0 0 0]);

    Similarly, lines 143-160 of today’s print2eps.m:

    % MATLAB bug fix - black and white text can come out inverted sometimes
    % Find the white and black text
    black_text_handles = findobj(fig, 'Type', 'text', 'Color', [0 0 0]);
    white_text_handles = findobj(fig, 'Type', 'text', 'Color', [1 1 1]);
     
    % Set the font colors slightly off their correct values
    set(black_text_handles, 'Color', [0 0 0] + eps);
    set(white_text_handles, 'Color', [1 1 1] - eps);
     
    % MATLAB bug fix - white lines can come out funny sometimes
    % Find the white lines
    white_line_handles = findobj(fig, 'Type', 'line', 'Color', [1 1 1]);
     
    % Set the line color slightly off white
    set(white_line_handles, 'Color', [1 1 1] - 0.00001);
     
    % Print to eps file
    print(fig, options{:}, name);
     
    % Reset the font and line colors
    set(black_text_handles, 'Color', [0 0 0]);
    set(white_text_handles, 'Color', [1 1 1]);
    set(white_line_handles, 'Color', [1 1 1]);

Design philosophy

The export_fig toolbox is just a collection of functions. I put code into a separate function when either:

  1. One might want to use that functionality on its own, or
  2. That functionality is required by two or more other functions.

Subfunctions are used for code called multiple times in only one file, or where it improves legibility of the code.

The easiest way to understand the structure of the export_fig toolbox is to visualize the tree of function dependencies within the toolbox:

export_fig overview (click for details)

export_fig overview (click for details)

I’ll now describe what each function does in a bit more detail:

  1. export_fig

    This is the main function in the toolbox. It allows exporting a figure to several different file formats at once. It parses the input options. If only a subset of axes are being exported then it handles the transfer of these to a new figure (which gets destroyed at the end). It makes calls to print2array() for raster outputs, and print2eps() for vector outputs. For raster outputs, it also does the downsampling if anti-aliasing is enabled, and alphamatte computation if transparency is enabled. For vector outputs, it also does the conversion from eps to pdf, and also pdf back to eps. The reason for this last conversion is that the pdf is cropped, compressed and has the fonts embedded, and eps outputs should get this too (though I don’t think the font embedding remains).

  2. print2array

    This function rasterizes the figure. If the painters algorithm is specified then it calls print2eps(), then rasterizes the resulting EPS file using ghostscript. If another renderer is specified then this function just calls print() to output a bitmap. Finally, borders are cropped, if requested.

  3. print2eps

    This function calls print() to generate an EPS file, then makes several fixes to the EPS file, including making dash lengths commensurate with line width (old graphics system (HG1) only, i.e. R2014a or earlier), and substituting back in unsupported fonts.

  4. crop_borders

    Crops away any margin space surrounding the image(s): Given an image or stack of images (stacked along the 4th dimension), and a background color, crop_borders() computes the number of rows at the top and bottom and number of columns on the left and the right of the image(s) that are entirely the background color, and removes these rows and columns.

  5. ghostscript

    This function looks for a Ghostscript executable. or asks the user to specify its location. It then stores the path, or simply loads the path if one is already stored. It then calls the Ghostscript executable with the specified arguments.

  6. fix_lines

    This function makes dash lengths commensurate with line width, and converts grid lines from dashes to circular dots, in EPS files generated by MATLAB using HG1 (R2014a or earlier). fix_lines is also available as a separate File Exchange utility (selected for Pick-of–the-Week, just like export_fig). This function is no longer required in HG2 (R2014b or newer) – see below.

  7. read_write_entire_textfile

    Does what it says, reading or writing and entire text file from/to disk to/from memory, but handles errors gracefully, not leaving files open.

  8. eps2pdf

    This function converts an EPS file into a PDF file using Ghostscript.

  9. pdf2eps

    This function converts a PDF file into an EPS file using pdftops, from the Xpdf package.

  10. pdftops

    Does exactly the same thing as the ghostscript function, but for the pdftops executable instead.

  11. user_string

    This stores or loads user-specific strings in text files. This allows user-specific values to propagate across different versions of MATLAB, whilst avoiding these values being stored in version controlled code.

  12. copyfig

    This function simply creates a copy of a figure, like the built-in copyobj(). However, because the latter has some bugs, this function, which saves the figure to disk then opens it again, is required. Indeed, much of the code within the toolbox is simply circumventing bugs in MATLAB functions.

  13. isolate_axes

    This function removes unwanted axes from a figure. The approach to exporting a subset of axes is to copy the figure then remove the unwanted axes. Again, this is to circumvent bugs in the builtin function copyobj().

  14. using_hg2

    This function provides a robust way of checking if HG2 is in use (HG1 is the default in R2014a or older; HG2 is the default in R2014b or newer).

    In HG2, the entire rendering pipeline has changed. While most of the features remain the same, the bugs have completely changed. For example, dash lengths in HG2 vector output are now sensible, so fix_lines is not required. However, in R2014b at least, patch-based graphics generate bloated EPS files, and line widths cannot go below 0.75pt. Finally, an obvious change for raster output is that it is smoothed by default, so anti-aliasing is not required; the default settings in the parse_args() function within export_fig.m reflect this.

  15. append_pdfs

    export_fig does have a -append option, but this gets slower the larger the appended file becomes. A quicker way of generating a multipage PDF from several figures is to export them all to separate PDFs, then use this function to combine them into one in a single go. Also available as a separate File Exchange utility.

  16. im2gif

    This function converts a stack of images, or a multi-image TIFF file, to an animated GIF. export_fig can generate multi-image TIFFs using the -append option, making generating animated GIFs straightforward. Also available as a separate File Exchange utility.

And that’s it! All the functions have help text describing their input and output arguments.

Editorial notes

After several year of creating, improving and maintaining export_fig, Oliver is unfortunately no longer able to maintain this utility. As Oliver mentioned above, I (Yair) have volunteered to try to step into his shoes and maintain it. As Oliver’s detailed description (which barely scratches the surface) shows, this is certainly not a trivial utility. It will take me some time to get up to speed with all the internal technical details, so please be patient…

Readers interested in high-fidelity export might also consider using my ScreenCapture utility. Unlike export_fig, which uses Matlab’s builtin print function to generate (and fix) the output, ScreenCapture uses the standard java.awt.Robot.createScreenCapture() to take an actual screen-capture of the requested figure, axes or window area and then saves this to file/clipboard or sends it to the printer. In a sense, the export_fig and ScreenCapture utilities nicely complement each other.

]]>
https://undocumentedmatlab.com/blog/export_fig/feed 20
Plot markers transparency and color gradienthttps://undocumentedmatlab.com/blog/plot-markers-transparency-and-color-gradient https://undocumentedmatlab.com/blog/plot-markers-transparency-and-color-gradient#comments Wed, 19 Nov 2014 16:42:11 +0000 http://undocumentedmatlab.com/?p=5262
 
Related posts:
  1. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  2. Customizing axes part 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  3. Plot line transparency and color gradient Static and interpolated (gradient) colors and transparency can be set for plot lines in HG2. ...
  4. Transparent legend Matlab chart legends are opaque be default but can be made semi- or fully transparent. ...
 
]]>
Last week I explained how to customize plot-lines with transparency and color gradient. Today I wish to show how we can achieve similar effects with plot markers. Note that this discussion (like the preceding several posts) deal exclusively with HG2, Matlab’s new graphics system starting with R2014b (well yes, we can also turn HG2 on in earlier releases).

As Paul has noted in a comment last week, we cannot simply set a 4th (alpha transparency) element to the MarkerFaceColor and MarkerEdgeColor properties:

>> x=1:10; y=10*x; hLine=plot(x,y,'o-'); drawnow;
>> hLine.MarkerFaceColor = [0.5,0.5,0.5];      % This is ok
>> hLine.MarkerFaceColor = [0.5,0.5,0.5,0.3];  % Not ok
While setting the 'MarkerFaceColor' property of Line:
Color value must be a 3 element numeric vector

Standard Matlab plot markers

Standard Matlab plot markers

Lost cause? – not in a long shot. We simply need to be a bit more persuasive, using the hidden MarkerHandle property:

>> hMarkers = hLine.MarkerHandle;  % a matlab.graphics.primitive.world.Marker object
 
>> hMarkers.get
    EdgeColorBinding: 'object'
       EdgeColorData: [4x1 uint8]
       EdgeColorType: 'truecolor'
    FaceColorBinding: 'object'
       FaceColorData: [4x1 uint8]
       FaceColorType: 'truecolor'
    HandleVisibility: 'off'
             HitTest: 'off'
               Layer: 'middle'
           LineWidth: 0.5
              Parent: [1x1 Line]
       PickableParts: 'visible'
                Size: 6
               Style: 'circle'
          VertexData: [3x10 single]
       VertexIndices: []
             Visible: 'on'
 
>> hMarkers.EdgeColorData'  % 4-element uint8 array
ans =
    0  114  189  255
 
>> hMarkers.FaceColorData'  % 4-element uint8 array
ans =
  128  128  128  255

As we can see, we can separately attach transparency values to the marker’s edges and/or faces. For example:

hMarkers.FaceColorData = uint8(255*[1;0;0;0.3]);  % Alpha=0.3 => 70% transparent red

70% Transparent Matlab plot markers

70% Transparent Matlab plot markers

And as we have seen last week, we can also apply color gradient across the markers, by modifying the EdgeColorBinding/FaceColorBinding from ‘object’ to ‘interpolated’ (there are also ‘discrete’ and ‘none’), along with changing the corresponding FaceColorData/EdgeColorData from being a 4×1 array to a 4xN array:

>> colorData = uint8([210:5:255; 0:28:252; [0:10:50,40:-10:10]; 200:-10:110])
colorData =
  210  215  220  225  230  235  240  245  250  255
    0   28   56   84  112  140  168  196  224  252
    0   10   20   30   40   50   40   30   20   10
  200  190  180  170  160  150  140  130  120  110
 
>> set(hMarkers,'FaceColorBinding','interpolated', 'FaceColorData',colorData)

Matlab plot markers with color and transparency gradients

Matlab plot markers with color and transparency gradients

This can be useful for plotting comet trails, radar/sonar tracks, travel trajectories, etc. We can also use it to overlay meta-data information, such as buy/sell indications on a financial time-series plot. In fact, it opens up Matlab plots to a whole new spectrum of customizations that were more difficult (although not impossible) to achieve earlier.

Throughout today, we’ve kept the default FaceColorType/EdgeColorType value of ‘truecolor’ (which is really the same as ‘truecoloralpha’ as far as I can tell, since both accept an alpha transparency value as the 4th color element). If you’re into experimentation, you might also try ‘colormapped’ and ‘texturemapped’.

]]>
https://undocumentedmatlab.com/blog/plot-markers-transparency-and-color-gradient/feed 60
Plot line transparency and color gradienthttps://undocumentedmatlab.com/blog/plot-line-transparency-and-color-gradient https://undocumentedmatlab.com/blog/plot-line-transparency-and-color-gradient#comments Fri, 14 Nov 2014 00:14:10 +0000 http://undocumentedmatlab.com/?p=5200
 
Related posts:
  1. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  2. Customizing axes part 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  3. Plot markers transparency and color gradient Matlab plot-line markers can be customized to have transparency and color gradients. ...
  4. Transparent legend Matlab chart legends are opaque be default but can be made semi- or fully transparent. ...
 
]]>
In the past few weeks, I discussed the new HG2 axes Backdrop and Baseline properties with their associated ability to specify the transparency level using a fourth (undocumented) element in their Color.

In other words, color in HG2 can still be specified as an RGB triplet (e.g., [1,0,0] to symbolize bright red), but also via a 4-element quadruplet RGBA, where the 4th element (Alpha) signifies the opacity level (0.0=fully transparent, 0.5=semi-transparent, 1.0=opaque). So, for example, [1, 0, 0, 0.3] means a 70%-transparent red.

This Alpha element is not documented anywhere as being acceptable, but appears to be supported almost universally in HG2 wherever a color element can be specified. In some rare cases (e.g., for patch objects) Matlab has separate Alpha properties that are fully documented, but in any case nowhere have I seen documented that we can directly set the alpha value in the color property, especially for objects (such as plot lines) that do not officially support transparency. If anyone finds a documented reference anywhere, please let me know – perhaps I simply missed it.

Here is a simple visualization:

xlim([1,5]);
hold('on');
h1a = plot(1:5,     11:15, '.-', 'LineWidth',10, 'DisplayName',' 0.5');
h1b = plot(1.5:5.5, 11:15, '.-', 'LineWidth',10, 'DisplayName',' 1.0', 'Color',h1a.Color);  % 100% opaque
h1a.Color(4) = 0.5;  % 50% transparent
h2a = plot(3:7,  15:-1:11, '.-r', 'LineWidth',8, 'DisplayName',' 0.3'); h2a.Color(4)=0.3;  % 70% transparent
h2b = plot(2:6,  15:-1:11, '.-r', 'LineWidth',8, 'DisplayName',' 0.7'); h2b.Color(4)=0.7;  % 30% transparent
h2c = plot(1:5,  15:-1:11, '.-r', 'LineWidth',8, 'DisplayName',' 1.0');  % 100% opaque = 0% transparent
legend('show','Location','west')

Transparent HG2 plot lines

Transparent HG2 plot lines

Now for the fun part: we can make color-transition (gradient) effects along the line, using its hidden Edge property:

>> h2b.Edge.get
          AlignVertexCenters: 'off'
             AmbientStrength: 0.3
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecoloralpha'
             DiffuseStrength: 0.6
            HandleVisibility: 'off'
                     HitTest: 'off'
                       Layer: 'middle'
                   LineStyle: 'solid'
                   LineWidth: 8
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 Line]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: [1 6]
                     Texture: []
                  VertexData: [3x5 single]
               VertexIndices: []
                     Visible: 'on'
       WideLineRenderingHint: 'software'
 
>> h2b.Edge.ColorData  %[4x1 uint8]
ans =
  255
    0
    0
  179

The tricky part is to change the Edge.ColorBinding value from its default value of ‘object’ to ‘interpolated’ (there are also ‘discrete’ and ‘none’). Then we can modify Edge.ColorData from being a 4×1 array of uint8 (value of 255 corresponding to a color value of 1.0), to being a 4xN matrix, where N is the number of data points specified for the line, such that each data point along the line will get its own unique RGB or RGBA value. (the data values themselves are kept as a 3xN matrix of single values in Edge.VertexData).

So, for example, let’s modify the middle (30%-transparent) red line to something more colorful:

>> cd=uint8([255,200,250,50,0; 0,50,250,150,200; 0,0,0,100,150; 179,150,200,70,50])
cd =
  255  200  250   50    0
    0   50  250  150  200
    0    0    0  100  150
  179  150  200   70   50
 
>> set(h2b.Edge, 'ColorBinding','interpolated', 'ColorData',cd)

HG2 plot line color, transparency gradient

HG2 plot line color, transparency gradient

As you can see, we can interpolate not only the colors, but also the transparency along the line.

Note: We need to update all the relevant properties together, in a single set() update, otherwise we’d get warning messages about incompatibilities between the property values. For example:

>> h2b.Edge.ColorBinding = 'interpolated';
Warning: Error creating or updating LineStrip
 Error in value of property ColorData
 Array is wrong shape or size
(Type "warning off MATLAB:gui:array:InvalidArrayShape" to suppress this warning.)

Markers

Note how the markers are clearly seen in the transparent lines but not the opaque ones. This is because the markers have the same color as the lines in today’s example. Since the lines are wide, the markers are surrounded by pixels of the same color. Therefore, the markers are only visible when the surrounding pixels are less opaque (i.e., lighter).

As a related customization, we can control whether the markers appear “on top of” (in front of) the line or “beneath” it by updating the Edge.Layer property from ‘middle’ to ‘front’ (there is also ‘back’, but I guess you won’t typically use it). This is important for transparent lines, since it controls the brightness of the markers: “on top” (in front) they appear brighter. As far as I know, this cannot be set separately for each marker – they are all updated together.

Of course, we could always update the line’s fully-documented MarkerSize, MarkerFaceColor and MarkerEdgeColor properties, in addition to the undocumented customizations above.

Next week I will describe how we can customize plot line markers in ways that you never thought possible. So stay tuned :-)

]]>
https://undocumentedmatlab.com/blog/plot-line-transparency-and-color-gradient/feed 35
Customizing axes part 4 – additional propertieshttps://undocumentedmatlab.com/blog/customizing-axes-part-4-additional-properties https://undocumentedmatlab.com/blog/customizing-axes-part-4-additional-properties#comments Wed, 29 Oct 2014 18:00:08 +0000 http://undocumentedmatlab.com/?p=5190
 
Related posts:
  1. Customizing axes part 2 Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  2. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  3. Plot line transparency and color gradient Static and interpolated (gradient) colors and transparency can be set for plot lines in HG2. ...
  4. Plot markers transparency and color gradient Matlab plot-line markers can be customized to have transparency and color gradients. ...
 
]]>
In the past three weeks I explained how HG2 (in R2014b) enables us to customize the axes rulers, back-drop, baselines, box and grid-lines in ways that were previously impossible in HG1 (R2014a or earlier). Today I will conclude the mini-series on axes customizations by describing other useful undocumented customizations of the HG2 axes:

Camera

The Camera object (matlab.graphics.axis.camera.Camera3D) controls the 3D camera/lighting of the axes. Camera is a new hidden property of HG2 axes, that did not exist in earlier Matlab releases (HG1). This functionality is normally controlled via the 3D figure toolbar and related functions (view, camup, campos etc.). We can have better granularity by discretely customizing Camera‘s properties:

>> hAxes.Camera.get
               AspectRatio: 1
                  Children: []
      DepthCalculationHint: 'careful'
                 DepthSort: 'on'
          HandleVisibility: 'off'
                    Parent: [1x1 Axes]
        PlotBoxAspectRatio: [1 1 1]
    PlotBoxAspectRatioMode: 'auto'
                  Position: [-4.06571071756541 -5.45015005218426 4.83012701892219]
                Projection: 'orthographic'
                    Target: [0.5 0.5 0.5]
    TransparencyMethodHint: 'depthpeel'
                  UpVector: [0 0 1]
                 ViewAngle: 10.339584907202
                  Viewport: [1x1 matlab.graphics.general.UnitPosition]
                   Visible: 'on'
                WarpToFill: 'vertical'

SortMethod

SortMethod is a newly-supported axes property in R2014b. It is new only in the sense that it became documented: It has existed in its present form also in previous Matlab releases, as a hidden axes property (I’m not sure exactly from which release). This is yet another example of an undocumented Matlab functionality that existed for many years before MathWorks decided to make it official (other examples in R2014b are the set of uitab/uitabgroup functions).

SortMethod is important due to its impact on graphic rendering performance: By default, Matlab draws objects in a back-to-front order based on the current view. This means that objects (lines, patches etc.) which should appear “on top” of other objects are drawn last, overlapping the objects “beneath” them. Calculating the order of the objects, especially in complex plots having multiple overlapping segments, can take noticeable time. We can improve performance by telling the renderer to draw objects in the order of the Children property, which is typically the order in which the objects were created. This can be done by setting the axes’ SortMethod property to ‘childorder’ (default=’depth’). Since SortOrder existed in past Matlab releases as well, we can use this technique on the older MATLAB releases just as for R2014b or newer.

In a related matter, we should note that transparent/translucent patches and lines (having a 4th Color element (alpha) value between 0.0 and 1.0) are slower to render for much the same reasons. Reducing the number of transparent/translucent objects will improve graphics rendering performance. Note that specifying a 4th Color element is again an undocumented feature: Matlab officially only supports RGB triplets and named color strings, not RGBA quadruplets. I’ll discuss this aspect next week.

WarpToFill

WarpToFill (default=’on’) is a simple flag that controls whether or not the axes should fill its container (panel or figure), leaving minimal margins. We can consider this as another variant to the documented alternatives of the axis function:

surf(peaks);
set(gca,'WarpToFill','off');

HG2 axes WarpToFill on HG2 axes WarpToFill off

HG2 axes WarpToFill (on, off)

Note that WarpToFill is not new in R2014b – it has existed in its present form also in previous Matlab releases, as a hidden axes property. One would hope that it will finally become officially supported, as SortMethod above has.

Additional properties

Additional undocumented aspects of Matlab axes that I have reported over the years, including the LooseInset property and determining axes zoom state, still work in HG2 (R2014b). They might be removed someday, but hopefully they will take the opposite path that SortMethod did, of becoming fully supported.

On the other hand, some important undocumented HG1 axes properties have been removed: x_NormRenderTransform, x_ProjectionTransform, x_RenderOffset, x_RenderScale, x_RenderTransform, x_ViewPortTransform and x_ViewTransform could be used in HG1 axes to map between 3D and 2D data spaces. Some users have already complained about this. I have [still] not found a simple workaround for this, but I’m pretty sure that there is one. Maybe the hidden axes DataSpace property could be used for this, but I’m not sure how exactly. Edit: see my post on view transformations (and the reader comments on it).

This concludes my series on undocumented HG2 axes customizations (at least for now). Next week, I will describe undocumented HG2 plot-line customizations.

]]>
https://undocumentedmatlab.com/blog/customizing-axes-part-4-additional-properties/feed 5