Accessing hidden HG2 plot functionality

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

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

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.

 Print

17 Responses to Accessing hidden HG2 plot functionality

1. Yaroslav says:

@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 the uiinspect function, I found the Interpreter residing in the TipHandle property of DataCursor. To re-enable it use simply:

hDataCursorMode = datacursormode(); hDataCursor = hDataCursorMode.createDatatip(someTargetHandle); hDataCursor.TipHandle.Interpreter = 'tex'; hDataCursorMode.Enable = 'off'; % turn off

If one wishes to use just the interactive data cursors, they can simply change the UpdateFcn of the data cursor mode:

hDataCursorMode.UpdateFcn = @myUpdateFcn; ... function txt = myUpdateFcn(obj,evd) obj.TipHandle.Interpreter = 'tex'; txt = sprintf('\\tau = %g\n\\mu_i = %g',evd.Position(1),evd.Position(2)); end

It is such a simple and useful feature; I wish that TMW made it documented and accessible…

2. Greg says:

These days, I typically use the metaclass function for this kind of thing.

>> metaclass(hContour) ans = GraphicsMetaClass with properties:   Name: 'matlab.graphics.chart.primitive.Contour' Description: '' DetailedDescription: '' Hidden: 0 Sealed: 1 Abstract: 0 Enumeration: 0 ConstructOnLoad: 1 HandleCompatible: 1 InferiorClasses: {0x1 cell} ContainingPackage: [1x1 meta.package] PropertyList: [178x1 meta.property] MethodList: [129x1 meta.method] EventList: [13x1 meta.event] EnumerationMemberList: [0x1 meta.EnumeratedValue] SuperclassList: [6x1 matlab.graphics.internal.GraphicsMetaClass]

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”). Using metaclass won’t give you GetAccess to properties which deny it like struct 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.

3. Dan says:

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:

hBar = bar(randn(5)); x = get(get(hBar(i), 'Children'), 'XData'); x = x(2, :) + diff(x(2:3, :))/2;

With HG2, I have to use the undocumented XOffset property which I found with your struct method.

hBar = bar(randn(5)); x = get(hBar(i), 'XData') + get(hBar(i), 'XOffset');

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:

figure; set(gca, 'NextPlot', 'Add') hBar = bar(randn(5)); get(hBar(1), 'XOffset') drawnow; get(hBar(1), 'XOffset')
4. Yow-Ren Chang says:

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

handle = hPlot.get('BrushHandles');

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.

5. Nick Gianopouulos says:

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.

• Nick Gianopouulos says:

@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

6. Christoph Feenders says:

Hi,

extracting the x-coordinates (on R2016a, Linux) using

hLine.XData(logical(hLine.BrushData))

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

• Christoph Feenders says:

addition: the problem only appears when using log-scaling on the y-axis and the y-data contains zeros. Minimal working example:

h = scatter(1:5, [0 1 0 2 0]); set(gca(), 'YScale', 'log')

now brush some data-point and try

h.XData(logical(h.BrushData))

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…

• Christoph Feenders says:

@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.

• Christoph Feenders says:

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.

• Christoph Feenders says:

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