Hidden property – Undocumented Matlab https://undocumentedmatlab.com/blog_old Charting Matlab's unsupported hidden underbelly Tue, 29 Oct 2019 15:26:09 +0000 en-US hourly 1 https://wordpress.org/?v=4.4.1 Undocumented plot marker typeshttps://undocumentedmatlab.com/blog_old/undocumented-plot-marker-types https://undocumentedmatlab.com/blog_old/undocumented-plot-marker-types#comments Wed, 13 Mar 2019 11:05:14 +0000 https://undocumentedmatlab.com/?p=8539 Related posts:
  1. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  2. FIG files format FIG files are actually MAT files in disguise. This article explains how this can be useful in Matlab applications....
  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. ...
]]>
I wanted to take a break from my miniseries on the Matlab toolstrip to describe a nice little undocumented aspect of plot line markers. Plot line marker types have remained essentially unchanged in user-facing functionality for the past two+ decades, allowing the well-known marker types (.,+,o,^ etc.). Internally, lots of things changed in the graphics engine, particularly in the transition to HG2 in R2014b and the implementation of markers using OpenGL primitives. I suspect that during the massive amount of development work that was done at that time, important functionality improvements that were implemented in the engine were forgotten and did not percolate all the way up to the user-facing functions. I highlighted a few of these in the past, for example transparency and color gradient for plot lines and markers, or various aspects of contour plots.

Fortunately, Matlab usually exposes the internal objects that we can customize and which enable these extra features, in hidden properties of the top-level graphics handle. For example, the standard Matlab plot-line handle has a hidden property called MarkerHandle that we can access. This returns an internal object that enables marker transparency and color gradients. We can also use this object to set the marker style to a couple of formats that are not available in the top-level object:

>> x=1:10; y=10*x; hLine=plot(x,y,'o-'); box off; drawnow;
>> hLine.MarkerEdgeColor = 'r';
 
>> set(hLine, 'Marker')'  % top-level marker styles
ans =
  1×14 cell array
    {'+'} {'o'} {'*'} {'.'} {'x'} {'square'} {'diamond'} {'v'} {'^'} {'>'} {'<'} {'pentagram'} {'hexagram'} {'none'}
 
>> set(hLine.MarkerHandle, 'Style')'  % low-level marker styles
ans =
  1×16 cell array
    {'plus'} {'circle'} {'asterisk'} {'point'} {'x'} {'square'} {'diamond'} {'downtriangle'} {'triangle'} {'righttriangle'} {'lefttriangle'} {'pentagram'} {'hexagram'} {'vbar'} {'hbar'} {'none'}

We see that the top-level marker styles directly correspond to the low-level styles, except for the low-level ‘vbar’ and ‘hbar’ styles. Perhaps the developers forgot to add these two styles to the top-level object in the enormous upheaval of HG2. Luckily, we can set the hbar/vbar styles directly, using the line’s MarkerHandle property:

hLine.MarkerHandle.Style = 'hbar';
set(hLine.MarkerHandle, 'Style','hbar');  % alternative

hLine.MarkerHandle.Style='hbar'

hLine.MarkerHandle.Style='hbar'

hLine.MarkerHandle.Style='vbar'

hLine.MarkerHandle.Style='vbar'

USA visit

I will be travelling in the US in May/June 2019. Please let me know (altmany at gmail) if you would like to schedule a meeting or onsite visit for consulting/training, or perhaps just to explore the possibility of my professional assistance to your Matlab programming needs.

]]>
https://undocumentedmatlab.com/blog_old/undocumented-plot-marker-types/feed 1
Matlab toolstrip – part 4 (control customization)https://undocumentedmatlab.com/blog_old/matlab-toolstrip-part-4-control-customization https://undocumentedmatlab.com/blog_old/matlab-toolstrip-part-4-control-customization#respond Sun, 30 Dec 2018 16:00:00 +0000 https://undocumentedmatlab.com/?p=8110 Related posts:
  1. Matlab toolstrip – part 6 (complex controls) Multiple types of customizable controls can be added to Matlab toolstrips...
  2. Figure window customizations Matlab figure windows can be customized in numerous manners using the underlying Java Frame reference. ...
  3. Matlab toolstrip – part 2 (ToolGroup App) Matlab users can create custom Apps with toolstrips and docked figures. ...
  4. Matlab toolstrip – part 3 (basic customization) Matlab toolstrips can be created and customized in a variety of ways. ...
]]>
In a previous post I showed how we can create custom Matlab app toolstrips. Toolstrips can be a bit complex to develop so I’m trying to proceed slowly, with each post in the miniseries building on the previous posts. I encourage you to review the earlier posts in the Toolstrip miniseries before reading this post. In today’s post we continue the discussion of the toolstrip created in the previous post:
Toolstrip example (basic controls)

Toolstrip example (basic controls)

Today’s post will show how to attach user-defined functionality to toolstrip components, as well as some additional customizations. At the end of today’s article, you should be able to create a fully-functional custom Matlab toolstrip. Today’s post will remain within the confines of a Matlab “app”, i.e. a tool-group that displays docked figures. Future posts will discuss lower-level toolstrip mechanisms, that enable advanced customizations as well as integration in legacy (Java-based, even GUIDE-created) Matlab figures.

Control callbacks

Controls are useless without settable callbacks that affect the program state based on user interactions. There are two different mechanisms for setting callbacks for Matlab toolstrip controls. Refer to the example in the previous post:

  1. Setting the control’s callback property or properties – the property names differ across components (no, for some reason it’s never as simple as Callback in standard uicontrols). For example, the main action callback for push-buttons is ButtonPushedFcn, for toggle-buttons and checkboxes it’s ValueChangedFcn and for listboxes it’s . Setting the callback is relatively easy:
    hColorbar.ValueChangedFcn = @toggleColorbar;
     
    function toggleColorbar(hAction,hEventData)
        if hAction.Selected
            colorbar;
        else
            colorbar('off');
        end
    end

    The hAction object that is passed to the callback function as the first input arg contains various fields of interest, but for some reason the most important object property (Value) is renamed as the Selected property (most confusing). Also, a back-reference to the originating control (hColorbar in this example), which is important for many callbacks, is also missing (and no – I couldn’t find it in the hidden properties either):

    >> hAction
    hAction = 
      Action with properties:
     
                Description: 'Toggle colorbar display'
                    Enabled: 1
                   Shortcut: ''
                   Selected: 1
            QuickAccessIcon: []
        SelectionChangedFcn: @toggleColorbar
                       Text: 'Colorbar'
            IsInQuickAccess: 0
                ButtonGroup: []
                       Icon: [1×1 matlab.ui.internal.toolstrip.Icon]
     
    >> hEventData
    hEventData = 
      ToolstripEventData with properties:
     
        EventData: [1×1 struct]
           Source: [0×0 handle]
        EventName: ''
     
    >> hEventData.EventData
    ans = 
      struct with fields:
     
        Property: 'Value'
        NewValue: 1
        OldValue: 0

    Note that hEventData.Source is an empty handle for some unknown reason.

    The bottom line is that to reference the button state using this callback mechanism we need to either:

    1. Access hAction‘s Selected property which stands-in for the originating control’s Value property (this is what I have shown in the short code snippet above)
    2. Access hEventData.EventData and use its reported Property, NewValue and OldValue fields
    3. Pass the originating control handle as an extra (3rd) input arg to the callback function, and then access it from within the callback. For example:
      hColorbar.ValueChangedFcn = {@toggleColorbar, hColorbar};
       
      function toggleColorbar(hAction,hEventData,hButton)
          if hButton.Value %hAction.Selected
              colorbar;
          else
              colorbar('off');
          end
      end
  2. As an alternative, we can use the addlistener function to attach a callback to control events. Practically all toolstrip components expose public events that can be listened-to using this mechanism. In most cases the control’s callback property name(s) closely follow the corresponding events. For example, for buttons we have the ValueChanged event that corresponds to the ValueChangedFcn property. We can use listeners as follows:
    hCheckbox.addlistener('ValueChanged',@toggleLogY);
     
    function toggleLogY(hCheckbox,hEventData)
        if hCheckbox.Value, type = 'log'; else, type = 'linear'; end
        set(gca, 'XScale',type, 'YScale',type, 'ZScale',type);
    end

    Note that when we use the addlistener mechanism to attach callbacks, we don’t need any of the tricks above – we get the originating control handle as the callback function’s first input arg, and we can access it directly.

    Unfortunately, we cannot pass extra args to the callback that we specify using addlistener (this seems like a trivial and natural thing to have, for MathWorks’ attention…). In other words, addlistener only accepts a function handle as callback, not a cell array. To bypass this limitation in uicontrols, we typically add the extra parameters to the control’s UserData or ApplicationData properties (the latter via the setappdata function). But alas – toolstrip components have neither of these properties, nor can we add them in runtime (as with for other GUI controls). So we need to find some other way to pass these extra values, such as using global variables, or making the callback function nested so that it could access the parent function’s workspace.

Additional component properties

Component text labels, where relevant, can be set using the component’s Text property, and the tooltip can be set via the Description property. As I noted in my previous post, I believe that this is an unfortunate choice of property names. In addition, components have control-specific properties such as Value (checkboxes and toggle buttons). These properties can generally be modified in runtime, in order to reflect the program state. For example, we can disable/enable controls, and modify their label, tooltip and state depending on the control’s new state and the program state in general.

The component icon can be set via the Icon property, where available (for example, buttons have an icon, but checkboxes do not). There are several different ways in which we can set this Icon. I will discuss this in detail in the following post; in the meantime you can review the usage examples in the previous post.

There are a couple of additional hidden component properties that seem promising, most notably Shortcut and Mnemonic (the latter (Mnemonic) is also available in Section and Tab, not just in components). Unfortunately, at least as of R2018b these properties do not seem to be connected yet to any functionality. In the future, I would expect them to correspond to keyboard shortcuts and underlined mnemonic characters, as these functionalities behave in standard menu items.

Accessing the underlying Java control

As long as we’re not displaying the toolstrip on a browser page (i.e., inside a uifigure or Matlab Online), the toolstrip is basically composed of Java Swing components from the com.mathworks.toolstrip.components package (such as TSButton or TSCheckBox). I will discuss these Java classes and their customizations in a later post, but for now I just wish to show how to access the underlying Java component of any Matlab MCOS control. This can be done using a central registry of toolstrip components (so-called “widgets”), which is accessible via the ToolGroup‘s hidden ToolstripSwingService property, and then via each component’s hidden widget Id. For example:

>> widgetRegistry = hToolGroup.ToolstripSwingService.Registry;
>> jButton = widgetRegistry.getWidgetById(hButton.getId)  % get the hButton's underlying Java control
ans =
com.mathworks.toolstrip.components.TSToggleButton[,"Colorbar",layout<>,NORMAL]

We can now apply a wide variety of Java-based customizations to the retrieved jButton, as I have shown in many other articles on this website over the past decade.

Another way to access the toolstrip Java component hierarchy is via hToolGroup.Peer.get(tabIndex).getComponent. This returns the top-level Java control representing the tab whose index in tabIndex (0=left-most tab):

>> jToolGroup = hToolGroup.Peer;  % or: =hToolGroup.ToolstripSwingService.SwingToolGroup;
>> jDataTab = jToolGroup.get(0).getComponent;  % Get tab #0 (first tab: "Data")
>> jDataTab.list   % The following is abridged for brevity
com.mathworks.toolstrip.impl.ToolstripTabContentPanel[tab0069230a-52b0-4973-b025-2171cd96301b,0,0,831x93,...]
 SectionWrapper(section54fb084c-934d-4d31-9468-7e4d66cd85e5)
  com.mathworks.toolstrip.impl.ToolstripSectionComponentWithHeader[,0,0,241x92,...]
   com.mathworks.toolstrip.components.TSPanel[section54fb084c-934d-4d31-9468-7e4d66cd85e5,,layout<HORIZONTAL>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSButton[,"Refresh all",layout<>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSButton[,"Refresh X,Y",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSButton[,"Refresh Y,Z",layout<>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSButton[,"Refresh X",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSButton[,"Refresh Y",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSButton[,"Refresh Z",layout<>,NORMAL]
 SectionWrapper(sectionebd8ab95-fd33-4a3d-8f24-152589713994)
  com.mathworks.toolstrip.impl.ToolstripSectionComponentWithHeader[,0,0,159x92,...]
   com.mathworks.toolstrip.components.TSPanel[sectionebd8ab95-fd33-4a3d-8f24-152589713994,,layout<HORIZONTAL>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSCheckBox[,"Axes borders",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSCheckBox[,"Log scaling",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSCheckBox[,"Inverted Y",layout<>,NORMAL]
 SectionWrapper(section01995bfd-61de-490f-aa22-de50bae1af75)
  com.mathworks.toolstrip.impl.ToolstripSectionComponentWithHeader[,0,0,125x92,...]
   com.mathworks.toolstrip.components.TSPanel[section01995bfd-61de-490f-aa22-de50bae1af75,,layout<HORIZONTAL>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSToggleButton[,"Legend",layout<>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSLabel[null," ",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSToggleButton[,"Colorbar",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSLabel[null," ",layout<>,NORMAL]
 com.mathworks.mwswing.MJButton[toolstrip.header.collapseButton,808,70,20x20,...]

Toolstrip miniseries roadmap

The next post will discuss icons, for both toolstrip controls as well as the ToolGroup app window.

I plan to discuss complex components in subsequent posts. Such components include button-group, drop-down, listbox, split-button, slider, popup form, gallery etc.

Following that, my plan is to discuss toolstrip collapsibility, the ToolPack framework, docking layout, DataBrowser panel, QAB (Quick Access Bar), underlying Java controls, and adding toolstrips to figures – not necessarily in this order.

Have I already mentioned that Matlab toolstrips can be a bit complex?

If you would like me to assist you in building a custom toolstrip or GUI for your Matlab program, please let me know.

Happy New Year, everyone!

]]>
https://undocumentedmatlab.com/blog_old/matlab-toolstrip-part-4-control-customization/feed 0
Customizing contour plots part 2https://undocumentedmatlab.com/blog_old/customizing-contour-plots-part2 https://undocumentedmatlab.com/blog_old/customizing-contour-plots-part2#comments Sun, 12 Nov 2017 11:03:37 +0000 https://undocumentedmatlab.com/?p=7149 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. Customizing contour plots Contour labels, lines and fill patches can easily be customized in Matlab HG2. ...
  3. Matlab’s HG2 mechanism HG2 is presumably the next generation of Matlab graphics. This article tries to explore its features....
  4. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
]]>
A few weeks ago a user posted a question on Matlab’s Answers forum, asking whether it is possible to display contour labels in the same color as their corresponding contour lines. In today’s post I’ll provide some insight that may assist users with similar customizations in other plot types.

Matlab does not provide, for reasons that escape my limited understanding, documented access to the contour plot’s component primitives, namely its contour lines, labels and patch faces. Luckily however, these handles are accessible (in HG2, i.e. R2014b onward) via undocumented hidden properties aptly named EdgePrims, TextPrims and FacePrims, as I explained in a previous post about contour plots customization, two years ago.

Let’s start with a simple contour plot of the peaks function:

[X,Y,Z] = peaks;
[C,hContour] = contour(X,Y,Z, 'ShowText','on', 'LevelStep',1);

The result is the screenshot on the left:

Standard Matlab contour labels

Standard Matlab contour labels

 
Customized Matlab contour labels

Customized Matlab contour labels

In order to update the label colors (to get the screenshot on the right), we create a short updateContours function that updates the TextPrims color to their corresponding EdgePrims color:

The updateContours() function

function updateContours(hContour)
    % Update the text label colors
    drawnow  % very important!
    levels = hContour.LevelList;
    labels = hContour.TextPrims;  % undocumented/unsupported
    lines  = hContour.EdgePrims;  % undocumented/unsupported
    for idx = 1 : numel(labels)
        labelValue = str2double(labels(idx).String);
        lineIdx = find(abs(levels-labelValue)<10*eps, 1);  % avoid FP errors using eps
        labels(idx).ColorData = lines(lineIdx).ColorData;  % update the label color
        %labels(idx).Font.Size = 8;                        % update the label font size
    end
    drawnow  % optional
end

Note that in this function we don’t directly equate the numeric label values to the contour levels’ values: this would work well for integer values but would fail with floating-point ones. Instead I used a very small 10*eps tolerance in the numeric comparison.

Also note that I was careful to call drawnow at the top of the update function, in order to ensure that EdgePrims and TextPrims are updated when the function is called (this might not be the case before the call to drawnow). The final drawnow at the end of the function is optional: it is meant to reduce the flicker caused by the changing label colors, but it can be removed to improve the rendering performance in case of rapidly-changing contour plots.

Finally, note that I added a commented line that shows we can modify other label properties (in this case, the font size from 10 to 8). Feel free to experiment with other label properties.

Putting it all together

The final stage is to call our new updateContours function directly, immediately after creating the contour plot. We also want to call updateContours asynchronously whenever the contour is redrawn, for example, upon a zoom/pan event, or when one of the relevant contour properties (e.g., LevelStep or *Data) changes. To do this, we add a callback listener to the contour object’s [undocumented] MarkedClean event that reruns our updateContours function:

[X,Y,Z] = peaks;
[C,hContour] = contour(X,Y,Z, 'ShowText','on', 'LevelStep',1);
 
% Update the contours immediately, and also whenever the contour is redrawn
updateContours(hContour);
addlistener(hContour, 'MarkedClean', @(h,e)updateContours(hContour));

Contour level values

As noted in my comment reply below, the contour lines (hContour.EdgePrims) correspond to the contour levels (hContour.LevelList).

For example, to make all negative contour lines dotted, you can do the following:

[C,hContour] = contour(peaks, 'ShowText','on', 'LevelStep',1); drawnow
set(hContour.EdgePrims(hContour.LevelList<0), 'LineStyle', 'dotted');

Customized Matlab contour lines

Customized Matlab contour lines

Prediction about forward compatibility

As I noted on my previous post on contour plot customization, 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. Note that these properties have not changed their names or functionality in the past 3 years, so while it could well happen next year, it could also remain unchanged for many years to come. The exact same thing can be said for the MarkedClean event.

Professional assistance anyone?

As shown by this and many other posts on this site, a polished interface and functionality is often composed of small professional touches, many of which are not exposed in the official Matlab documentation for various reasons. So if you need top-quality professional appearance/functionality in your Matlab program, or maybe just a Matlab program that is dependable, robust and highly-performant, consider employing my consulting services.

]]>
https://undocumentedmatlab.com/blog_old/customizing-contour-plots-part2/feed 9
Customizing axes part 5 – origin crossover and labelshttps://undocumentedmatlab.com/blog_old/customizing-axes-part-5-origin-crossover-and-labels https://undocumentedmatlab.com/blog_old/customizing-axes-part-5-origin-crossover-and-labels#comments Wed, 27 Jul 2016 17:00:02 +0000 https://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. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
]]>
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_old/customizing-axes-part-5-origin-crossover-and-labels/feed 9
Figure window customizationshttps://undocumentedmatlab.com/blog_old/figure-window-customizations https://undocumentedmatlab.com/blog_old/figure-window-customizations#respond Wed, 01 Jun 2016 08:00:11 +0000 https://undocumentedmatlab.com/?p=6439 Related posts:
  1. Minimize/maximize figure window Matlab figure windows can easily be maximized, minimized and restored using a bit of undocumented magic powder...
  2. 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....
  3. Uitable sorting Matlab's uitables can be sortable using simple undocumented features...
  4. Frameless (undecorated) figure windows Matlab figure windows can be made undecorated (borderless, title-less). ...
]]>
A friend recently asked me, in light of my guesstimate that Java-based Matlab figures will be replaced by web-based figures sometime around 2018-2020, whether there are any “killer features” that make it worthwhile to use undocumented Java-based tricks today, despite the fact that they will probably break in 2-5 years. In my opinion, there are many such features; today I will focus on just a subset of them – those features that relate to the entire figure window.

Over the years I wrote many articles here about figure-level customizations, as well as an entire chapter in my Matlab-Java programming book. So today’s post will be a high-level overview, and users who are interested in any specific topic can visit the referenced links for the implementation details.

An undecorated Matlab figure window - one of many possible figure-level customizations
An undecorated Matlab figure window – one of many possible figure-level customizations

JavaFrame

JavaFrame is an undocumented hidden property of the figure handle that provides access to the underlying Java window (JFrame) peer object’s reference. Since R2008a, a warning is issued whenever we retrieve this property:

>> jFrame = get(gcf,'JavaFrame');
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.) 

Until HG2 (R2014b+) we could suppress the warning by simply wrapping the figure handle within a handle() call, as explained here. Since R2014b we need to use the warning function to do this:

warning('off', 'MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');

We can do several things directly with the JavaFrame‘s properties and methods, including:

  • Maximize/minimize/restore the window, via the properties Maximized/Minimized (which accept and return a boolean (logical) value), or the corresponding methods jFrame.isMaximized(), isMinimized(), setMaximized(flag), setMinimized(flag). details
  • Modify the container to which the figure will be docked. By default this is the “Figures” container, but this can be changed to any user-specified container, or even to the “Editor”, using the GroupName property or its associated methods. See the related setFigDockGroup utility that I posted on the Matlab File exchange.
  • Remove the top separator line between the toolbar and the content-pane, to blend them together, via the jFrame.showTopSeparator(flag) method.
  • Retrieve a direct Java reference to the Matlab Desktop and the figure’s internal containers via the Desktop and FigurePanelContainer properties, respectively (we can also get those references by other means).
  • Retrieve a direct Java reference to the containing JFrame (Java window), as discussed below
  • A few other features that I will not discuss here

MathWorks have set up a dedicated webpage where you can specify how you are using JavaFrame and why it is important for you: http://www.mathworks.com/javaframe. I encourage you to use this webpage to tell MathWorks which features are important for you. This will help them to decide which functionality should be added to the new web-based figures.

JFrame window

The JavaFrame handle enables direct retrieval of the containing Java JFrame (window) reference, using several alternatives. Here are two of these alternatives (there are others):

% Alternative #1
>> jWindow = jFrame.getFigurePanelContainer.getTopLevelAncestor
jWindow = 
com.mathworks.hg.peer.FigureFrameProxy$FigureFrame[fClientProxyFrame,72,62,576x507,...]
 
% Alternative #2
try
    jClient = jFrame.fFigureClient;  % This works up to R2011a
catch
    try
        jClient = jFrame.fHG1Client;  % This works from R2008b-R2014a
    catch
        jClient = jFrame.fHG2Client;  % This works from R2014b and up
    end
end
jWindow = jClient.getWindow;

Customized menu items Customized menu items
Integrated figure status bar

Customized menu items (top) and figure status bar (bottom)

With the retrieved jWindow reference, we can do several additional interesting things:

  • Enable/disable the entire figure in a single go (details)
  • Remove/restore the window frame (borders and title bar), otherwise known as an “undecorated window” (details)
  • Set the figure window to be “Always-On-Top”, i.e. not occluded by any other window, via the AlwaysOnTop property, or the corresponding jWindow.isAlwaysOnTop(), setAlwaysOnTop(flag) methods.
  • Make the figure window fully or partially transparent (details). Note: this fails on R2013b/Java7 and higher due to a change in the way that transparency works in Java 7 compared to earlier releases; in other words blame Oracle’s Java, not MathWorks’ Matlab….
  • Blur/restore the figure window (details). This too works only up to R2013a.
  • Detect and handle window-level focus gain/loss events (details), as well as window-level mouse events (enter/exit/hover etc. – details).
  • Customize the figure’s menu bar – dynamic behavior, tooltips, highlights, keyboard shortcuts/accelerators, font colors/styles, callbacks, icons etc. (details1, details2)
  • Control figure docking in compiled (deployed) applications (details1, details2)
  • Display an integral figure status-bar with text and GUI controls (details1, details2).
  • A few other features that I will not discuss here

As you can see, there are numerous very interesting customizations that can be done to Matlab figures which rely on the undocumented implementation. Here are a couple of usage examples that you can easily adapt (follow the links above for additional details and usage examples):

jWindow.setEnabled(false);     % disable entire figure [true/false]
jWindow.setMinimized(true);    % minimize window [true/false]
jWindow.setMaximized(true);    % maximize window [true/false]
jWindow.setAlwaysOnTop(true);  % set to be always on top [true/false]
 
% Set a Matlab callback function to a window focus-gain event
hjWindow = handle(jWindow, 'CallbackProperties');
hjWindow.FocusGainedCallback = @myCallbackFunc;

In addition to the Java-based features above, some functionalities can also be achieved via direct OS manipulations, for example using Jan Simon’s great WindowAPI utility (Windows-only), although I typically prefer using the Java approach since it is cross-platform compatible.

Using all these features is super-easy, so there is not really a question of code complexity or technical risk – the main question is whether to accept the risk that the associated code will stop working when Matlab figures will eventually become web-based.

So is it worth the risk?

This is an excellent question. I contend that the answer depends on the specific use-case. In one project you may decide that it is indeed worth-while to use these undocumented features today, whereas in another GUI you may decide that it is not.

It might make sense to use the features above in any of the following circumstances:

  • If you need any of the features in your Matlab GUI today. In this case, you really have no alternative other than to use these features, since there is no documented way to achieve the required functionality.
  • If you do not plan to upgrade your Matlab release soon, or at least after the Java-based figures are discontinued in a few years. The commercial Matlab license is perpetual, enabling users to enjoy these features for as long as they continue using this Matlab release.
  • If you are compiling your Matlab program using the Matlab Compiler or Coder toolboxes. In such cases, the executable will remain static, until such time (if ever) that you decide to recompile it using a newer Matlab release. Users of the compiled code could continue to use the compiled undocumented features well into the future, for as long as their computers keep running. In such cases, we are not concerned with release compatibility issues.
  • If you accept the risk that some recoding may be necessary in the future, or that some functionality will degrade, for the added benefit that they provide your GUIs today.
  • If you are willing to code without MathWorks’ official support and endorsement, and accept the fact that they will not fix any internal bugs that you may discover which is related to these features.
  • If you wish to present a professional-grade GUI today, and worry about potential incompatibilities only if and when they eventually arrive, sometime in the future.

Here’s another twist to consider: do not take it for granted that when web-based uifigures replace Java-based figures all the documented functionality will work as-is on the new uifigures just as they have on the old figures. In fact, I personally believe that we will need to extensively modify our GUI code to make it compatible with the new uifigures. In other words, avoiding the undocumented hacks above will probably not save us from the need to recode (or at least adapt) our GUI, it will just reduce the necessary work somewhat. We encountered a similar situation with the graphics hacks that I exposed over the years: many people avoided them in the fear that they might someday break; then when R2014b came and HG2 graphics replaced HG1, it turned out that many of these supposedly risky hacks continued working in HG2 (examples: LooseInset, YLimInclude) whereas quite a bit of standard fully-documented Matlab functionality was broken and required some recoding. I believe that the lessons from the HG2 migration were well studied and assimilated by MathWorks, but realistically speaking we should not expect a 100% full-proof transition to uifigures.

Still, accepting the risk does not mean that we should bury our head in the sand. Whenever using any undocumented feature in your code, I strongly suggest to use defensive coding practices, such as wrapping your code within try-catch blocks. This way, even if the feature is removed in R2020a (or whenever), the program will still run, albeit with somewhat diminished functionality, or in other words, graceful degradation. For example:

try
    jFrame = get(hFig, 'JavaFrame');
    jFrame.setMaximized(true);
catch
    oldUnits = get(hFig, 'Units');
    set(hFig, 'Units','norm', 'Pos',[0,0,1,1]);
    set(hFig, 'Units',oldUnits);
end

Once again, I urge you to visit http://www.mathworks.com/javaframe and tell MathWorks which of the above features are important for you. The more users tell MathWorks that they depend on a specific feature, the more would MathWorks be likely to invest R&D efforts in enabling it in the future web-based figures.

]]>
https://undocumentedmatlab.com/blog_old/figure-window-customizations/feed 0
Customizing contour plotshttps://undocumentedmatlab.com/blog_old/customizing-contour-plots https://undocumentedmatlab.com/blog_old/customizing-contour-plots#comments Wed, 18 Nov 2015 18:00:55 +0000 https://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. Customizing contour plots part 2 Matlab contour labels' color and font can easily be customized. ...
  3. Matlab’s HG2 mechanism HG2 is presumably the next generation of Matlab graphics. This article tries to explore its features....
  4. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
]]>
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.

Addendum November 11, 2017: The TextPrims, EdgePrims and FacePrims properties have still not changed their names and functionality. I explained a nice use for them in a followup post, explaining how we can modify the contour labels to have different font sizes and the same colors as their corresponding contour lines.

]]>
https://undocumentedmatlab.com/blog_old/customizing-contour-plots/feed 10
Figure keypress modifiershttps://undocumentedmatlab.com/blog_old/figure-keypress-modifiers https://undocumentedmatlab.com/blog_old/figure-keypress-modifiers#comments Wed, 04 Nov 2015 21:19:59 +0000 https://undocumentedmatlab.com/?p=6059 Related posts:
  1. Matlab toolstrip – part 4 (control customization) Matlab toolstrip components (controls) can be customized in various ways, including user-defined callbacks. ...
  2. 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....
  3. Customizing print setup Matlab figures print-setup can be customized to automatically prepare the figure for printing in a specific configuration...
  4. Minimize/maximize figure window Matlab figure windows can easily be maximized, minimized and restored using a bit of undocumented magic powder...
]]>
Matlab figures have a documented property called SelectionType that returns information about keypress modifiers such as or that were pressed during mouse clicks. Using this property has several drawbacks IMHO:

  • The reported SelectionType value is 'extend' for shift-clicks and 'alt' for ctrl-clicks, not very intuitive.
  • There is no support for alt-clicks, which are reported as regular ('normal') mouse clicks. In fact, 'alt' is reported for ctrl-clicks rather than for alt-clicks, which is very confusing.
  • There is no support for modifier combinations such as ctrl+shift-click. These again are reported as regular ('normal') mouse clicks.
  • SelectionType is only updated for mouse clicks, not for keyboard clicks. This again is very confusing. To extract the keypress modifier for key-click events we need to get the Modifier property of the key-press event, and this returns a cell-array of strings (e.g., {'shift','control','alt'}). Note that in this case, unlike SelectionType, the modifier names are as expected, alt-clicks is recognised and so are modifier combinations.
% Documented: we need to get the modifier data in two different manners
set(gcf, 'WindowButtonDownFcn', @(h,e) disp(get(gcf,'SelectionType')));  % mouse clicks: displays a single string: 'normal','alt','extend' or 'open'
set(gcf, 'WindowKeyPressFcn',   @(h,e) disp(e.Modifier));  % keyboard clicks: displays a cell-array of strings, e.g. {'shift','control','alt'}

The inconsistency between the functionality for mouse and keyboard clicks, and the limitations of the SelectionType property, are striking and most annoying. Some might say that it’s been like this for the past 2 decades so Matlab users have probably gotten used to it by now. But I must admit that after over 2 decades with Matlab I still need to refer to the documentation to figure out the correct behavior. Maybe that’s because I’m getting old, or maybe it means that the behavior is indeed not as intuitive as one could hope for.

For this reason, I was very happy to discover several years ago that there was a much simpler, easier and consistent solution, although (alas) undocumented. The trick is to simply query the undocumented hidden figure property CurrentModifier: CurrentModifier is updated during both mouse and keyboard clicks, in the same consistent manner, in both cases returning the same cell-array of strings as the Modifier property of the standard key-press event:

% Undocumented: the following displays a cell-array of strings having a consistent format, e.g. {'shift','control','alt'}
set(gcf, 'WindowButtonDownFcn', @(h,e) disp(get(gcf,'CurrentModifier')));  % mouse clicks
set(gcf, 'WindowKeyPressFcn',   @(h,e) disp(get(gcf,'CurrentModifier')));  % keyboard clicks

Checking whether any modifier was pressed becomes trivial:

modifiers = get(gcf,'CurrentModifier');
wasShiftPressed = ismember('shift',   modifiers);  % true/false
wasCtrlPressed  = ismember('control', modifiers);  % true/false
wasAltPressed   = ismember('alt',     modifiers);  % true/false

Hurrah for simplicity and consistency!

Note that despite the fact that CurrentModifier is hidden and undocumented, it has existed as-is for many years, and even survived (unchanged) the major transition from HG1 to HG2 in R2014b. This has to mean that MathWorks recognized the importance of CurrentModifier and took the deliberate decision (and associate dev effort) to preserve it. So why wasn’t this useful property made documented ages ago, or at the very least at the HG2 transition point? I don’t have a good answer to this riddle.

]]>
https://undocumentedmatlab.com/blog_old/figure-keypress-modifiers/feed 6
Using linkaxes vs. linkprophttps://undocumentedmatlab.com/blog_old/using-linkaxes-vs-linkprop https://undocumentedmatlab.com/blog_old/using-linkaxes-vs-linkprop#comments Wed, 22 Jul 2015 20:30:04 +0000 https://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_old/using-linkaxes-vs-linkprop/feed 7
Undocumented view transformation matrixhttps://undocumentedmatlab.com/blog_old/undocumented-view-transformation-matrix https://undocumentedmatlab.com/blog_old/undocumented-view-transformation-matrix#comments Wed, 15 Apr 2015 21:21:51 +0000 https://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. Getting default HG property values Matlab has documented how to modify default property values, but not how to get the full list of current defaults. This article explains how to do this. ...
  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_old/undocumented-view-transformation-matrix/feed 5
Transparent legendhttps://undocumentedmatlab.com/blog_old/transparent-legend https://undocumentedmatlab.com/blog_old/transparent-legend#respond Wed, 11 Mar 2015 19:43:27 +0000 https://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_old/transparent-legend/feed 0
Customizing Matlab uipanelshttps://undocumentedmatlab.com/blog_old/customizing-matlab-uipanels https://undocumentedmatlab.com/blog_old/customizing-matlab-uipanels#comments Wed, 25 Feb 2015 12:24:50 +0000 https://undocumentedmatlab.com/?p=5595 Related posts:
  1. Inactive Control Tooltips & Event Chaining Inactive Matlab uicontrols cannot normally display their tooltips. This article shows how to do this with a combination of undocumented Matlab and Java hacks....
  2. 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...
  3. The javacomponent function Matlab's built-in javacomponent function can be used to display Java components in Matlab application - this article details its usages and limitations...
  4. Transparent Matlab figure window Matlab figure windows can be made fully or partially transparent/translucent or blurred - this article explains how...
]]>
The major innovation in Matlab release R2014b was the introduction of the new handle-based graphics system (HG2). However, this release also included a few other improvements to graphics/GUI that should not be overlooked. The most notable is that uitabs are finally officially documented/supported, following a decade or being undocumented (well, undocumented in the official sense, since I took the time to document this functionality in this blog and in my Matlab-Java book).

A less-visible improvement occurred with uipanels: Panels are very important containers when designing GUIs. They enable a visual grouping of related controls and introduce order to an otherwise complex GUI. Unfortunately, until R2014b panels were drawn at the canvas level, and did not use a standard Java Swing controls like other uicontrols. This made it impossible to customize uipanels in a similar manner to other GUI uicontrols (example).

In R2014b, uipanels have finally become standard Java Swing controls, a com.mathworks.hg.peer.ui.UIPanelPeer$UIPanelJPanel component that extends Swing’s standard javax.swing.JPanel and Matlab’s ubiquitous com.mathworks.mwswing.MJPanel. This means that we can finally customize it in various ways that are not available in plain Matlab.

We start the discussion with a simple Matlab code snippet. It is deliberately simple, since I wish to demonstrate only the panel aspects:

figure('Menubar','none', 'Color','w');
hPanel = uipanel('Title','Panel title', 'Units','norm', 'Pos',[.1,.1,.6,.7]);
hButton = uicontrol('String','Click!', 'Parent',hPanel);

Standard Matlab uipanel

Standard Matlab uipanel

Notice the default ‘etchedin’ panel border, which I hate (note the broken edges at the corners). Luckily, Swing includes a wide range of alternative borders that we can use. I’ve already demonstrated customizing Matlab uicontrols with Java borders back in 2010 (has it really been that long? wow!). In R2014b we can finally do something similar to uipanels:

The first step is to get the uipanel‘s underlying Java component’s reference. We can do this using my findjobj utility, but in the specific case of uipanel we are lucky to have a direct shortcut by using the panel’s undocumented hidden property JavaFrame and its PrintableComponent property:

>> jPanel = hPanel.JavaFrame.getPrintableComponent
jPanel =
com.mathworks.hg.peer.ui.UIPanelPeer$UIPanelJPanel[,0,0,97x74,...]

Let’s now take a look at the jPanel‘s border:

>> jPanel.getBorder
ans =
com.mathworks.hg.peer.ui.borders.TitledBorder@25cd9b97
 
>> jPanel.getBorder.get
                Border: [1x1 com.mathworks.hg.peer.ui.borders.EtchedBorderWithThickness]
          BorderOpaque: 0
                 Class: [1x1 java.lang.Class]
                 Title: 'Panel title'
            TitleColor: [1x1 java.awt.Color]
             TitleFont: [1x1 java.awt.Font]
    TitleJustification: 1
         TitlePosition: 2

Ok, simple enough. Let’s replace the border’s EtchedBorderWithThickness with something more appealing. We start with a simple red LineBorder having rounded corners and 1px width:

jColor = java.awt.Color.red;  % or: java.awt.Color(1,0,0)
jNewBorder = javax.swing.border.LineBorder(jColor, 1, true);  % red, 1px, rounded=true
jPanel.getBorder.setBorder(jNewBorder);
jPanel.repaint;  % redraw the modified panel

Rounded-corners LineBorder

Rounded-corners LineBorder

Or maybe a thicker non-rounded orange border:

jColor = java.awt.Color(1,0.5,0);
jNewBorder = javax.swing.border.LineBorder(jColor, 3, false);  % orange, 3px, rounded=false
jPanel.getBorder.setBorder(jNewBorder);
jPanel.repaint;  % redraw the modified panel

Another LineBorder example

Another LineBorder example

Or maybe a MatteBorder with colored insets:

jColor = java.awt.Color(0,0.3,0.8);  % light-blue
jNewBorder = javax.swing.border.MatteBorder(2,5,8,11,jColor)  % top,left,bottom,right, color
jPanel.getBorder.setBorder(jNewBorder);
jPanel.repaint;  % redraw the modified panel

MatteBorder with solid insets

MatteBorder with solid insets

MatteBorder can also use an icon (rather than a solid color) to fill the border insets. First, let’s load the icon. We can either load a file directly from disk, or use one of Matlab’s standard icons. Here are both of these alternatives:

% Alternative #1: load from disk file
icon = javax.swing.ImageIcon('C:\Yair\star.gif');
 
% Alternative #2: load a Matlab resource file
jarFile = fullfile(matlabroot,'/java/jar/mlwidgets.jar');
iconsFolder = '/com/mathworks/mlwidgets/graphics/resources/';
iconURI = ['jar:file:/' jarFile '!' iconsFolder 'favorite_hoverover.png'];  % 14x14 px
icon = javax.swing.ImageIcon(java.net.URL(iconURI));

We can now pass this icon reference to MatteBorder‘s constructor:

w = icon.getIconWidth;
h = icon.getIconHeight;
jNewBorder = javax.swing.border.MatteBorder(h,w,h,w,icon)  % top,left,bottom,right, icon
jPanel.getBorder.setBorder(jNewBorder);
jPanel.repaint;  % redraw the modified panel

MatteBorder with icon insets

MatteBorder with icon insets

Additional useful Swing borders can be found in the list of classes implementing the Border interface, or via the BorderFactory class. For example, let’s create a dashed border having a 3-2 ratio between the line lengths and the spacing:

jColor = java.awt.Color.blue;  % or: java.awt.Color(0,0,1);
lineWidth = 1;
relativeLineLength = 3;
relativeSpacing = 2;
isRounded = false;
jNewBorder = javax.swing.BorderFactory.createDashedBorder(jColor,lineWidth,relativeLineLength,relativeSpacing,isRounded);
jPanel.getBorder.setBorder(jNewBorder);
jPanel.repaint;  % redraw the modified panel

StrokeBorder (dashed)

StrokeBorder (dashed)

After seeing all these possibilities, I think you’ll agree with me that Matlab’s standard uipanel borders look pale in comparison.

Have you used any interesting borders in your Matlab GUI? Or have you customized your panels in some other nifty manner? If so, then please place a comment below.

]]>
https://undocumentedmatlab.com/blog_old/customizing-matlab-uipanels/feed 14
Accessing hidden HG2 plot functionalityhttps://undocumentedmatlab.com/blog_old/hidden-hg2-plot-functionality https://undocumentedmatlab.com/blog_old/hidden-hg2-plot-functionality#comments Wed, 04 Feb 2015 20:14:20 +0000 https://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. 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 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_old/hidden-hg2-plot-functionality/feed 19
Plot markers transparency and color gradienthttps://undocumentedmatlab.com/blog_old/plot-markers-transparency-and-color-gradient https://undocumentedmatlab.com/blog_old/plot-markers-transparency-and-color-gradient#comments Wed, 19 Nov 2014 16:42:11 +0000 https://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’.

p.s. – other chart types have similar internal objects that can be customized. For example, bar charts have internal Face and Edge properties that correspond to internal objects that can be similarly modified:

>> get(h.Edge)
          AlignVertexCenters: 'on'
             AmbientStrength: 0.3
                ColorBinding: 'object'
                   ColorData: [4×1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'on'
                     HitTest: 'off'
                       Layer: 'middle'
                    LineJoin: 'miter'
                   LineStyle: 'solid'
                   LineWidth: 0.5
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1×1 Bar]
                         ...
]]>
https://undocumentedmatlab.com/blog_old/plot-markers-transparency-and-color-gradient/feed 83
Plot line transparency and color gradienthttps://undocumentedmatlab.com/blog_old/plot-line-transparency-and-color-gradient https://undocumentedmatlab.com/blog_old/plot-line-transparency-and-color-gradient#comments Fri, 14 Nov 2014 00:14:10 +0000 https://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_old/plot-line-transparency-and-color-gradient/feed 43
Customizing axes part 4 – additional propertieshttps://undocumentedmatlab.com/blog_old/customizing-axes-part-4-additional-properties https://undocumentedmatlab.com/blog_old/customizing-axes-part-4-additional-properties#comments Wed, 29 Oct 2014 18:00:08 +0000 https://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_old/customizing-axes-part-4-additional-properties/feed 5
Customizing axes part 3 – Backdrophttps://undocumentedmatlab.com/blog_old/customizing-axes-part-3-backdrop https://undocumentedmatlab.com/blog_old/customizing-axes-part-3-backdrop#comments Wed, 22 Oct 2014 18:00:50 +0000 https://undocumentedmatlab.com/?p=5136 Related posts:
  1. Customizing axes part 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
  2. Plot line transparency and color gradient Static and interpolated (gradient) colors and transparency can be set for plot lines in HG2. ...
  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 two weeks I explained how HG2 (in R2014b) enables us to customize the axes rulers, baselines, box and grid-lines in ways that were previously impossible in HG1 (R2014a or earlier). Today I will describe another useful undocumented property of the HG2 axes – Backdrop.

HG2 axes Backdrop with custom tint

HG2 axes Backdrop with custom tint


Backdrop

The axes’ Backdrop property (a matlab.graphics.axis.decorator.Backdrop object) controls the background color of the axes’ content area. This is normally white, but can be set to any color, including translucency:

% Create the plot
x = -10:0.1:10;
y = 1e7*sin(x)./x; 
hLine = plot(x,y);
hAxes = gca;
box('off');
 
% Set a light-yellow backdrop
hAxes.Backdrop.FaceColor = [1, 1, 0, 0.4];

HG2 axes Backdrop

HG2 axes Backdrop

By default, Backdrop spans the entire (rectangular) axes content area. But we can customize its vertex points using the Face.VertexData property, which accepts an array of 3xN singles:

HG2 axes Backdrop with custom vertex points

HG2 axes Backdrop with custom vertex points

% Note: VertexData is specified in normalized (0-1) units, not data units
hAxes.Backdrop.Face.VertexData = single([0,1,0.8,0; 0,0.43,0.75,1; 0,0,0,0]);  % default: [0,1,1,0; 0,0,1,1; 0,0,0,0]
 
>> hAxes.Backdrop.Face.get
             AmbientStrength: 0.3
             BackFaceCulling: 'none'
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecoloralpha'
             DiffuseStrength: 0.6
            HandleVisibility: 'off'
                     HitTest: 'off'
                       Layer: 'back'
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 Backdrop]
               PickableParts: 'all'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: []
                     Texture: []
            TwoSidedLighting: 'off'
                  VertexData: [3x4 single]
               VertexIndices: []
                     Visible: 'on'

The down side is that whenever we modify anything in the axes (or even get some axes property values), Face.VertexData is reset back to its default value of [0,1,1,0; 0,0,1,1; 0,0,0,0], so we need to either update it in a timer and/or in a property listener.

We can create a tint (gradient color) effect by setting the Face.ColorData property to match the vertex points and modifying Face.ColorBinding from the default ‘object’ to ‘interpolated’ (other alternative values are ‘discrete’ and ‘none’):

colorData = uint8([255, 150, 200, 100; ...  % instead of [255;255;0;102] = light-yellow
                   255, 100,  50, 200; ...
                     0,  50, 100, 150; ...
                   102, 150, 200,  50]);
set(hAxes.Backdrop.Face, 'ColorBinding','interpolated', 'ColorData',colorData);

HG2 axes Backdrop with custom vertex, tint

HG2 axes Backdrop with custom vertex, tint

The tint effect does not rely on the vertex – we can set a tinted background for the entire content area by modifying Face.ColorData without modifying the vertexes:

HG2 axes Backdrop with custom tint

HG2 axes Backdrop with custom tint

Using a patch

Of course, one could say that using Backdrop is not much better than creating a patch that has the specified vertex points and colors. For example (note that Backdrop has different color and vertex units than a patch):

% Create a patch to replace the backdrop
cdata = [255, 150, 200, 100; ...
         255, 100,  50, 200; ...
           0,  50, 100, 150 ...
        ]' / 255;  % alpha values are not directly accepted by patch CData
vertexData = [0,1,0.8,0; 0,0.43,0.75,1];  % in normalized units
xdata = hAxes.XLim(1) + diff(hAxes.XLim)*vertexData(1,:);  % in data units
ydata = hAxes.YLim(1) + diff(hAxes.YLim)*vertexData(2,:);  % in data units
hPatch = patch(xdata, ydata, 'k', 'Parent',hAxes, 'EdgeColor','none', ...
               'FaceVertexCData',cdata, 'FaceColor','interp', ...
               'FaceVertexAlpha',[102;150;200;50]/255, 'FaceAlpha','interp');

This results in a gradient very similar to the Backdrop, although not exactly the same. Perhaps the color interpolation for Backdrop is different than for patches, or maybe I just made a mistake somewhere above…

Standard patch with custom tint

Standard patch with custom tint

I don’t have a good answer as to why MathWorks chose to add the Backdrop property to HG2 axes and what advantage it might have over a separate patch. If anyone has an idea, please post a comment below. We can be pretty sure that this was discussed at MathWorks, since Michelle Hirsch, who heads Matlab product development at MathWorks, posted a utility that implements a patch-based gradient backdrop back in 2010 and has recently rehosted it on GitHub:

Michelle Hirsch's patch-based gradient backdrop utility

Michelle Hirsch's patch-based gradient backdrop utility

Next week, I will conclude the mini-series on axes customizations by describing some additional undocumented axes features.

]]>
https://undocumentedmatlab.com/blog_old/customizing-axes-part-3-backdrop/feed 6
Customizing axes part 2https://undocumentedmatlab.com/blog_old/customizing-axes-part-2 https://undocumentedmatlab.com/blog_old/customizing-axes-part-2#comments Wed, 15 Oct 2014 14:10:13 +0000 https://undocumentedmatlab.com/?p=5126 Related posts:
  1. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  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 4 – additional properties Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
]]>
Last week I explained how HG2 (in R2014b) enables us to customize the axes rulers in ways that were previously impossible in HG1 (R2014a or earlier). Today I will describe other useful undocumented customizations of the HG2 axes:

Baseline

A baseline is a line that has some important meaning. For example, in a plot of the sin function, we might want to place a horizontal baseline at Y=0 or Y=-1, and similarly a vertical baseline at X=0. In HG1, we could do this programmatically using extra line commands (presumably with the additional *LimInclude properties). In HG2 it becomes easier – we can simply update the relevant baseline’s properties: XBaseline, YBaseline and ZBaseLine. All baselines are automatically created with a value of 0, color=gray and Visible=’off’. For example, continuing last week’s example:

HG2 axes Baseline

HG2 axes Baseline

% Create the plot
x = -10:0.1:10;
y = 1e7*sin(x)./x; 
hLine = plot(x,y);
hAxes = gca;
 
% Display the default YBaseline's property values
% Note: there are also plenty of hidden properties that can be gotten with getundoc()
>> hAxes.YBaseline.get
           BaseValue: 0
            Children: []
               Color: [0.15 0.15 0.15]
    HandleVisibility: 'off'
           LineStyle: '-'
           LineWidth: 0.5
              Parent: [1x1 DecorationContainer]
             Visible: 'off'
 
% Display a horizontal YBaseline with custom color
hAxes.YBaseline.Color = 'b';  % default: [0.15 0.15 0.15]
hAxes.YBaseline.Visible = 'on';  % default: 'off'
 
% Hide the bottom x-axis, to make the Y-baseline stand out
hAxes.XRuler.Axle.Visible = 'off';  % default: 'on'

Note the horizontal blue Y-baseline. also note how I hid the bottom x-axis, in order to make the Y-baseline stand out.

Translucent colors can be specified by entering a 4-element color vector. For example: [1, 0, 0, 0.5] means a 50%-transparent red.

For extra prominence, you might want to increase the baseline’s LineWidth from its default value of 0.5 to a value of 1 or 2.

Another useful customization is to set a non-zero BaseValue, or a non-solid baseline LineStyle.

While YBaseline is normally a horizontal line that relates to the Y-axis (hAxes.YBaseline.Axis==1), we can make it vertical attached to the X-axis by simply modifying its Axis value to 0 (0=X, 1=Y, 2=Z).

For the record, baselines are matlab.graphics.axis.decorator.Baseline class objects.

BoxFrame

The BoxFrame property controls the “box” lines that surround the plot when we issue a box(‘on’) command. This is normally a simple black square/rectangle for 2D plots, and a simple black background wireframe for a 3D plot. But who said it needs to be boring?

surf(peaks);  % 3D plot
box on;  % standard black box wireframe
boxFrame = get(gca,'BoxFrame');  % or: hAxes.BoxFrame
set(boxFrame, 'XColor','r', 'YColor','b', 'ZColor','g');  % default colors: [0.15 0.15 0.15]

HG2 axes BoxFrame

HG2 axes BoxFrame


The BoxFrame object is a matlab.graphics.axis.decorator.BoxFrame class object that, like the rulers and baseline, can be customized (once the box(‘on’) command has been issued):

>> get(boxFrame)
           AxesLayer: 'bottom'
            BackEdge: [1x1 LineStrip]
            Children: []
           FrontEdge: [1x1 LineStrip]
    HandleVisibility: 'off'
           LineWidth: 0.5
              Parent: [1x1 DecorationContainer]
             Visible: 'off'
              XColor: [0.15 0.15 0.15]
              YColor: [0.15 0.15 0.15]
              ZColor: [0.15 0.15 0.15]

By default, only the box frame’s back-edge is displayed (axes BoxStyle property =’back’). We can modify this behavior by setting the axes’ BoxStyle to ‘full’, but we can have much more control if we simply update the properties of the box-frame object. For example:

set(boxFrame.FrontEdge, 'Visible','on', 'LineStyle','dashdot', 'LineWidth',1);  %defaults: 'off', 'solid', 0.5

As with the Ruler.Axle objects (see last week’s article), we can customize the Vertex points and colors to achieve very colorful effects (anyone say psychedelic?)

GridHandle

In HG2, MathWorks have dramatically improved the appearance of the grid-lines, making them a much lighter, less-obtrusive gray color. Even more importantly, we now have fine-grained control over the grid lines, via the axes’ hidden XGridHandle, YGridHandle and ZGridHandle properties. These return an empty handle when the grid is not shown, and a matlab.graphics.axis.decorator.SimpleGrid class object when the relevant grid-lines are displayed.

>> grid on
>> xgrid = get(gca,'XGridHandle');  % or: hAxes.XGridHandle
>> get(xgrid)
           BackMajorEdge: [1x1 LineStrip]
           BackMinorEdge: [1x1 LineStrip]
                Children: []
                   Color: [0.15 0.15 0.15]
      FirstCrossoverAxis: 0
     FirstCrossoverValue: 0
          FrontMajorEdge: [1x1 LineStrip]
          FrontMinorEdge: [1x1 LineStrip]
           GridLineStyle: '-'
        HandleVisibility: 'off'
               LineWidth: 0.5
              MajorMinor: 'major'
               MajorTick: [-10 -8 -6 -4 -2 0 2 4 6 8 10]
              MinorColor: [0.1 0.1 0.1]
      MinorGridLineStyle: ':'
          MinorLineWidth: 0.5
               MinorTick: []
                  Parent: [1x1 DecorationContainer]
    SecondCrossoverValue: 0
                 Visible: 'on'

As before, we can customize the grid line’s LineWidth, GridLineStyle, and Color. MajorMinor can accept ‘major’ (default), ‘minor’, or ‘majorandminor’, which is pretty intuitive.

customized HG2 axes X-grid

customized HG2 axes X-grid

set(xgrid,'Color','r', 'GridLineStyle',':', 'LineWidth',1.5, 'MajorMinor','majorandminor');

To control the tick lines in 3D plot we can set BackMajorEdge, BackMinorEdge, FrontMajorEdge and FrontMinorEdge in the same way as we did for the BoxFrame. By default only the back-edges are visible. If you wish to also display the front edges, then set the front edge’s Visible to ‘on’ and don’t forget to modify its VertexData to different values than the back edge’s VertexData. As before, colorful effects can be achieved in the grid-lines by customizing the edge objects’ properties:

>> xgrid.BackMajorEdge.get
          AlignVertexCenters: 'on'
             AmbientStrength: 0.3
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'off'
                     HitTest: 'off'
                       Layer: 'back'
                   LineStyle: 'dotted'
                   LineWidth: 1.5
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 SimpleGrid]
               PickableParts: 'visible'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: [1 3 5 7 9 11]
                     Texture: []
                  VertexData: [3x10 single]
               VertexIndices: []
                     Visible: 'on'
       WideLineRenderingHint: 'software'

A note about performance

MATLAB HG2, on R2014b, relies on advanced OpenGL hardware features much more than earlier releases. When Matlab detects an incompatible display driver, it issues a warning and reverts to using the much slower software implementation. It is advisable to update the display driver to the very latest version, since this often solves such incompatibilities.

Some computer vendors use custom OEM drivers and their latest version might still be incompatible. For example, on my Lenovo E530 laptop, the latest Intel HG Graphics 4000 driver (v.9.15) was still incompatible; although the system told me that I have the very latest driver installed. I downloaded the latest generic driver (v.10.18) from Intel’s website and this fixed the issue: HG2 now uses hardware-accelerated OpenGL on my laptop, which is much faster. It may not be as 100% compatible with my laptop as the custom OEM version, but so far it appears to work ok and importantly Matlab graphics now work much better. I can always roll-back the driver to the OEM driver if I see a problem with the generic driver.

Next week I will continue my series on Matlab axes customizations, by describing Backdrop, WarpToFill and a few other aspects. Stay tuned!

]]>
https://undocumentedmatlab.com/blog_old/customizing-axes-part-2/feed 21
Customizing axes rulershttps://undocumentedmatlab.com/blog_old/customizing-axes-rulers https://undocumentedmatlab.com/blog_old/customizing-axes-rulers#comments Wed, 08 Oct 2014 14:40:43 +0000 https://undocumentedmatlab.com/?p=5116 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 5 – origin crossover and labels The axes rulers (axles) can be made to cross-over at any x,y location within the chart. ...
  3. Undocumented scatter plot jitter Matlab's scatter plot can automatically jitter data to enable better visualization of distribution density. ...
  4. HG2 update HG2 appears to be nearing release. It is now a stable mature system. ...
]]>
Over 4 years have passed since I’ve posted my scoop on Matlab’s upcoming new graphics system (a.k.a. HG2, Handle Graphics version 2). At that time HG2 was still far from usable, but that has changed when I posted my HG2 update last year. Numerous user feedbacks, by email and blog comments, were reported and the MathWorks developers have listened and improved the code. HG2 was finally released with Matlab R2014b last Friday, and it seems at first glance to be a beauty. I hope my posts and the feedbacks have contributed, but in any case the MathWorks dev group deserves big kudos for releasing a totally new system that provides important usability and aesthetic improvements [almost] without sacrificing performance or backward-compatibility. Trust me, it’s not an easy achievement.

Customized HG2 plot

Customized HG2 plot


One of the nice things that I like about HG2 is that it provides numerous new ways to customize the objects in ways that were impossible (or nearly so) in the old HG1. In R2014b, the leap was large enough that MathWorks wisely chose to limit the official new properties to a bare minimum, for maximal HG1 compatibility. I assume that this will remain also in R2015a, which I expect to be a release devoted to bug fixing and stabilization rather than to new features. I expect the new features to start becoming official in R2015b onward. Such a measured roadmap is to be expected from a responsible engineering company such as MathWorks. So in fact there is no need at all to be disappointed at the relative lack of new functional features. They are all there already, just not yet official, and this is just as it should be.

That being said, if we are aware of the risk that these features might change in the upcoming years until they become official (if ever), then we can start using them today. Experience with this blog has shown that the vast majority of such undocumented features remain working unchanged for years, and some of them eventually become documented. For example, uitab/uitabgroup, on which I posted over 4 years ago, and which has existed almost unchanged since 2005, finally became official in R2014b after many years of running in unofficial form.

In the next few weeks I intend to present a series of posts that highlight some of the undocumented customizations in HG2. I’ll start with some new axes features, followed by plotting aspects.

By the way, “HG2” is an internal name, that you will find scattered throughout the code but never in the official documentation. Apparently for marketing reasons, MathWorks chose to call it the “New Graphics Engine/System”. I’ll keep using the term HG2, as I think it sounds better than “NGE/NGS”. And besides, old habits die hard…

Axes Rulers

“Rulers” are the X, Y and Z axes lines and all their related aspects, including ticks, tick labels, and secondary label (see below). Some of the important aspects (e.g., ticks and tick labels) have mirror properties in the axes object, for convenience. The ruler objects can be accessed via the hidden properties (XRuler, YRuler and ZRuler, which can be seen using the getundoc utility):

>> x = -10:0.1:10;
>> y = 1e7*sin(x)./x; 
>> hLine = plot(x,y);
>> hAxes = gca;
 
>> yruler = hAxes.YRuler  % or: get(hAxes,'YRuler')
yruler = 
    NumericRuler
 
>> class(yruler)
ans =
matlab.graphics.axis.decorator.NumericRuler
 
>> get(yruler)
                               Axle: [1x1 LineStrip]
                           Children: []
                              Color: [0.15 0.15 0.15]
                           Exponent: 6
                 FirstCrossoverAxis: 0
                FirstCrossoverValue: -Inf
                          FontAngle: 'normal'
                           FontName: 'Helvetica'
                           FontSize: 10
                      FontSmoothing: 'on'
                         FontWeight: 'normal'
                   HandleVisibility: 'off'
                              Label: [1x1 Text]
                          LineWidth: 0.5
                         MajorTicks: [1x1 LineStrip]
                          MinorTick: [1x7 double]
                         MinorTicks: []
                             Parent: [1x1 DecorationContainer]
    PlaceSecondaryLabelAtLowerLimit: 'off'
                              Scale: 'linear'
               SecondCrossoverValue: -Inf
                     SecondaryLabel: [1x1 Text]
                               Tick: [1x8 double]
                            TickDir: 'in'
                          TickLabel: {8x1 cell}
               TickLabelInterpreter: 'tex'
                  TickLabelRotation: 0
                         TickLabels: [1x1 Text]
                         TickLength: 3.255
                            Visible: 'on'

The ruler object itself has plenty of very useful hidden properties (getundoc again…). In R2014b the number of hidden properties for *ALL* objects has multiplied – many standard properties now have hidden internal mirror properties having the “_I” suffix. For example, Tick is a visible property, and Tick_I is hidden. But there are of course other properties that are hidden and are not such internal mirrors. Today I’ll focus on the ruler’s visible properties only.

Axle property

The Axle property controls the axis line, which is a solid black line by default:

>> get(yruler.Axle)
          AlignVertexCenters: 'on'
             AmbientStrength: 0.3
                ColorBinding: 'object'
                   ColorData: [4x1 uint8]
                   ColorType: 'truecolor'
             DiffuseStrength: 0.6
            HandleVisibility: 'off'
                     HitTest: 'off'
                       Layer: 'back'
                   LineStyle: 'solid'
                   LineWidth: 0.5
               NormalBinding: 'none'
                  NormalData: []
                      Parent: [1x1 NumericRuler]
               PickableParts: 'none'
    SpecularColorReflectance: 1
            SpecularExponent: 10
            SpecularStrength: 0.9
                   StripData: [1 3 5]
                     Texture: []
                  VertexData: [3x4 single]
               VertexIndices: []
                     Visible: 'on'
       WideLineRenderingHint: 'software'

As you can see, there are many ways by which we can customize the Axle. For example, let’s use a dotted green line instead of the default solid black line:

hAxes.YRuler.Axle.LineStyle = 'dotted';  % default: 'solid', also possible: dashdot, dashed, none
hAxes.YRuler.Axle.ColorData = uint8([0,100,0,255])';  %=dark green; default: [51 51 51 255], corresponding to [0.2 0.2 0.2 1]

The result can be seen in the screenshot above.

ColorData is closely related to ColorType, which is ‘truecolor’ by default. There are also the alternative ‘colormapped’, ‘texturemapped’, and ‘truecoloralpha’. We can see the various possible combinations if we try to issue an illegal ColorData value:

>> yruler.Axle.ColorData = [.1,.2,.3];
While setting the 'ColorData' property of LineStrip:
Value must be one of the following:
Truecolor  - a 4xN array of type uint8
Colormapped - a 1xN vector of type single
Texturemapped - a 2xN array of type single

For Truecoloralpha, the fourth color value (255 in the example above) signifies the relative transparency: 0=transparent, 255=opaque, and values between 0-255 indicate the degree of translucency.

Note that the default Axle.ColorData sometimes changes between [51 51 51 255] and [38 38 38 255], I’m not sure exactly when this happens. In some cases the YRuler may use ColorData=51 while XRuler uses 38 (which is slightly darker).

We can make interesting effects by specifying non-default VertexData endpoints (array of 3xN singles that should correspond to a 1xN uint32 StripData). For example, an axle that spans only part of the axis line… – have fun :-)

Exponent and SecondaryLabel properties

The Exponent controls the computation of the tick label format. For example, in the example above Exponent=6 which means that SecondaryLabel.String=’\times10^{6}’. The exponent is automatically calculated by default, but we can override it to whatever integer we want (non-integer exponent values are rounded to the nearest integer):

hAxes.YRuler.Exponent = -3;

This way we can force the exponent to always have a certain value, or to always display engineering values (multiples of 3). We can do this by attaching a listener to the corresponding axes’ limits, e.g. YLim for the Y-axes.

In addition to forcing the Exponent value, we can also specify a non-numeric SecondaryLabel. For example:

hAxes.YRuler.SecondaryLabel.String = 'millions';  % original: 'x10^{6}'
hAxes.YRuler.SecondaryLabel.FontAngle = 'italic';  % default: 'normal'
hAxes.YRuler.SecondaryLabel.Visible = 'on';  % default: 'off'
 
% Alternative:
set(hAxes.YRuler.SecondaryLabel, 'String','millions', 'FontAngle','italic', 'Visible','on');

The result can be seen in the screenshot above.

Call for action

Go ahead – play around with the new HG2. It’s a lot of fun and quite painless with the new object.property notation. If you have any feedback on the features above or any other, please do share them in a comment below. I’m quite sure that MathWorks will read these comments and this may be a way for you to influence the features that may someday become officially supported. More to the point, make your graphics become even more stunning then they have already become with out-of-the-box HG2!

The HG2 customization series will continue next week with a discussion of additional new axes properties, including Baseline, BoxFrame and GridHandle.

]]>
https://undocumentedmatlab.com/blog_old/customizing-axes-rulers/feed 20
Draggable plot data-tipshttps://undocumentedmatlab.com/blog_old/draggable-plot-data-tips https://undocumentedmatlab.com/blog_old/draggable-plot-data-tips#comments Wed, 23 Oct 2013 22:01:32 +0000 https://undocumentedmatlab.com/?p=4294 Related posts:
  1. Matlab’s HG2 mechanism HG2 is presumably the next generation of Matlab graphics. This article tries to explore its features....
  2. New information on HG2 More information on Matlab's new HG2 object-oriented handle-graphics system...
  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. ...
]]>
A couple of years ago, I was asked by a client to figure out a way to make Matlab plot data-tips movable. The problem was that he had a very crowded plot and Matlab’s standard data-tips are displayed immediately adjacent to the data, thereby overlapping the plot parts. Matlab’s standard data-tips enable dragging to a very limited extent (only to the 4 corners of the data-point), and in any case the displayed textbox remains directly attached to the data point.

So I developed a small utility for my client that solves this problem. I then forgot all about it until a few days ago when I came across it again. Yesterday I uploaded this draggableDataTips utility to the File Exchange, where you can download it.

draggableDataTips enables the user to interactively drag any newly-created data-tip, anywhere in the Matlab figure. A dashed line connects the dragged data-tip with the original data point. Simple, intuitive, and effective. At 300 lines, the draggableDataTips.m source code is compact and easy to follow, and you’re welcome to take a look and further customize this utility for your specific needs.

draggableDataTips utility in action

draggableDataTips utility in action


Technical implementation

The implementation of draggableDataTips relies on undocumented aspects of the data-cursor object exposed via the datacursormode function. I have already shown how these can be used to customize and control data-tips programmatically (rather than interactively). For draggableDataTips, we first get the cursor object’s meta-data classhandle (a schema.class object), then use it to find the meta-data for its hidden CurrentDataCursor property (a schema.prop object). There is also a more direct approach, using the findprop function.

Whichever alternative we choose, we finally use this object to set the property’s SetFunction meta-property. Quite straight-forward, I should say:

% Get the cursor-mode object
cursorObj = datacursormode(hFig);
 
% Alternative #1: the indirect route to the meta-property
ch = classhandle(cursorObj);
hPropIdx = strcmpi(get(ch.Properties,'Name'), 'CurrentDataCursor');
hProp = ch.Properties(hPropIdx);
 
% Alternative #2: the direct approach
hProp = findprop(cursorObj, 'CurrentDataCursor');
 
% Update the meta-property to use a custom function whenever new data-tips are created
hProp.SetFunction = @installNewMoveFcn;

This has the effect that all new data-tips that will be created from then on will call our custom installNewMoveFcn function when the data-tip object is created. This installNewMoveFcn function, in turn, simply replaces the data-tips standard default callback for mouse movement (which is used while dragging), to a new custom function textBoxUpdateFcn:

% Install a replacement callback function for datatip textbox mouse movement
function hDataTip = installNewMoveFcn(cursorObj, hDataTip)
    srcObjs = get(hDataTip.SelfListenerHandles,'SourceObject');
    for srcIdx = 1 : length(srcObjs)
        if strcmpi(srcObjs{srcIdx}.Name,'Orientation')
            hDataTip.SelfListenerHandles(srcIdx).Callback = @textBoxUpdateFcn;
            hDataTip.MarkerHandle.Marker = 'none';
            break;
        end
    end
end

The textBoxUpdateFcn function basically moves the datatip textbox to the new mouse location, without limiting its positions as Matlab’s standard default callback function did. A dashed connector line is then drawn to connect the center of the textbox with the data-point. It’s a bit long to include here, but interested readers are welcome to look at the code (lines #99 onward in draggableDataTips.m).

As can be seen from the screenshot above, standard and draggable data-tips can be used in the same plot. This is done by simply turning the draggability functionality on and off by draggableDataTips before creating a new data-tip (using either alt-click or programmatically):

draggableDataTips on    % or: draggableDataTips('on')  or: draggableDataTips(true)
draggableDataTips off   % or: draggableDataTips('off') or: draggableDataTips(false)

Notes:

  1. The actual code of draggableDataTips naturally contains more sanity checks, exception handling etc. I have only presented the core code in the snippets above, but you should always include such extra checks in any real-life program.
  2. There is a minor bug that causes the connector line to be on top of the box rather than beneath it, but aside from this all works ok. For some reason, uistack did not work. If anyone has a fix for this bug, please let me know.
  3. Did you notice that Java was not mentioned anywhere above? Mode managers, such as the data-cursor mode, use pure-Matlab functionality.

HG2

Unfortunately, when I tested draggableDataTips on HG2, Matlab’s upcoming new graphics engine, the utility failed. Data-cursors have undergone a significant reengineering in HG2 (about time!), causing multiple properties and methods to change names and functionality. But this could be overcome (see lines #43-53 in draggableDataTips.m).

However, I could not overcome the apparent fact that unlike HG1 (or more specifically, schema objects, that HG1 uses for its data-tips functionality), in HG2 (or rather, MCOS class objects) we cannot override an object’s meta-properties, specifically its SetMethod (which is the MCOS equivalent of schema.prop‘s SetFunction meta-property).

So for the moment, draggableDataTips does not work on HG2. I hope to find a solution for this by the time HG2 launches. If anyone finds a fix for the problem, please let me know. If I ever find or receive a fix, I will update this post with the relevant technical details.

For the moment there is no rush, since HG2 is still relatively far away in the distant future, and draggableDataTips works splendidly in the current HG1.

Have you used plot data-tips in some nifty way? If so, please share your experience in a comment below.

]]>
https://undocumentedmatlab.com/blog_old/draggable-plot-data-tips/feed 6
Handle Graphics Behaviorhttps://undocumentedmatlab.com/blog_old/handle-graphics-behavior https://undocumentedmatlab.com/blog_old/handle-graphics-behavior#comments Wed, 06 Mar 2013 18:00:19 +0000 https://undocumentedmatlab.com/?p=3665 Related posts:
  1. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  2. Uitable sorting Matlab's uitables can be sortable using simple undocumented features...
  3. 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. ...
  4. Customizing print setup Matlab figures print-setup can be customized to automatically prepare the figure for printing in a specific configuration...
]]>
Matlab’s Handle Graphics (HG) have been around for ages. Still, to this day it contains many hidden gems. Today I discuss HG’s Behavior property, which is a standard undocumented hidden property of all HG objects.

Behavior is not normally updated directly (although there is absolutely nothing to prevent this), but rather via the semi-documented built-in accessor functions hggetbehavior and hgaddbehavior. This manner of accessing Behavior is similar to its undocumented sibling property ApplicationData, which is accessed by the corresponding getappdata and setappdata functions.

Using HB behaviors

hggetbehavior with no input args displays a list of all pre-installed HG behaviors:

>> hggetbehavior
 
   Behavior Object Name         Target Handle
   --------------------------------------------
   'Plotedit'...................Any Graphics Object   
   'Print'......................Any Graphics Object   
   'Zoom'.......................Axes                  
   'Pan'........................Axes                  
   'Rotate3d'...................Axes                  
   'DataCursor'.................Axes and Axes Children
   'MCodeGeneration'............Axes and Axes Children
   'DataDescriptor'.............Axes and Axes Children
   'PlotTools'..................Any graphics object   
   'Linked'.....................Any graphics object   
   'Brush'......................Any graphics object

hggetbehavior can be passed a specific behavior name (or cell array of names), in which case it returns the relevant behavior object handle(s):

>> hBehavior = hggetbehavior(gca, 'Zoom')
hBehavior =
	graphics.zoombehavior
 
>> hBehavior = hggetbehavior(gca, {'Zoom', 'Pan'})
hBehavior =
	handle: 1-by-2

As the name indicates, the behavior object handle controls the behavior of the relevant action. For example, the behavior object for Zoom contains the following properties:

>> hBehavior = hggetbehavior(gca, 'Zoom');
>> get(hBehavior)
       Enable: 1        % settable: true/false
    Serialize: 1        % settable: true/false
         Name: 'Zoom'   % read-only
        Style: 'both'   % settable: 'horizontal', 'vertical' or 'both'

By setting the behavior’s properties, we can control whether the axes will have horizontal, vertical, 2D or no zooming enabled, regardless of whether or not the toolbar/menu-bar zoom item is selected:

hBehavior.Enable = false;         % or: set(hBehavior,'Enable',false)
hBehavior.Style  = 'horizontal';  % or: set(hBehavior,'Style','horizontal')

This mechanism is used internally by Matlab to disable zoom/pan/rotate3d (see %matlabroot%/toolbox/matlab/graphics/@graphics/@zoom/setAllowAxesZoom.m and similarly setAllowAxesPan, setAllowAxesRotate).

At this point, some readers may jump saying that we can already do this via the zoom object handle that is returned by the zoom function (where the Style property was renamed Motion, but never mind). However, I am just trying to show the general usage. Not all behaviors have similar documented customizable mechanisms. In fact, using behaviors we can control specific behaviors for separate HG handles in the same figure/axes.

For example, we can set a different callback function to different HG handles for displaying a plot data-tip (a.k.a. data cursor). I have explained in the past how to programmatically control data-tips, but doing so relies on the figure datacursormode, which is figure-wide. If we want to display different data-tips for different plot handles, we would need to add logic into our custom update function that would change the returned string based on the clicked handle. Using HG behavior we can achieve the same goal much easier:

% Use dataCursorLineFcn() for the line data-tip
bh = hggetbehavior(hLine,'DataCursor');
set(bh,'UpdateFcn',@dataCursorLineFcn);
 
% Use dataCursorAnnotationFcn() for the annotation data-tip
bh = hggetbehavior(hAnnotation,'DataCursor');
set(bh,'UpdateFcn',{@dataCursorAnnotationFcn,extraData});

Note: there is also the related semi-documented function hgbehaviorfactory, which is used internally by hggetbehavior and hgaddbehavior. I do not see any need for using hgbehaviorfactory directly, only hggetbehavior and hgaddbehavior.

Custom behaviors

The standard behavior objects are UDD schema objects (i.e., the old object-oriented mechanism in MATLAB). They are generally located in a separate folder beneath %matlabroot%/toolbox/matlab/graphics/@graphics/. For example, the Zoom behavior object is located in %matlabroot%/toolbox/matlab/graphics/@graphics/@zoombehavior/. These behavior object folders generally contain a schema.m file (that defines the behavior object class and its properties), and a dosupport.m function that returns a logical flag indicating whether or not the behavior is supported for the specified handle. These are pretty standard functions, here is an example:

% Zoom behavior's schema.m:
function schema
% Copyright 2003-2006 The MathWorks, Inc.
 
pk = findpackage('graphics');
cls = schema.class(pk,'zoombehavior');
 
p = schema.prop(cls,'Enable','bool');
p.FactoryValue = true;
 
p = schema.prop(cls,'Serialize','MATLAB array');
p.FactoryValue = true;
p.AccessFlags.Serialize = 'off';
 
p = schema.prop(cls,'Name','string');
p.AccessFlags.PublicSet = 'off';
p.AccessFlags.PublicGet = 'on';
p.FactoryValue = 'Zoom';
p.AccessFlags.Serialize = 'off';
 
% Enumeration Style Type
if (isempty(findtype('StyleChoice')))
    schema.EnumType('StyleChoice',{'horizontal','vertical','both'});
end
p = schema.prop(cls,'Style','StyleChoice');
p.FactoryValue = 'both';
% Zoom behavior's dosupport.m:
function [ret] = dosupport(~,hTarget)
% Copyright 2003-2009 The MathWorks, Inc.
 
% axes 
ret = ishghandle(hTarget,'axes');

All behaviors must define the Name property, and most behaviors also define the Serialize and Enable properties. In addition, different behaviors define other properties. For example, the DataCursor behavior defines the CreateNewDatatip flag and no less than 7 callbacks:

function schema
% Copyright 2003-2008 The MathWorks, Inc.
 
pk = findpackage('graphics');
cls = schema.class(pk,'datacursorbehavior');
 
p = schema.prop(cls,'Name','string');
p.AccessFlags.PublicSet = 'off';
p.AccessFlags.PublicGet = 'on';
p.FactoryValue = 'DataCursor';
 
schema.prop(cls,'StartDragFcn','MATLAB callback');
schema.prop(cls,'EndDragFcn','MATLAB callback');
schema.prop(cls,'UpdateFcn','MATLAB callback');
schema.prop(cls,'CreateFcn','MATLAB callback');
schema.prop(cls,'StartCreateFcn','MATLAB callback');
schema.prop(cls,'UpdateDataCursorFcn','MATLAB callback');
schema.prop(cls,'MoveDataCursorFcn','MATLAB callback');
 
p = schema.prop(cls,'CreateNewDatatip','bool');
p.FactoryValue = false;
p.Description = 'True will create a new datatip for every mouse click';
p = schema.prop(cls,'Enable','bool');
p.FactoryValue = true;
 
p = schema.prop(cls,'Serialize','MATLAB array');
p.FactoryValue = true;
p.AccessFlags.Serialize = 'off';

Why am I telling you all this? Because in addition to the standard behavior objects we can also specify custom behaviors to HG handles. All we need to do is mimic one of the standard behavior object classes in a user-defined class, and then use hgaddbehavior to add the behavior to an HG handle. Behaviors are differentiated by their Name property, so we can either use a new name for the new behavior, or override a standard behavior by reusing its name.

hgaddbehavior(hLine,myNewBehaviorObject)

If you wish the behavior to be serialized (saved) to disk when saving the figure, you should add the Serialize property to the class and set it to true, then use hgaddbehavior to add the behavior to the relevant HG handle. The Serialize property is searched-for by the hgsaveStructDbl function when saving figures (I described hgsaveStructDbl here). All the standard behaviors except DataDescriptor have the Serialize property (I don’t know why DataDescriptor doesn’t).

Just for the record, you can also use MCOS (not just UDD) class objects for the custom behavior, as mentioned by the internal comment within the hgbehaviorfactory function. Most standard behaviors use UDD schema classes; an example of an MCOS behavior is PlotEdit that is found at %matlabroot%/toolbox/matlab/graphics/+graphics/+internal/@PlotEditBehavor/PlotEditBehavor.m.

ishghandle‘s undocumented type input arg

Note that the Zoom behavior’s dosupport function uses an undocumented format of the built-in ishghandle function, namely accepting a second parameter that specifies a specific handle type, which presumably needs to correspond to the handle’s Type property:

ret = ishghandle(hTarget,'axes');

The hasbehavior function

Another semi-documented built-in function called hasbehavior is located right next to hggetbehavior and hgaddbehavior in the %matlabroot%/toolbox/matlab/graphics/ folder.

Despite its name, and the internal comments that specifically mention HG behaviors, this function is entirely independent of the HG behavior mechanism described above, and in fact makes use of the ApplicationData property rather than Behavior. I have no idea why this is so. It may be a design oversight or some half-baked attempt by a Mathworker apprentice to emulate the behavior mechanism. Even the function name is misleading: in fact, hasbehavior not only checks whether a handle has some “behavior” (in the 2-input args format) but also sets this flag (in the 3-input args format).

hasbehavior is used internally by the legend mechanism, to determine whether or not an HG object (line, scatter group, patch, annotation etc.) should be added to the legend. This can be very important for plot performance, since the legend would not need to be updated whenever these objects are modified in some manner:

hLines = plot(rand(3,3));
hasbehavior(hLines(1), 'legend', false);   % line will not be in legend
hasbehavior(hLines(2), 'legend', true);    % line will be in legend

(for anyone interested, the relevant code that checks this flag is located in %matlabroot%/toolbox/matlab/scribe/private/islegendable.m)

hasbehavior works by using a dedicated field in the handle’s ApplicationData struct with a logical flag value (true/false). The relevant field is called [name,'_hgbehavior'], where name is the name of the so-called “behavior”. In the example above, it creates a field called “legend_hgbehavior”.

Do you know of any neat uses for HG behaviors? If so, please post a comment below.

]]>
https://undocumentedmatlab.com/blog_old/handle-graphics-behavior/feed 5