Using LimitsChangedFcn to sync axes of different x-scales

조회 수: 24 (최근 30일)
Spencer Chen
Spencer Chen 2021년 8월 18일
편집: Adam Danz 2022년 3월 28일
Trying out the newly added LimitsChangedFcn callback in the XAxis of Axes objects in R2021a. I'm running into callback re-entry problems that I need some help with.
I want to sync the xlim of multiple axes with different x-scales such that when zoom and pan are executed, all axes shift and scale correspondingly. I attached a sample script of what I'm trying to achieve. This script sets up so that the bottom axis shift and scale accordingly to zoom and pan on the top axis.
The problem occurs when I want to set up so that zoom and pan of the bottom axis will also update the top axis accordingly. That is with the following line uncommeted from my script:
% ax(2).XAxis.LimitsChangedFcn = {@syncxlim, ax};
With both axes set up to update each other on LimitsChangedFcn callback, they infinitely re-activate the callback. That is change in ax(1) activates the callback to update limits on ax(2), which then activates the callback again to update limits on ax(1), etc, etc, etc.
Any suggestions on how I can get this working?
Thanks, heaps.
Blessings,
Spencer
  댓글 수: 1
Adam Danz
Adam Danz 2022년 3월 28일
Hi Spencer, I just ran into your question today (7 months later) and I enjoy the topic. You may have figured out a solution by now but I've added perhaps a different solution.

댓글을 달려면 로그인하십시오.

답변 (1개)

Adam Danz
Adam Danz 2022년 3월 28일
편집: Adam Danz 2022년 3월 28일
> Sync the xlim of multiple axes with different x-scales such that when zoom and pan are executed all axes shift and scale correspondingly
This solution uses the scaling logic used in this answer but extends it to affect all axes which requires adding another step to prevent recursion. To escape recursion, when the current xlims match the computed xlims within a threshold of error, the xlims setting is stopped.
Critically, the updated xlim calculations use the original/initial xlim data to maintain scales rather using the current xlim values. This prevents the gradual accumulation of error.
For some background info on LimitChangedFcn, see this Community Highlight.
Generate figure
Adapted from OP's code, this uses 3 axes with different x-axis ranges.
figure;
tiledlayout(3,1);
ax(1) = nexttile;
plot(ax(1),1:0.1:11, rand(1,101));
xline(ax(1),6,'m-','LineWidth',2)
axis(ax(1),'tight') % for demo purposes only
ax(2) = nexttile;
plot(ax(2),0:0.5:20, rand(1,41));
xline(ax(2),10,'m-','LineWidth',2)
axis(ax(2),'tight') % for demo purposes only
ax(3) = nexttile;
plot(ax(3),50:50:1000, rand(1,20));
xline(ax(3),525,'m-','LineWidth',2)
axis(ax(3),'tight') % for demo purposes only
% Render plots before triggering callback
drawnow();
Store the original x-limits and x-limit-ranges. This must be done after setting the initial desired xlimits for the axes.
% Original xlims
xLimits = vertcat(ax.XLim);
% Original x axis ranges
xRanges = diff(xLimits,1,2);
ax(1).XAxis.LimitsChangedFcn = {@syncxlim, ax(:), xLimits, xRanges};
ax(2).XAxis.LimitsChangedFcn = {@syncxlim, ax(:), xLimits, xRanges};
ax(3).XAxis.LimitsChangedFcn = {@syncxlim, ax(:), xLimits, xRanges};
Define the LimitsChangedFcn
function syncxlim(src, event, axs, xLimits, xRanges)
% Responds to changes to x-axis limits in axes listed in axs.
% Updates xlims to maintain original scales.
% axs: nx1 vector of axes handles
% xLimits: nx2 matrix of original [min,max] xlims
% xRanges: nx1 vector of original axis ranges
% Index of axes that just changed
axIdx = axs == src.Parent;
% Compute the new xlims for axes that weren't just changed
normLowerLim = (event.NewLimits(1) - xLimits(axIdx,1)) / xRanges(axIdx);
newLowerLimits = normLowerLim * xRanges(~axIdx) + xLimits(~axIdx,1);
newUpperLimits = newLowerLimits + diff(event.NewLimits) .* xRanges(~axIdx)./ xRanges(axIdx);
newXLimits = [newLowerLimits, newUpperLimits];
% Only update if the new XLimits significantly differ from current xlims
allCurrentXLims = cell2mat(get(axs(~axIdx),'xlim'));
if any(abs(allCurrentXLims - newXLimits) > (1E-8 * xRanges(~axIdx)),'all')
set(axs(~axIdx), {'xlim'}, mat2cell(newXLimits, ones(sum(~axIdx),1), 2))
drawnow
pause(0.05) % needed to prevent reentry
end

카테고리

Help CenterFile Exchange에서 Visual Exploration에 대해 자세히 알아보기

제품


릴리스

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by