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=viewT = 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):
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…