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:
In order to update the label colors (to get the screenshot on the right), we create a short
updateContoursfunction 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
[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');
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.