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](https://undocumentedmatlab.com/images/hg2-contourf-animated.gif)
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](https://undocumentedmatlab.com/images/hg2-brushing.gif)
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.
@Yair,
Using struct may not be the optimal solution; I, personally, prefer your uiinspect utility due to its graphical and interactive interface. One must not forget your instrumental FindJObj and checkClass utilities for inspecting java objects.
On another matter: the data cursors in HG2 have TeX interpretation capabilities. It had been a default option in the non-official HG2 releases (Matlab R2013b with “
-hgVersion 2
” startup); however, it has been removed in the official Matlab R2014b release. Using theuiinspect
function, I found theInterpreter
residing in theTipHandle
property ofDataCursor
. To re-enable it use simply:If one wishes to use just the interactive data cursors, they can simply change the
UpdateFcn
of the data cursor mode:It is such a simple and useful feature; I wish that TMW made it documented and accessible…
These days, I typically use the
metaclass
function for this kind of thing.That list of properties in PropertyList includes 178 of the 181 properties that you found with
struct
(the missing ones apparently being “SerializableChildren”, “SerializableUIContextMenu”, and “TopLevelSerializedObject”). Usingmetaclass
won’t give you GetAccess to properties which deny it likestruct
does, but at least for base MATLAB graphics classes, the list of properties with private or protected GetAccess is usually very small and properties that you probably don’t need anyway (that contour object has 6 properties with protected GetAccess: “SerializableApplicationData”, “SerializableBehavior”, “CreateFcn_IS”, “DeleteFcn_IS”, “ButtonDownFcn_IS”, and “SerializableUserData”).The benefits of using the
metaclass
function are that it gives you access to a lot more information: property attributes (Hidden, GetAccess, etc), methods (including hidden ones), and even pointers to the meta.class objects of superclasses inherited by the object in question.@Greg – thanks for mentioning metaclass, which indeed complements struct. In my eyes, the main benefit of struct is not in its access to private/protected properties (as you’ve noted, these are probably of little or no interest), but rather in providing the current values of each of the properties. It is very convenient to scan a list of property/values to find something that looks interesting – much easier than to scan through the internal lists exposed by metaclass. The property value help me understand the nature of the property, which is not always clear from its name. Had MathWorks programmers taken the time to document properties and classes using their internal Description metaproperty, this could be different. But unfortunately, the vast majority of descriptions are either empty or contain the boilerplate ‘TODO: Fill in Description’ string.
I often need to get the x position of each bar in a grouped bar plot so I can put errorbars on them. With HG1, I would get the x position of the ith bar with:
With HG2, I have to use the undocumented XOffset property which I found with your struct method.
Interestingly, and I don’t know if it is a general HG2 bug, but the undocumented XOffset property does not initially get set correctly if the NextPlot property of the axes object is set to Add:
Here’s another usage example of using the bars’ undocumented XOffset property to get the bar centers for the benefit of the errorbar function: https://www.mathworks.com/matlabcentral/answers/451794-adding-standard-error-bars-to-grouped-bar-graph
Hello,
I am trying to follow your example of grabbing the brushed data. Before I continue, I’m not really good with Matlab so I apologize if I am a bit slow to follow. Anyways, when I make a simple plot and try
handle = hPlot.BrushHandles;
I get an error saying ‘No appropriate method.’ After some quick google searching, I see that the error message may be related to accessing a nonpublic property and I tried
Then I get ‘There is no BrushHandles property on the Figure class.’
So I checked the properties using struct(hPlot) and I’ve combed through and can’t seem to find anything related to brush data. Is there something obvious that I am missing? I am using version 2015a.
@Yow-Ren – your hPlot variable is not a plot handle (returned from the plot function) but a figure handle, so of course it doesn’t work…
Yair, do you know if any of these TriangleStrips properties controls transparency (similar to the FaceAlpha property)?
Thanks,
Nick Gianopoulos
@Nick – I do not understand your question: at the very top of this post I showed how to use the ColorType and ColorData properties of the
TriangleStrip
objects in order to achieve transparency.@Yair – So you did, I just failed to read the first part of this post for some reason. Lol, I’m honestly in shock over how quick your reply was – thanks so much! It’s been one of those days … Huge fan of your work btw
Hi,
extracting the x-coordinates (on R2016a, Linux) using
works fine with data plotted using plot(), but if I use scatter() instead, the extracted x-coordinates do not match the brushed ones. This seems somehow unexpected to me…
Best,
Christoph
addition: the problem only appears when using log-scaling on the y-axis and the y-data contains zeros. Minimal working example:
now brush some data-point and try
The zeros still appear in h.YData and h.BrushData is of same size, however, its indexing seems different (considering visible symbols only).
@Christoph – I believe that this is due to an internal Matlab bug. You can try to report this to MathWorks but don’t be too hopeful, since this functionality is undocumented/unsupported after all…
@Yair – I found that the bug also affects the “official way”: brushing data and selecting Tools->Brushing->Create new variable… also gives the wrong result.
One can also observe it visually in my example above, when switching between linear and logarithmic scaling: the brushed points change!
@Christoph – excellent: this means that you can report this to MathWorks as an official bug. If and when you ever learn anything new about this, then please update with a new comment here.
update: MathWorks confirmed that the observed behaviour is indeed a bug affecting MATLAB R2014b through MATLAB R2016a (Technical Support Case #01981712). Their developers are working on fixing this issue.
update: the issue is fixed in R2016b. The position of flags in h.BrushData now corresponds to data-points in h.XData and h.YData (irrespective of data-point visibility due to log-scaling)
Thanks for the update, Christoph
Hi,I programed callbacks for brush mode operations below,which I wanted to access the point I brushed. However,it don’t work. Can you tell me where I am wrong? Thanks!
Hi Yair,
There is “VertexData” in property of “TriangleStrip .FacePrims”, but there is no information about patch “faces” that can connect vertices to each other.
Is there any way to use from VertexData and extract border or relationship between triangles of one region?
Yair, I found the solution by using “hFills(i).StripData”. Thanks