Clear Filters
Clear Filters

Mimic axis equal in secondary y-axis

45 views (last 30 days)
Chris Nemecek
Chris Nemecek on 27 Aug 2024 at 21:38
Commented: Umar on 29 Aug 2024 at 1:07
Is there a way to mimc 'axis equal' for a secondary axis? 'Axis equal' doesn't work when using yyaxis(ax,'right').
In addtion, the mimic should resize the same as if 'axis equal' was called on a single plot.

Accepted Answer

Chris Nemecek
Chris Nemecek on 28 Aug 2024 at 23:06
The solution to the problem I developed is given below. It relies on a changedfcn to mimic axis equal when the figure is resized or the axes limits change via setting manually or scrolling.
% Plot lines on left y-axis
f = figure(); ax = axes(f);
plot(ax,a,b,'-k',c,d,'-b')
xlim([500 3500])
% Plot patch on right y-axis
yyaxis(ax,'right')
patch(ax,x,y,[0.5 0.5 0.5])
set(ax,'SortMethod','depth')
% Set changed functions to mimic axis equal when figure is resized or axes are changed/zoomed
f.SizeChangedFcn = {@(src,event)axisEqual(src,event,gcf,gca)};
ax.XAxis.LimitsChangedFcn = {@(src,event)axisEqual(src,event,gcf,gca)};
function axisEqual(src,~,f,ax)
yyaxis(ax,'right')
ax.YAxis(2).Visible = "off";
ax.Box = "off";
ax.Children.PickableParts = "none";
scale = (f.Position(4)/f.Position(3)) * (ax.Position(4)/ax.Position(3));
xx = xlim(ax);
xRange = xx(2) - xx(1);
% Centers on y-axis and scales approriately to keep a 1:1 aspect ratio
ylim(ax,scale*[-xRange/2 xRange/2])
yyaxis(ax,'left')
end

More Answers (1)

Umar
Umar on 28 Aug 2024 at 1:25

Hi @Chris Nemecek ,

Let me address your query regarding, “Is there a way to mimc 'axis equal' for a secondary axis? 'Axis equal' doesn't work when using yyaxis(ax,'right').In addtion, the mimic should resize the same as if 'axis equal' was called on a single plot.”

Please see my response to your comments below. So, first a sample data is generated for two different functions. Then, a figure is created, and the first y-axis is plotted. The second y-axis is activated, and the corresponding data is plotted along with the retrieval of limits of both axes , and the aspect ratio is calculated. Based on the aspect ratio, the limits of the axes are adjusted to mimic axis equal. Finally, the axis tight command is used to make sure that the axes fit the data closely. Here is completely example code,

% Sample Data
x = linspace(0, 10, 100);
y1 = sin(x);
y2 = cos(x) * 10; % Scale to demonstrate secondary axis
% Create a figure
figure;
% Create the first axis
yyaxis left;
plot(x, y1, 'b-', 'LineWidth', 2);
ylabel('Sine Values');
xlabel('X Values');
grid on;
% Create the second axis
yyaxis right;
plot(x, y2, 'r-', 'LineWidth', 2);
ylabel('Cosine Values (scaled)');
% Mimic 'axis equal'
% Get limits for both axes
xLimits = xlim;
y1Limits = ylim; % Left axis limits
yyaxis right; % Activate right axis to get its limits
y2Limits = ylim; % Right axis limits
% Calculate aspect ratio
aspectRatio = (y1Limits(2) - y1Limits(1)) / (y2Limits(2) - y2Limits(1));
% Set equal aspect ratio
if aspectRatio > 1
  % Adjust x limits based on aspect ratio
  xlim([xLimits(1), xLimits(1) + (y1Limits(2) - y1Limits(1)) / aspectRatio]);
else
  % Adjust y limits based on aspect ratio
  ylim([y2Limits(1), y2Limits(1) + (xLimits(2) - xLimits(1)) * aspectRatio]);
end
% Final adjustments
axis tight; % Tighten the axes to fit the data

So, as you can see by looking at this code, it mimics the axis equal functionality for plots with dual y-axes. By calculating the aspect ratio and adjusting the limits accordingly, you will be satisfied that plots maintain a proportional relationship between the axes.

Please see attached.

Please let me know if you have any further questions.

  4 Comments
Chris Nemecek
Chris Nemecek on 28 Aug 2024 at 23:00
While the above appears like it would work, it seems like it lacks robustness. For example, you have to manually know the limits of your data and set the tick marks manually to mimic axis equal. So while it answers the question in spirit, I think it lacks rigor.
Umar
Umar on 29 Aug 2024 at 1:07

Hi @Chris Nemecek ,

To address the concerns raised above and enhance the robustness of my MATLAB plotting code, I have implemented a more dynamic approach that automatically adjusts axis limits and tick marks based on the data. Here’s a refined version of my code that includes these improvements:

% Sample Data
x = linspace(0, 10, 100);
y1 = sin(x);
y2 = cos(x) * 10; % Scale to demonstrate secondary axis
% Create a figure
figure;
% Create the first axis
yyaxis left;
plot(x, y1, 'b-', 'LineWidth', 2);
ylabel('Sine Values');
xlabel('X Values');
grid on;
% Create the second axis
yyaxis right;
plot(x, y2, 'r-', 'LineWidth', 2);
ylabel('Cosine Values (scaled)');
% Dynamically get limits for both axes
xLimits = xlim;
y1Limits = ylim; % Left axis limits
yyaxis right; % Activate right axis to get its limits
y2Limits = ylim; % Right axis limits
% Calculate aspect ratio dynamically
aspectRatio = (y1Limits(2) - y1Limits(1)) / (y2Limits(2) - 
y2Limits(1));
% Set equal aspect ratio dynamically
if aspectRatio > 1
  % Adjust x limits based on aspect ratio
  xlim([xLimits(1), xLimits(1) + (y1Limits(2) - y1Limits(1)) / 
  aspectRatio]);
else
  % Adjust y limits based on aspect ratio
  ylim([y2Limits(1), y2Limits(1) + (xLimits(2) - xLimits(1)) * 
  aspectRatio]);
end
% Final adjustments
axis tight; % Tighten the axes to fit the data
% Dynamically set equidistant ticks for both axes based on limits
set(gca, 'XTick', linspace(xLimits(1), xLimits(2), 5), ...
        'YTick', linspace(min([y1Limits(1), y2Limits(1)]), ...
                         max([y1Limits(2), y2Limits(2)]), 5));
grid on; % Ensure grid is displayed

So, this revised code uses linspace to automatically generate tick marks for both axes based on their current limits which eliminates the need for manual input and increases flexibility when working with different datasets. The aspect ratio calculation remains intact but is now tied directly to the dynamic limits retrieved from the plot which makes sure that any changes in data are automatically reflected in how axes are adjusted. Also, relying on calculated limits rather than hard-coded values, this code is more adaptable to varying datasets, enhancing its robustness and the inclusion of grid on makes sure that users can visually confirm that tick marks are equidistant and aligned properly across both axes.

I will advise to test this code with various datasets to ensure that it behaves as expected under different conditions and helps verify that plot remains clear and informative regardless of changes in data ranges. I will suggest testing this code across various datasets such as

Use sine and cosine functions with different frequencies or amplitudes (e.g., y1 = sin(2*x) or y2 = cos(3*x) * 20 to observe how well the dual-axis handles varying scales. Secondly, test with datasets that have different ranges (e.g., negative values or larger magnitudes) to verify that axis limits adjust correctly. Also, include edge cases such as all zero values or constant functions to see how well the plot adapts.

Please let me know if you have any further questions or need additional modifications!

Sign in to comment.

Categories

Find more on Axes Appearance in Help Center and File Exchange

Products


Release

R2023b

Community Treasure Hunt

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

Start Hunting!