Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace quiver arrowheads with images

I have a circular lattice and on the lattice sites I plot normalized arrows that remain in the same magnitude and change direction according to a simulation, the details of which don't matter.

My plots look like this

enter image description here

Is it possible to replace the arrow in the quiver plot by an jpg/bmp/gif/png image? or by any other command?

Ideally, it would look something like this (although not necessarily arrows)

enter image description here

like image 562
jarhead Avatar asked Mar 17 '16 17:03

jarhead


1 Answers

Explanation

One way that you can do this, would be to use a surface object with a texture-map as the FaceColor.

In MATLAB, you can create a simple rectangular surface. You can set the FaceColor to be texturemap which will cause the value assigned to CData to be mapped across the surface.

Then to get transparency, you can also set the FaceAlpha value to be texturemap and set the AlphaData and those transparency values will be mapped across the extent of the surface as well.

For this to be applied to your case, you want to set the CData to the image that you want to use to replace your arrows. And you will want the AlphaData to be the same size as your image data with values of 1 where you want it to be opaque and 0 where you want it to be transparent. This will allow it to not look like the image that you have posted where you can clearly see the bounding box. Then you will need to draw one of these surfaces where each of the arrows would go and scale/position it appropriately.

Implementation

Update: A more polished version of this code (ImageQuiver) is now available on Github as well as the MATLAB File Exchange.

As a demonstration of what I'm talking about, I have created the following function which essentially does just this. It accepts the same inputs as quiver (with the image data being supplied first and an optional AlphaData parameter at the end) and creates a surface at all of the requested coordinates pointing in the requested direction, and scaled by the specified amount.

function h = quiverpic(im, X, Y, dX, dY, scale, alpha)
    % im - RGB or indexed image
    % X - X positions
    % Y - Y positions
    % dX - X direction vector
    % dY - Y direction vector
    % scale - Any scaling (Default = 1)
    % alpha - Transparency (same size as im), if not specified = ~isnan(im)

    h = hggroup();

    if ~exist('scale', 'var')
        % By default there is no scaling
        scale = 1;
    end

    if ~exist('alpha', 'var')
        % By default, any NaN will be transparent
        alpha = ~isnan(im);
    end

    % Determine aspect ratio of the source image
    width_to_height = size(im, 2) / size(im, 1);

    for k = 1:numel(X)
        % Determine angle from displacement vectors
        theta = atan2(dY(k), dX(k));

        % Subtract pi/2 to +y is considered "up"
        theta = theta + pi/2;

        % Setup surface plot boundary
        [xx,yy] = meshgrid([-0.5, 0.5] * width_to_height, [0 1]);

        % Scale depending on magnitude of dX and dY
        this_scale = scale * sqrt(dX(k).^2 + dY(k).^2);

        % Scale X and Y components prior to rotating
        xx = xx .* this_scale;
        yy = yy .* this_scale;

        % Rotate to align with the desired direction
        xdata = xx .* cos(theta) - yy .* sin(theta);
        ydata = xx .* sin(theta) + yy .* cos(theta);

        % Determine what is considered the "anchor" of the graphic.
        % For now this is assumed to be the "bottom-middle"
        xoffset = X(k) - mean(xdata(2,:));
        yoffset = Y(k) - mean(ydata(2,:));

        % Actually plot the surface.
        surf(xdata + xoffset, ...
             ydata  + yoffset, zeros(2), ...
             'Parent', h, ...
             'FaceColor', 'texture', ...
             'EdgeColor', 'none', ...
             'CData', im, ...
             'FaceAlpha', 'texture', ...
             'AlphaData', double(alpha));
    end
end

Example

I wrote a little test script to show how this can be used and to show the results.

t = linspace(0, 2*pi, 13);
dX = cos(t(1:end-1));
dY = sin(t(1:end-1));
X = (3 * dX) + 5;
Y = (3 * dY) + 5;
scale = 1;

% Load the MATLAB logo as an example image
png = fullfile(matlabroot,'/toolbox/matlab/icons/matlabicon.gif');
[im, map] = imread(png);
im = ind2rgb(im, map);

% Determine alpha channel based on upper left hand corner pixel
flatim = reshape(im, [], 3);
alpha = ~ismember(flatim, squeeze(im(1,1,:)).', 'rows');
alpha = reshape(alpha, size(im(:,:,1)));

% Plot some things prior to creating the quiverpic object
fig = figure();
hax = axes('Parent', fig);
axis(hax, 'equal');

% Plot a full circle
t = linspace(0, 2*pi, 100);
plot((cos(t) * 3) + 5, (sin(t) * 3) + 5, '-')

hold(hax, 'on')

% Plot markers at all the quiver centers
plot(X, Y, 'o', 'MarkerFaceColor', 'w')

% Plot a random image behind everything to demonstrate transparency
him = imagesc(rand(9));
uistack(him, 'bottom')

axis(hax, 'equal')
colormap(fig, 'gray')
set(hax, 'clim', [-4 4]);

% Now plot the quiverpic
h = quiverpic(im, X, Y, dX, dY, 1, alpha);

axis(hax, 'tight')

Results

enter image description here

Absurdity

Same image with varying vectors and scaling

enter image description here

Any image of any aspect ratio will work just fine

enter image description here

like image 109
Suever Avatar answered Nov 15 '22 22:11

Suever