Replace quiver arrowheads with images

后端 未结 1 1318
既然无缘
既然无缘 2021-02-06 02:27

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 whic

相关标签:
1条回答
  • 2021-02-06 02:53

    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

    Absurdity

    Same image with varying vectors and scaling

    Any image of any aspect ratio will work just fine

    0 讨论(0)
提交回复
热议问题