Mr. W.
Mr. W.

Reputation: 369

Link different properties of MATLAB axes

Is it possible to link, for example, the 'XLim' property of one MATLAB axis to the 'YLim' property of another axis? I have looked into linkaxes and linkprop but as far as I can tell, they can only link same properties, e.g. one axis 'XLim' to another axis 'XLim' and so on.

Using MATLAB 2014b. Thanks!

Upvotes: 6

Views: 1197

Answers (6)

Thomas Wheatcroft
Thomas Wheatcroft

Reputation: 11

It is possible in MATLAB 2024_b.

Given two axes ax1 and ax2, you can simply do

linkprop([ax1.YAxis,ax2.XAxis],'Limits')

Upvotes: 1

Luis Erasmo
Luis Erasmo

Reputation: 1

this is my solution, works in R2019a function

function crosslinkaxes(ax,str)
%
% ax(1)=subplot(1,2,1);
% ax(2)=subplot(1,2,2);
% str='xy'; %link, x limits of ax(1) with ylimits of ax(2)
% crosslinkaxes(ax,str)

for k=1:numel(ax)
    propListener(k) = addlistener(ax(k),expand(str(k)),'PostSet',@event);
end

    function p=expand(str)
        switch str
            case 'x'
                p='XLim';
            case 'y'
                p='YLim';
            otherwise

                error(str)
        end
    end
    function event(src,evnt)
        %src.Name
        disp('lims')
        L=evnt.AffectedObject.(src.Name);
        for k=1:numel(ax)
            ax(k).(expand(str(k)))=L;

        end
        %      keyboard

    end
end

Upvotes: 0

Hoki
Hoki

Reputation: 11812

I worked up a way to it, it works but it remains slightly clunky ...

The idea is obviously to attach listeners to the two properties you want to link, and update one property each time the other one is changed.

It takes a bit of fiddling around just to be able to attach a listener to the properties of the built-in axes object, you have to retrieve the associated meta.property. I package this in an external subfunction findPropertyHandle.

I made the solution generic so you can link any type of property together. Now I didn't program input checks so of course it will work only on compatible properties. XLim is obvisouly compatible with YLim, but if you try to link XLim with PlotBoxAspectRatioMode you're in for a deluge of red and orange text in your console.

%% Demo data and figure
x = linspace(0,24*pi,1000) ;
y = cos(x) ;
hf = figure ;
hax1 = subplot(1,2,1) ; hp1 = plot(x,y) ;
hax2 = subplot(1,2,2) ; hp2 = plot(y,x) ;

%% Find (meta)property handle
prop1 = findPropertyHandle( hax1 , 'XLim' ) ;
prop2 = findPropertyHandle( hax2 , 'YLim' ) ;

%% Add a listener to each property
% no callback yet because we need the reference of both listeners to be part of the callback arguments
lh1 = addlistener( hax1, prop1 , 'PostSet', @() [] ) ;
lh2 = addlistener( hax2, prop2 , 'PostSet', @() [] ) ;

%% group a few things for convenience
pl.obj2link   = [ hax1  ; hax2  ] ;     % axes handles
pl.props2link = { 'XLim' ; 'YLim' } ;   % name of properties to link
pl.listeners  = [ lh1 ; lh2 ] ;         % listener handle
pl.metaprops2link = [ prop1 ; prop2 ] ; % metaproperty handles

%% Set the callback of both listeners
% Have to use dot notation because the 'set' method is not defined for listener objects
lh1.Callback = @(h,e) LinkPropChangedFcn(h,e,pl) ;
lh2.Callback = @(h,e) LinkPropChangedFcn(h,e,pl) ;

Linked properties


The code for LinkPropChangedFcn.m

function LinkPropChangedFcn( hobj, evt, plink )
%LinkPropChangedFcn Links 2 compatible properties

    % find which property was triggered
    changedPropIdx = find( hobj==plink.metaprops2link ) ;
    prop2changeIdx = 3-changedPropIdx ; % because cumsum([1 2])=3

    % disable the other listener to not trigger the property change on the
    % target object
    plink.listeners(prop2changeIdx).Enabled = false ;

    % retrieve property value
    val = get( plink.obj2link(changedPropIdx) , plink.props2link{changedPropIdx} ) ;

    % aplpy the value to the target object property
    set( plink.obj2link(prop2changeIdx) , plink.props2link{prop2changeIdx} , val ) ;

    % re-enable target listener
    plink.listeners(prop2changeIdx).Enabled = true ;

end

The code for findPropertyHandle.m

function [ hprop ] = findPropertyHandle( hobj , propname )
%FINDPROPERTYHANDLE retrieve the handle of the meta.property object

    mco = metaclass(hobj) ;
    plist = mco.PropertyList;
    for k=1:numel(plist)
        if strcmpi(plist(k).Name,propname)
            fprintf('[%s] property was found at index #%d\n',propname,k)
            hprop = plist(k) ;
            return
        end
    end
    % no preperty was found if we are here
    hprop = [] ;
    fprintf('[%s] property was not found.\n',propname)

end

Note The clunkiness I was talking about is when instead of "zooming out" you use the "reset to original view" menu. This function does not trigger the PostSet event of the XLim property, so the change is not always propagated to the other axes/property. To overcome that, just after using "reset to original view", right click again and "zoom out". This will not zoom further out in the clicked axes (if you were already at the "original view"), but it will trigger the listener and update the second target axes.

Upvotes: 4

aka.nice
aka.nice

Reputation: 9582

Yes it is possible. Don't trust the documentation. I just had the same need: I have a surface z(x,y):

x=-10:10; y=-15:15;
[xx,yy]=meshgrid(x,y);
z=real( exp( -(xx.^2/9+yy.^2/4+xx.*yy/12)) .* exp( 1i * (xx/4+yy/9-xx.*yy/12+1)));
xz= sum(z,1);
zy= sum(z,2);

And want to plot surface and partial sums like this

figure;
zyy= subplot(2,2,1); plot(zy,y);
xyz= subplot(2,2,2); contour(x,y,z);
xxz = subplot(2,2,4); plot(x,xz);

What I want is to link x-axis of xyz and xxz and y-axis of zyy and xyz.

plot zyy xyz and xxz

As advertised in documentation of linkaxes, this does not work:

linkaxes([xyz xxz],'x');
linkaxes([xyz zyy],'y');

However, decomposing like this works as expected, at least in Matlab 2012b:

set([xyz xxz],'XLimMode','manual');
set([xyz zyy],'YLimMode','manual');
hx=linkprop([xyz xxz],'XLim');
hy=linkprop([xyz zyy],'YLim');
setappdata(xxz,'graphics_linkaxes',hx);
setappdata(zyy,'graphics_linkaxes',hy);

If you look at source code of linkaxes, you will find that the code is attempting to setappdata on both end of the link, which effectively cannot work, but seems un-necessary.

Upvotes: 2

Luis Mendo
Luis Mendo

Reputation: 112769

You could use a trick: use linkaxes to link the 'XLim' property of both axes. In the second axis, instead of

plot(x2,y2)

you use

plot(y2,x2)
view(90,-90)

That way the x-coordinate in your second axis contains the y data (thanks to the inversion of plot arguments) and is seen at the y position (thanks to the change of view); and similarly for the other coordinate.

So you are basically linking both 'XLim' values, but letting the x-coordinate of the second axis look exactly as if it's the y coordinate.

Upvotes: 1

Sam Roberts
Sam Roberts

Reputation: 24147

No, it's not possible to do that with linkaxes or linkplot.

I'm afraid you'll have to create your own system that roughly replicates what linkprop does internally, which is to listen for events on one axis, and respond to them by making corresponding changes to the other. I'm afraid linkprop only lets you do that for the same property on each.

Upvotes: 0

Related Questions