Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Undocumented view transformation matrix

April 15, 2015 5 Comments

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

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

>> 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;

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))

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);

% "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…

Related posts:

  1. Matrix processing performance – Matrix operations performance is affected by internal subscriptions in a counter-intuitive way....
  2. ishghandle's undocumented input parameter – The built-in function ishghandle accepts a second input argument with the expected handle type....
  3. getundoc – get undocumented object properties – getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
  4. Undocumented plot marker types – Undocumented plot marker styles can easily be accesses using a hidden plot-line property. ...
  5. HG's undocumented parameters interface – Some HG functions also accept inputs parameters in a struct fields rather than the normal P-V pairs format. ...
  6. Undocumented scatter plot behavior – The scatter plot function has an undocumented behavior when plotting more than 100 points: it returns a single unified patch object handle, rather than a patch handle for each specific point as it returns with 100 or less points....
Handle graphics HG2 Hidden property Pure Matlab Undocumented feature
Print Print
« Previous
Next »
5 Responses
  1. Eric April 27, 2015 at 08:49 Reply

    Hi Yair, I haven’t had enough coffee yet on this Monday morning to follow this post entirely, but is makehgtform/hgtransform helpful?

    • Yair Altman April 27, 2015 at 09:29 Reply

      @Eric – these functions are related to the article in the sense that they also deal with a 4×4 affine transformation matrix. They can be used in cases when you know in advance the requested rotation/scaling/translation amounts. My article dealt with the undocumented feature of extracting the current matrix from the axes, and af far as I can tell this is not supported by makehgtform and hgtransform.

  2. Michelle Hirsch April 27, 2015 at 10:14 Reply

    Yair –

    Thanks for taking the time to point out these inconsistencies in the behavior and documentation. We will clean up the documentation to make things more consistent. We agree that the current state of the view transformation is a bit of a mess. We are keeping it around for compatibility, and are considering your suggestion that we should provide a documented, reliable, and consistent way to work with view transformation matrices.

  3. Yaroslav May 12, 2015 at 04:34 Reply

    There is another semi-documented feature of the view: you can link the View property across multiple axes. For example,

    figure('Units','normalized','OuterPosition',[.10 .10 .80 .80])
    %
    hAxis(1) = subplot(1,2,1); 
    surf(peaks(31));
    axis tight;
    %
    hAxis(2) = subplot(1,2,2);
    surf(membrane(1,15));
    axis tight;
    %
    linkprop(hAxis,'View'); % link the view
    view([-54 26])

    figure('Units','normalized','OuterPosition',[.10 .10 .80 .80]) % hAxis(1) = subplot(1,2,1); surf(peaks(31)); axis tight; % hAxis(2) = subplot(1,2,2); surf(membrane(1,15)); axis tight; % linkprop(hAxis,'View'); % link the view view([-54 26])

    Now, just press the “Rotate 3D” toolbar button, and have both axes rotating together.

    • Yair Altman May 12, 2015 at 05:00 Reply

      cute 🙂

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (email)
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
ActiveX (6) AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
  • Josh (10 hours 14 minutes ago): Dear Yair, Small typo; you wrote >>Move lines up or down – CTRL + ALT + UP or DOWN allows you to move selected lines up or down but it’s actually ALT+SHIFT then UP/DOWN...
  • Yair Altman (7 days 4 hours ago): You can try to increase the Java heap memory size in the Matlab preferences (General => Java Heap Memory). Any changes to the settings will only take effect after restarting...
  • Thomas (7 days 6 hours ago): Hello, thanks for sharing! I currently receive the following memory error message when using savezip with a big object (Matlab R2020b, Windows 10). Error using...
  • Yair Altman (10 days 13 hours ago): Yerlan – either use menuItem1.setEnabled(0) or set(menuItem1,'Enabled',0)
  • Manzn (10 days 16 hours ago): Thank you man! you saved me, when there was no more light 😀
  • Yerlan (11 days 21 hours ago): Hello Mr. Altman, how can I disable menu items in the context menu? E.g. when I am trying to do this: menuItems = jmenu.getSubElements; menuItem1 = menuItems(1);...
  • Yair Altman (12 days 13 hours ago): Thanks for the note Eric – you forgot the crucial call to jTable.setLabelTable(labelTabl e) – I added it into your code snippet above.
  • Erik (14 days 0 hours ago): Thank you for this — I don’t know if this has been mentioned anywhere else before, but it could be useful to mention how to add custom labels to a jslider. I did the...
  • turhv (26 days 19 hours ago): very nice! work perfectly to me in MATLAB 2019a. thanks!!!
  • Jianfei (59 days 22 hours ago): I have tried the MathWorks CheckBoxList in Matlab 2015b. For whatever the reason, I can’t change the font properties. I can change the font color, but changing font properties...
  • Donato Coppola (65 days 18 hours ago): Hi Yair, I have a problem with the double format. When I run the treeTable function, the numbers in double format cells are displayed with comma as decimal separator. How can...
  • Kim (71 days 8 hours ago): Yair, the following didn’t work for me either: jh.setBorderPainted(false); Regards, Kim
  • Adil (73 days 9 hours ago): Thank you for the blog, it was useful for me. I have a file named App_project.mlapp.zip and when I extract it through Winzip it gives all the files exactly as you described above. I...
  • Mr Ashley Trowell (75 days 19 hours ago): Thank you so much for this analysis. Also, I find it somewhat horrifying. The take away seems to be to use && / || and NOT and / or. Thanks a bunch! ~Ashley
  • Matt (80 days 21 hours ago): Late reply I know, but you can call custom shortcuts with alt-#. Hold down Alt to see what number is assigned to the shortcuts you’ve created. Now if there was a way to create a...
Contact us
Undocumented Matlab © 2009 - Yair Altman
Scroll to top