Search code examples
matlabmatlab-figure

Matlab quiver arrow length


I am working on the following code:

function myquiver

clc
R=2.3;
C=1.2;
V=@(t) 3*sqw(t,1,10);
F=@(t,VC) (V(t)-VC)/(R*C);
[T,VC]=meshgrid(linspace(0,25,10),linspace(0,0.5,10));
S=(3*sqw(T,1,10)-VC)/(R*C);
L=sqrt(1+S.^2);
quiver(T,VC,1./L,S./L,'r','ShowArrowhead','off','AutoScale','off')
axis([0,25,0,0.5])
shg

function y=sqw(t,T,d)
r=mod(t,T);
y=(r<d*T/100);

This produces the following image:

enter image description here

Now, what I want to do is make each of the quiver arrows the same length based on the axes. I don't want to change axis([0,25,0.05]). That is exactly what I want. But how can I scale each quiver arrow so that they are all the same length in this particular image?

I am hoping to see an idea that I can use should I do another plot with say axis([0,10,-30,50]).

I'm hoping to see a full code example and an image.

Thanks.


Solution

  • Let's consider two coordinate systems. First, the world coordinate system, which the data are in. For example 1./L and S./L are world coordinate representations. Second, the plot coordinate system which we define such that one unit of distance is the width and height of the plot window. I.e. a horizontal vector with unit length in plot coordinates would exactly span the entire plot width, and similarly a vertical vector with unit length in plot coordinates would exactly span the entire plot height.

    What you appear to be asking is how to scale each vector so that they are all some fixed length in plot coordinates. It's not difficult to see that for some vector defined in world coordinates (x_world, y_world) the following functions transform the world coordinate to plot coordinates.

    x_plot = x_world / x_span
    y_plot = y_world / y_span
    

    Where x_span and y_span are the width and height of the plot in world coordinates (in your case 25 and 0.5 respectively).

    We would like to know what scaling factor to apply to x_world and y_world so that (x_plot, y_plot) is some fixed length. Observe that we must apply the same scaling factor in x and y to retain the original direction. Mathematically this can be described by the equation

    d = sqrt((s*x_world / x_span)^2 + (s*y_plot / y_span)^2)
    

    where d is the desired length in plot coordinates and s is the scale factor we need to determine.

    Solving the equation for s we get

    s = d/sqrt((x_world / x_span)^2 + (y_plot / y_span)^2)
    

    which gives us a solution.

    function myquiver
        xmin = 0; xmax = 25;
        ymin = 0; ymax = 0.5;
    
        R=2.3;
        C=1.2;
        V=@(t) 3*sqw(t,1,10);
        F=@(t,VC) (V(t)-VC)/(R*C);
        [T,VC]=meshgrid(linspace(xmin,xmax,10),linspace(ymin,ymax,10));
        S=(3*sqw(T,1,10)-VC)/(R*C);
        L=sqrt(1+S.^2);
    
        x_span = xmax - xmin;
        y_span = ymax - ymin;
        % Compute the scale factor so that vectors are 10% of plot window
        d = 0.1;
        X_world = 1./L;
        Y_world = S./L;
        s = d ./ sqrt((X_world / x_span).^2 + (Y_world / y_span).^2);
        quiver(T,VC,s.*X_world,s.*Y_world,'r','ShowArrowhead','off','AutoScale','off')
        axis([xmin,xmax,ymin,ymax])
    
        % force the axis to be square
        axis('square');
    
    function y=sqw(t,T,d)
        r=mod(t,T);
        y=(r<d*T/100);
    

    enter image description here


    One thing to note here is that we used axis('square'). This was necessary to ensure an equal scaling relationship between plot coordinates and screen/pixel coordinates. Since this may not be desirable (maybe we don't want a square plot!), then we can fix this by dividing x_span by the aspect ratio of the axis (in pixels). Unfortunately, we can't get the aspect ratio before the plot is created. In order to implement the generalized solution we basically have two options.

    1. Write a function handler to automatically re-plot each time the figure is resized. This is more work but generalized.
    2. Play with the figure until it is the desired size, then query the size, then use this information to re-plot for the desired figure size.

    I chose to pursue option two. First I plotted without axis('square'), then played with the window until it was about the same size as the original figure that you posted. Next, I queried the position of the figure and aspect ratio as follows

    >> get(gcf, 'Position')
    ans = 666   223   672   505
    >> set(gca, 'Units', 'Pixels');
    >> get(gca, 'Position')
    ans = 88.3600   56.5500  520.8000  411.5750
    

    This tells us that for this figure position the aspect ratio of the axis in pixels is 520.8 / 411.575. Using this knowledge, we modify the function to plot equal length vectors for this non-square axis as follows.

    function myquiver
        xmin = 0; xmax = 25;
        ymin = 0; ymax = 0.5;
    
        R=2.3;
        C=1.2;
        V=@(t) 3*sqw(t,1,10);
        F=@(t,VC) (V(t)-VC)/(R*C);
        [T,VC]=meshgrid(linspace(xmin,xmax,10),linspace(ymin,ymax,10));
        S=(3*sqw(T,1,10)-VC)/(R*C);
        L=sqrt(1+S.^2);
    
        aspect = 520.8 / 411.575;
        x_span = (xmax - xmin) / aspect;
        y_span = ymax - ymin;
        % Compute the scale factor so that vectors are 10% of y-direction of plot window
        d = 0.1;
        X_world = 1./L;
        Y_world = S./L;
        s = d ./ sqrt((X_world / x_span).^2 + (Y_world / y_span).^2);
        quiver(T,VC,s.*X_world,s.*Y_world,'r','ShowArrowhead','off','AutoScale','off')
        axis([xmin,xmax,ymin,ymax])
    
        % Make the figure have the desired position/size
        set(gcf, 'Position', [666   223   672   505]);
    
    function y=sqw(t,T,d)
        r=mod(t,T);
        y=(r<d*T/100);
    

    enter image description here