How can I rescale a patternCustom plot?

79 views (last 30 days)
Scott
Scott on 15 Oct 2025 at 18:53
Commented: dpb about 23 hours ago
I'm plotting multiple patternCustom plots in a tiledlayout, and would like to rescale the individual plots so that they all have the same magnitude. I've tried many things (I don't remember all what I tried, it was over a month ago) and couldn't get anything to work. The patternCustom function does not support the typical name-value pair arguments and seems really inflexible.
  10 Comments
Umar
Umar on 17 Oct 2025 at 23:42

@Scott,

One more thing I wanted to share — @dpb was genuinely trying to help you out. There are so many talented contributors on this platform, like @Stephen23, @ImageAnalyst, @Star Strider, @John D Errico, @Walter Robertson, @Torsten — and @dpb is certainly among them.

I’ve always respected his thoughts and opinions. We're all here to learn, grow, and make a difference — not just in our own work, but in the lives of students, scholars, and fellow contributors.

I won’t be on this platform much longer, but I’m grateful for the memories and the chance to make a positive impact. I hope you understand where I’m coming from.

Scott
Scott about 2 hours ago
@Umar Thanks for this reply. It makes me happy to see goodwill like this in an online forum. I appreciated @dpb's suggestion and I was trying to express my reasoning for not following that approach but didn't mean to sound disdainful or contemptuous. I apologize if I came off that way!

Sign in to comment.

Accepted Answer

dpb
dpb on 18 Oct 2025 at 14:29
Edited: dpb on 18 Oct 2025 at 23:59
OK, I had some time to try to play around a little this morning when didn't have to interact with the local community college Foundation staff ladies I've been doing pro bono work for to let them keep moving...
Try the following that fixes up the sizes of the images by scaling the plotted X, Y, ZData arrays that internally are scaled to full axes dimensions. It also finds the bounding circles and scales them as well as the surface data.
num_plots = 2;
num_azimuth_idxs = 100;
num_zenith_idxs = 100;
phi = linspace(-180, 180, num_azimuth_idxs);
theta = linspace(0, 180, num_zenith_idxs);
scales = [1, 0.5];
%patternCustom uses a strange input format, accomodate...
pattern_phi = repmat(phi, 1, num_zenith_idxs);
pattern_theta = repelem(theta, num_azimuth_idxs);
tiled_fig = figure;
tiled_plot = tiledlayout(tiled_fig, 1, num_plots);
% Prepare dummy E-field data
magE = zeros(num_azimuth_idxs * num_zenith_idxs, 1);
ax_handles = gobjects(1, num_plots);
hS=gobjects(1, num_plots); % going to need the surface handles, too...
for ii = 1:num_plots
pattern_idx = 1;
for az_idx = 1 : num_azimuth_idxs
for ze_idx = 1 : num_zenith_idxs
magE(pattern_idx) = abs(scales(ii) * cos(deg2rad(phi(az_idx) + theta(ze_idx))));
pattern_idx = pattern_idx + 1;
end
end
% Throw it up on a plot
ax_handles(ii) = nexttile(tiled_plot);
% Adjust the magnitude of the pattern plot to reflect the scaling
hS(ii)=patternCustom(magE, pattern_theta, pattern_phi); % save the handle, too...
hS(ii).XData=scales(ii)*hS(ii).XData; % and apply the scale factor
hS(ii).YData=scales(ii)*hS(ii).YData;
hS(ii).ZData=scales(ii)*hS(ii).ZData;
hL=findobj(ax_handles(ii),'-regexp','tag','.Circle'); % find the bounding circles
for h=hL.' % and scale them, too...
h.XData=scales(ii)*h.XData;
h.YData=scales(ii)*h.YData;
h.ZData=scales(ii)*h.ZData;
end
end
% the default formatting with '%g' is unspeakedly ugly when not all data are same precision
% NB: uses undocumented/hidden 'Ruler' property of the colorbar to set formatting string
hCB=findobj(gcf,'tag','Colorbar'); % find the colorbar handles
cellfun(@(h)set(h,'TickLabelFormat','%0.1f'),get(hCB,'Ruler')); % set to one decimal
% Now scale all of the plots to be the same scale
max_mag = -Inf;
% Loop through each axis handle to find the maximum magnitude
for tile_idx = 1:num_plots
current_mag = caxis(nexttile(tile_idx));
max_mag = max(max_mag, current_mag(2));
end
% Set the same limit for all plots
for tile_idx = 1:length(ax_handles)
caxis(nexttile(tile_idx), [0 max_mag]);
end
For data that aren't exactly scaled replicas of each other, one will have to compute a scale factor for each dimension based on the data ranges to apply; here it's easy since you have a factor defined.
I think this would be a valid use case to submit as an enhancement request in that it does make sense to be able to show the relative magnitudes as an option as well as the builtin default of trying to show the most detail possible.
This is the approach I had in mind that doesn't require replotting and why I suggested to save the object handles. I had initially thought the returned handles would also include the lines, not just the surface, but they're not hard to find since they did conveniently tag them. You could wrap that section of code into an internal function to clean up the appearance of the top level code a little if desired.
NOTA BENE: I didn't experiment with whether after rescaling the plotted data you might be able to do away with the extra color limits stuff as they just might be scaled as you want once the data themselves are.
  2 Comments
Scott
Scott about 2 hours ago
I haven't tested this since I already started with Umar's approach and that's working for me, but I like how this uses the original patternCustom function, so this would have been the easiest to integrate into my application. This seems like a great solution and I appreciate your help? Can I give credit to both of you guys?
dpb
dpb 3 minutes ago
I think you can only Accept one Answer, but you can vote for as many as want.
Sorry, but I simply didn't have time to be able to look in enough depth to be fully confident of it working as desired during the earlier conversation.
At my age, I'll probablly not live long enough given the high bar for Level 10, so the difference of just a couple points isn't going to matter too much! <vbg>

Sign in to comment.

More Answers (2)

Umar
Umar on 15 Oct 2025 at 23:10

Hi @Scott,

Thanks for the additional information! It sounds like you're trying to plot multiple `patternCustom` plots in a tiled layout while ensuring that all the plots share the same magnitude scaling. I totally get that `patternCustom` can be a bit tricky when it comes to scaling the magnitude consistently across multiple plots.

In your case, since you're not using the default `'CoordinateSystem', 'rectangular'` or `'polar'`, you're likely working with *spherical coordinates* (phi and theta). And since `patternCustom` doesn't offer a straightforward way to rescale the magnitude across multiple plots, let me offer you two approaches: one without any toolbox (using basic MATLAB functions) and the other with the *Antenna Toolbox* if you still want to stick to `patternCustom`.

Solution Without Toolbox

If you're looking for a toolbox-free approach, here's a simple solution using *basic MATLAB functions* that rescale the magnitude and visualize it in a tiled layout. The code uses `surf`, `meshgrid`, and `normalize` to ensure the magnitude is consistent across subplots.

Example Code (No Toolbox)

% Example data (replace with actual data)
az = linspace(0, 360, 100);  
el = linspace(-90, 90, 100);  
[Az, El] = meshgrid(az, el);  
MagE = sin(Az) .* cos(El);  % Example E-field pattern data
% Normalize the magnitude to [0, 1]
MagE_normalized = normalize(MagE, 'range');
% Create a tiled layout
figure;
tiledLayout = tiledlayout(2, 2);  % Create a 2x2 layout for 4 plots
% Loop through each subplot
for iax = 1:4
  nexttile;  % Move to the next subplot
    % Select pattern data for each subplot
    if iax == 1
        surf(Az, El, MagE);  % Plot original data
        title('Original Magnitude');
    elseif iax == 2
        surf(Az, El, MagE_normalized);  % Plot normalized data
        title('Normalized Magnitude');
    elseif iax == 3
        surf(Az, El, MagE * 0.1);  % Scaled data
        title('Scaled by 0.1');
    else
        surf(Az, El, MagE * 0.01);  % Another scaling factor
        title('Scaled by 0.01');
    end
    % Ensure the axes are consistent across plots
    axis([-180 180 -90 90 0 1]);  % Set common axis limits
    colormap jet;
    colorbar;  % Show color scale
  end

See attached results without using toolbox.

This approach doesn't require any special toolboxes and should give you the consistent scaling across subplots that you're looking for. If this works for you, it could be a very clean solution.

Solution With Antenna Toolbox (Using `patternCustom`)

If you're still relying on the Antenna Toolbox and want to use `patternCustom`, here’s how you can manage multiple radiation pattern plots with consistent scaling.

Rescale the Data: You can normalize the magnitude before passing it to `patternCustom`, just like we did in the example above. This ensures that each plot has the same scaling.

Adjust Plot Settings: Use the CoordinateSystem, Slice and SliceValue options to control the plot’s appearance.

Multiple Plots in a Single Figure: patternCustom allows plotting multiple radiation patterns in the same figure by passing in different data sets.

Here’s an example of how you could use `patternCustom` with normalized data for consistent scaling:

Antenna Toolbox (Using `patternCustom`)

% Assuming you already have MagE, theta, and phi
MagE_normalized = normalize(MagE, 'range');  % Normalize the magnitude
% Create a tiled layout for 4 plots
figure;
tiledLayout = tiledlayout(2, 2);  % Create a 2x2 layout
% Plotting the patterns
for iax = 1:4
  nexttile;  % Move to the next subplot
  if iax == 1
      patternCustom(MagE, theta, phi);  % Original data
      title('Original Magnitude');
  elseif iax == 2
      patternCustom(MagE_normalized, theta, phi);  % Normalized data
      title('Normalized Magnitude');
  elseif iax == 3
      patternCustom(MagE * 0.1, theta, phi);  % Scaled data
      title('Scaled by 0.1');
  else
      patternCustom(MagE * 0.01, theta, phi);  % Another scaling factor
      title('Scaled by 0.01');
  end
end

I just went through the Relevant Documentation for Toolbox Functions to find solution to your problem, here are the references:

patternCustom: Pattern Custom Documentation https://www.mathworks.com/help/antenna/ref/patterncustom.html

normalize: Normalize Documentation https://www.mathworks.com/help/matlab/ref/normalize.html

tiledlayout: Tiled Layout Documentation https://www.mathworks.com/help/matlab/ref/tiledlayout.html

If you're comfortable using basic MATLAB functions (like `surf` and `normalize`), you can achieve the same visual results without needing the Antenna Toolbox. However, if you still want to use `patternCustom`, you can normalize your data before passing it into the function to maintain consistent scaling across plots.

Let me know if you need further clarification on any part of this or if you’d like to explore other methods!

  4 Comments
Umar
Umar on 17 Oct 2025 at 21:53

Hi @Scott,

I owe you an apology - You were right to be frustrated. So, I do completely understand about what actually happened after going through your posted comments. So, let me first address the caxis approach: You nailed it - this only fixed the colormap, not the physical size of the patterns. The 0.5-scale sphere still filled the entire plot when it should've been half the size. Now, the PatternPlotOptions disaster: I called it a "confirmed solution" but you systematically proved it doesn't work:

  • The PatternOptions=po syntax failed (your MATLAB version)
  • Passing po directly failed (wrong argument type)
  • Using 'MagnitudeScale' failed (not a valid parameter)

But after looking closer at the docs and your plots, I was curious about what I was missing and the key thing was MagnitudeScale only works with the pattern() method from antenna objects, not patternCustom. Even if syntax worked, it would've been ignored. You were absolutely right about patternCustom being inflexible. Since you mentioned being open to other approaches, here's what actually works. And good news - you don't need to refactor much. The code calculates global limits first, then plots everything with consistent scaling:

clear; clc; close all;
%% Setup
num_plots = 2;
num_azimuth_idxs = 100;
num_zenith_idxs = 100;
scales = [1, 0.5];
phi = linspace(-180, 180, num_azimuth_idxs);
theta = linspace(0, 180, num_zenith_idxs);
[PHI, THETA] = meshgrid(phi, theta);
%% Generate data and find global max
magE_all = cell(num_plots, 1);
max_mag = -Inf;
for ii = 1:num_plots
  magE = abs(scales(ii) * cos(deg2rad(PHI + THETA)));
  magE_all{ii} = magE;
  max_mag = max(max_mag, max(magE(:)));
end
%% Plot
figure('Position', [100, 100, 1200, 500]);
tiled_plot = tiledlayout(1, num_plots, 'TileSpacing', 'compact');
for ii = 1:num_plots
  nexttile;
  magE = magE_all{ii};
    % KEY: Use magnitude as radius - this is what makes it work
    theta_rad = deg2rad(THETA);
    phi_rad = deg2rad(PHI);
    r = magE;  % Smaller magnitude = smaller sphere
    x = r .* sin(theta_rad) .* cos(phi_rad);
    y = r .* sin(theta_rad) .* sin(phi_rad);
    z = r .* cos(theta_rad);
    surf(x, y, z, magE, 'EdgeColor', 'none', 'FaceColor', 'interp');
    caxis([0 max_mag]);  % Uniform color scale
    axis equal; axis vis3d;
    colormap jet; colorbar;
    view(3);
    % Add coordinate axes
    hold on;
    plot3([0 max_mag*1.2], [0 0], [0 0], 'r-', 'LineWidth', 2);
    plot3([0 0], [0 max_mag*1.2], [0 0], 'g-', 'LineWidth', 2);
    plot3([0 0], [0 0], [0 max_mag*1.2], 'b-', 'LineWidth', 2);
    hold off;
    xlabel('X'); ylabel('Y'); zlabel('Z');
    title(sprintf('Pattern %d (Scale: %.1f)', ii, scales(ii)));
    axis_limit = max_mag * 1.3;
    xlim([-axis_limit axis_limit]);
    ylim([-axis_limit axis_limit]);
    zlim([-axis_limit axis_limit]);
    lighting gouraud;
    camlight('headlight');
    material dull;
  end
title(tiled_plot, 'Spherical 3D Radiation Patterns with Uniform 
Magnitude Scaling');

Results: please see attached.

So, I executed the code and it worked because of r = magEl By using magnitude directly as the radius:

  • The 0.5-scale pattern is physically half the size (extends to ±0.5 instead of ±1)
  • With uniform caxis([0 max_mag]), it uses middle-range colors (blues/cyans/greens) instead of the full spectrum

This solves exactly what you showed in your image - where both spheres were the same size but shouldn't have been. Now the right plot will be visibly smaller and use middle colors like you wanted.

No toolbox needed, just standard MATLAB functions.

Hope this finally works for you!

Scott
Scott 1 minute ago
Hi Umar,
This is a great solution. I wanted some more angular feedback so I added a few other decorations to the plot, but I've started integrating this approach into my application. I also like that it doesn't require toolboxes, so I can share it with colleagues without having to worry about whether they have the antenna toolbox too. Thanks for your help! How do I mark this comment as an accepted answer? Can I give credit to both you and @dpb since you both came up with great solutions?

Sign in to comment.


dpb
dpb on 15 Oct 2025 at 19:53
Moved: dpb on 15 Oct 2025 at 19:53
%helixdata = readmatrix("antennadata_test.csv");
%hL=patternCustom(helixdata(:,3),helixdata(:,2),helixdata(:,1));
h = helix;
[D,az,el] = pattern(h,2e9);
phi = az';
theta = (90-el);
MagE = D';
tl=tiledlayout(2,2);
iax=1;
hAx(iax)=nexttile;
hL=patternCustom(MagE, theta, phi,CoordinateSystem="rectangular",Slice="phi", SliceValue=0);
[hAx(iax).XLim hAx(iax).YLim hAx(iax).ZLim]
ans = 1×6
0 200 -15 10 -1 1
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
iax=iax+1;
hAx(iax)=nexttile;
L=patternCustom(MagE/10, theta, phi,CoordinateSystem="rectangular",Slice="phi", SliceValue=0);
[hAx(iax).XLim hAx(iax).YLim hAx(iax).ZLim]
ans = 1×6
0 200.0000 -1.5000 1.0000 -1.0000 1.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
ylim(hAx(iax),ylim(hAx(iax-1)))
Takes a stab at it from one of the examples using rectangular coordinates.
You didn't provide anything to go on about your specific case, but the above should give the idea -- save the tiled axes handles and adjust the plot axes limits to match what you want.
  4 Comments
dpb
dpb on 16 Oct 2025 at 18:41
Edited: dpb on 16 Oct 2025 at 23:20
I don't have time to actually poke at it at the moment, but you'll have to map the colormap to match the same magnitude as the largest and set it for each as I noted before. The size appears to always be scaled to fit the axes by default.
Truthfully, @Umar's idea of drawing directly with primitives and then wrapping that into your own function may be easier than trying to beat the special graphics object into submission in ways it just wasn't designed for.
I'll try to get back again when I do have a little time to poke at it...
Umar
Umar on 17 Oct 2025 at 5:03

@dpb - You nailed it with the colormap mapping insight. Turns out `clim()` does the trick for forcing uniform color limits across the 3D spherical plots, and `PatternPlotOptions` with `MagnitudeScale` also works as an alternative. Appreciate you pointing in the right direction - saved @Scott from having to rebuild with primitives!

Sign in to comment.

Products


Release

R2020a

Community Treasure Hunt

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

Start Hunting!