Code covered by the BSD License  

Highlights from
SHADOWPLOT

image thumbnail
from SHADOWPLOT by Scott Hirsch
Add a shadow to an existing surface plot.

shadowplot(varargin)
function varargout = shadowplot(varargin)
% SHADOWPLOT    Add a shadow to an existing surface or patch plot
%
% For some surface plots, it can be helpful to visualize the shadow (2D
% projection) of the surface.  This can give a quick perspective on the
% data's variance.
%
% SHADOWPLOT PLANE   Adds a shadow plot on the PLANE boundary
%  PLANE can be:
%    x, y, or z: Plots on back/top wall of x, y or z
%    1 .. 6    : Plots on Nth wall, numbered as in AXIS:
%                [xmin xmax ymin ymax zmin zmax]
%
% SHADOWPLOT(HAX,PLANE) Adds a shadow plot on the Nth wall on axes HAX
% HS = SHADOWPLOT(...) Returns a handle to the shadow (a patch)
%
% Examples:
%    figure
%    surf(peaks)
%    shading interp
%    shadowplot x   % Back X Wall
%    shadowplot y   % Back Y Wall
%
%    figure
%    surf(peaks);hold on
%    surf(peaks+10)
%    shading interp
%    hs = shadowplot(1);
%    set(hs,'FaceColor','r');     % Red shadow
%    alpha(hs,.1)                 % More transparent
%    set(hs(1),'XData',get(hs(1),'XData')*.9)  % Move farther away
%
% UPDATE (9/07): Now includes limited support for data encapsulated in
% HGTRANSFORMS, thanks to Patrick Barney (psbarne@sandia.gov).  


% Scott Hirsch
% shirsch@mathworks.com
% Copyright 2004-2007 The MathWorks, Inc

%% We define three dimensions.  1=x, 2=y, 3=z
% dimplane - dimension that's constant in the projection plane (user-specified)
% dimvar - dimension in which data varies (typically 3)
% dimother - the other dimension (couldn't come up with a good name!).  

%% Parse input arguments.
if nargin==1
    hAx = gca;
    plane = lower(varargin{1});
elseif nargin==2
    hAx = varargin{1};
    plane = lower(varargin{2});
end;

%% Convert plane to numeric dimension
% plane can be specified as a string (x,y,z) or as a number (1..6)
if ~isstr(plane)
    dimplane = ceil(plane/2);
    axind = plane;  % Index into AXIS to get boundary plane
else    % string
    switch plane
        case 'x'
            dimplane = 1;
            axind = 2;  % Index into AXIS to get boundary plane
        case 'y'
            dimplane = 2;
            axind = 4;
        case 'z'
            dimplane = 3;
            axind = 6;
        otherwise
            error('Plane must be one of: ''x'', ''y'', or ''z'' or a number between 1 and 6');
    end;
end;

%% Get coordinates for placing surface from axis limits
ax = axis;
% ============ force axis into 3d mode =============
if length(axis==4)
  % axis problem. get the current view, rotate it, then
  % redo the axis and return to the original view.
  [az,el] = view;
  view(45,45)
  ax = axis;
  view(az,el)
end
planecoord = ax(axind);     % Plane Coordinate - back wall

%% Turn hold on
hold_current = ishold(hAx);
if hold_current == 0
    hold_current = 'off';
else
    hold_current = 'on';
end;

hold(hAx,'on')

%% Get handles to all surfaces
kids = findobj(hAx,'Type','surface');
h = [];

% Also get handles to all patch objects
kidsp = findobj(hAx,'Type','patch');
hp = [];

for ii=1:length(kids)       % Do separately for each surface
    hSurf = kids(ii);   % Current surface

    % We do everything with the X, Y, and ZData of the surface
    surfdata = get(hSurf,{'XData','YData','ZData'});

    % XData and YData might be vectors or matrices.  Force them to be
    % matrices (a la griddata)
    [Ny,Nx] = size(surfdata{3});
    if isvector(surfdata{1})
        surfdata{1} = repmat(surfdata{1},Ny,1);
    end;
    if isvector(surfdata{2})
        surfdata{2} = repmat(surfdata{2},1,Nx);
    end;

    % Figure out which two axes are independent (i.e., monotonic)
    grids = [ismeshgrid(surfdata{1}) ismeshgrid(surfdata{2}) ismeshgrid(surfdata{3})];
    if sum(grids)<2, error('Surface must have at least 2 monotonically increasing dimensions');end

    % The remaining dimension is the one along which data varies
    dimvar  = find(~grids); % Dimension where data varies
    if isempty(dimvar)  % All 3 dimensions are monotonic.  not sure what to do
        dimvar = max(setdiff(1:3,dimplane));% pick largest value that isn't dimplane
    end;
    
    if dimvar==dimplane
        error('Can not project data in the dimension that varies.  Try another plane')
    end;

    %dimdiff: dimension for taking difference (figure out through trial and error)
    % dimplane=1, dimvar=3: 2
    % dimplane=1, dimvar=2: 2
    % dimplane=2, dimvar=1: 2
    % dimplane=2, dimvar=3: 1
    % dimplane=3, dimvar=2: 1
    % dimplane=3, dimvar=1: 1
    dimdiff = 2;    % Most cases
    if (dimplane==2&&dimvar==3) | (dimplane==3)
        dimdiff = 1;
    end;
    
    % Compute projection
    dmin = min(surfdata{dimvar},[],dimdiff);    % Min of data projected onto this plane
    dmax = max(surfdata{dimvar},[],dimdiff);    % Max of data projected onto this plane

    dmin = dmin(:); % Force into row vector
    dmax = dmax(:);

    nval = length(dmin)*2 + 1;  % Total number of values we'll use for shadow

    % Compute shadow coordinates
    % Pull out independent variable
    dimother = setxor([dimvar dimplane],1:3); % Remaining dimension
    d1 = surfdata{dimother}(:,1);    % Not sure if should take row or col. find the dimension that varies
    d2 = surfdata{dimother}(1,:);
    if d1(1) ~= d1(end)
        dind = d1;
    else
        dind = d2';
    end;


    shadow{dimplane} = repmat(planecoord,nval,1);       % In the plane
    shadow{dimother} = [dind;flipud(dind);dind(1)];     % Independent variable
    shadow{dimvar} = [dmin;flipud(dmax);dmin(1)];       % the varying data

    h(ii) = patch(shadow{1},shadow{2},shadow{3},[.3 .3 .3]);
    alpha(h(ii),.3)
    set(h(ii),'LineStyle','none')
    % set a tag, so that a shadow will not try to cast a shadow
    set(h(ii),'Tag','Shadow')
end;

%% Shadow any patches, unless they are already shadows.
hp = [];

for ii=1:length(kidsp)       % Do separately for each patch object
    hPat = kidsp(ii);   % Current patch

    % Is this patch already tagged as a Shadow?
    if ~strcmpi(get(hPat,'Tag'),'Shadow')

      % We do everything with the X, Y, and ZData of the surface
      patdata = get(hPat,{'XData','YData','ZData'});

      switch get(get(hPat,'par'),'Type')
          case 'hgtransform'
              M=get(get(hPat,'par'),'Matrix');
              try
                  switch get(get(get(hPat,'par'),'par'),'type')
                      case 'hgtransform'
                          M2=get(get(get(hPat,'par'),'par'),'Matrix');
                          M=M2*M;
                  end
              end
              M=M(1:3,1:3);
              xyz=[patdata{1}(:),patdata{2}(:),patdata{3}(:)]*M';
              [n,m]=size(patdata{1});
              patdata{1}=reshape(xyz(:,1),n,m);
              patdata{2}=reshape(xyz(:,2),n,m);
              patdata{3}=reshape(xyz(:,3),n,m);
          otherwise
      end
      % Just replace the x, y, or z coordinate as indicated by dimplane
      patdata{dimplane} = repmat(planecoord,size(patdata{dimplane}));

      % then its just a call to patch
      hp(ii) = patch(patdata{1},patdata{2},patdata{3},[.3 .3 .3]);
      alpha(hp(ii),.3)
      set(hp(ii),'LineStyle','none')
      
      % set a tag, so that a shadow will not try to cast a shadow
      set(hp(ii),'Tag','Shadow')
    end
end;
h=[h,hp];

hold(hAx,hold_current)  % Return to original state

if nargout
    varargout{1} = h;
end;

function isgrid = ismeshgrid(d)
% Check if d looks like it came from griddata
dd = diff(d);
ddt = diff(d');

if all(~dd(:)) | all(~ddt(:))
    isgrid = 1;
else
    isgrid = 0;
end;
% if ~any(d(:,1) - d(:,end)) | ~any(d(1,:) - d(end,:))
%     isgrid = 1;
% else
%     isgrid = 0;
% end;


Contact us at files@mathworks.com