How do I draw an ellipse and an ellipsoid using MATLAB?
(x^2/a^2)+(y^2/b^2)=1
n=40;
a=0; b=2*pi;
c=0; d=2*pi;
for i=1:n
u=a+(b-a)*(i-1)/(n-1);
for j=1:m
v=a+(d-c)*(j-1)/(m-1);
x(i,j)=sin(u)*cos(v);
y(i,j)=sin(u)*sin(v);
z(i,j)=cos(u);
end
end
mesh(x,y,z);
But I want the shape?
Ellipse article on Wikipedia had a simple JavaScript code to draw ellipses.
It uses the parametric form:
x(theta) = a0 + ax*sin(theta) + bx*cos(theta)
y(theta) = b0 + ay*sin(theta) + by*cos(theta)
where
(a0,b0) is the center of the ellipse
(ax,ay) vector representing the major axis
(bx,by) vector representing the minor axis
I translated the code into a MATLAB function:
calculateEllipse.m
function [X,Y] = calculateEllipse(x, y, a, b, angle, steps)
%# This functions returns points to draw an ellipse
%#
%# #param x X coordinate
%# #param y Y coordinate
%# #param a Semimajor axis
%# #param b Semiminor axis
%# #param angle Angle of the ellipse (in degrees)
%#
narginchk(5, 6);
if nargin<6, steps = 36; end
beta = -angle * (pi / 180);
sinbeta = sin(beta);
cosbeta = cos(beta);
alpha = linspace(0, 360, steps)' .* (pi / 180);
sinalpha = sin(alpha);
cosalpha = cos(alpha);
X = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
Y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
if nargout==1, X = [X Y]; end
end
and an example to test it:
%# ellipse centered at (0,0) with axes length
%# major=20, ,minor=10, rotated 50 degrees
%# (drawn using the default N=36 points)
p = calculateEllipse(0, 0, 20, 10, 50);
plot(p(:,1), p(:,2), '.-'), axis equal
The answers from Jacob and Amro are very good examples for computing and plotting points for an ellipse. I'll address some easy ways you can plot an ellipsoid...
First, MATLAB has a built-in function ELLIPSOID which generates a set of mesh points given the ellipsoid center and the semi-axis lengths. The following creates the matrices x, y, and z for an ellipsoid centered at the origin with semi-axis lengths of 4, 2, and 1 for the x, y, and z directions, respectively:
[x, y, z] = ellipsoid(0, 0, 0, 4, 2, 1);
You can then use the function MESH to plot it, returning a handle to the plotted surface object:
hMesh = mesh(x, y, z);
If you want to rotate the plotted ellipsoid, you can use the ROTATE function. The following applies a rotation of 45 degrees around the y-axis:
rotate(hMesh, [0 1 0], 45);
You can then adjust the plot appearance to get the following figure:
axis equal; %# Make tick mark increments on all axes equal
view([-36 18]); %# Change the camera viewpoint
xlabel('x');
ylabel('y');
zlabel('z');
Also, if you want to use the rotated plot points for further calculations, you can get them from the plotted surface object:
xNew = get(hMesh, 'XData'); %# Get the rotated x points
yNew = get(hMesh, 'YData'); %# Get the rotated y points
zNew = get(hMesh, 'ZData'); %# Get the rotated z points
I've adapted this excellent ellipse plotting script from MATLAB Central for your requirement of
function plotEllipse(a,b,C)
% range to plot over
%------------------------------------
N = 50;
theta = 0:1/N:2*pi+1/N;
% Parametric equation of the ellipse
%----------------------------------------
state(1,:) = a*cos(theta);
state(2,:) = b*sin(theta);
% Coordinate transform (since your ellipse is axis aligned)
%----------------------------------------
X = state;
X(1,:) = X(1,:) + C(1);
X(2,:) = X(2,:) + C(2);
% Plot
%----------------------------------------
plot(X(1,:),X(2,:));
hold on;
plot(C(1),C(2),'r*');
axis equal;
grid;
end
Note: change N to define the resolution of your ellipse
Here's an ellipse centered at (10,10) with a = 30 and b = 10
Ellipse article on Wikipedia and Rotation matrix.
Rewrite that functions:
rotate by rotAngle counter-clockwise around (0,0)
Coordinate transform to (cx, cy)
function [X,Y] = calculateEllipse(cx, cy, a, b, rotAngle)
%# This functions returns points to draw an ellipse
%#
%# #param x X coordinate
%# #param y Y coordinate
%# #param a Semimajor axis
%# #param b Semiminor axis
%# #param cx cetner x position
%# #param cy cetner y position
%# #param angle Angle of the ellipse (in degrees)
%#
steps = 30;
angle = linspace(0, 2*pi, steps);
% Parametric equation of the ellipse
X = a * cos(angle);
Y = b * sin(angle);
% rotate by rotAngle counter clockwise around (0,0)
xRot = X*cosd(rotAngle) - Y*sind(rotAngle);
yRot = X*sind(rotAngle) + Y*cosd(rotAngle);
X = xRot;
Y = yRot;
% Coordinate transform
X = X + cx;
Y = Y + cy;
end
and an example to test it:
[X,Y] = calculateEllipse(0, 0, 20, 10, 0);
plot(X, Y, 'b'); hold on; % blue
[X,Y] = calculateEllipse(0, 0, 20, 10, 45);
plot(X, Y, 'r'); hold on; % red
[X,Y] = calculateEllipse(30, 30, 20, 10, 135);
plot(X, Y, 'g'); % green
grid on;
Create two vectors, one of the x-coordinates of the points of the circumference of the ellipsoid, one of the y-coordinates. Make these vectors long enough to satisfy your accuracy requirements. Plot the two vectors as (x,y) pairs joined up. I'd drop the for loops from your code, much clearer if you use vector notation. Also I'd format your question using the SO markup for code to make it all clearer to your audience.
The simplest way might be to use the Matlab function
pdeellip(xc,yc,a,b,phi)
For example:
pdeellip(0,0,1,0.3,pi/4)
However, this is simple solution is good to have a quick glance how the ellipse looks like. If you want to have a nice plot, look at the other solutions.
I don't know in which version of Matlab this was added, but it's available at least from version R2012b on.
Related
I am trying to draw a cone, connected to the sphere in Matlab. I have the point [x1,y1,z1] outside of the sphere [x2,y2,z2] with R radius and I want it to be the top of the cone, created out of tangents.
On this pictures you can see what I have in mind:
Below you can see what I have already done. I am using it in order to mark the part of the Earth's surface, visible from the satellite position in orbit. Unfortunately, the cone in this picture is approximate, I need to create accurate one, connected with surface. For now, it is not only inaccurate, but also goes under it.
I am creating the sphere with this simple code (I am skipping the part of putting the map on it, it's just an image):
r = 6371.0087714;
[X,Y,Z] = sphere(50);
X2 = X * r;
Y2 = Y * r;
Z2 = Z * r;
surf(X2,Y2,Z2)
props.FaceColor= 'texture';
props.EdgeColor = 'none';
props.FaceLighting = 'phong';
figure();
globe = surface(X2,Y2,Z2,props);
Let's assume that I have the single point in 3D:
plot3(0,0,7000,'o');
How can I create such a cone?
There are two different questions here:
How to calculate cone dimensions?
How to plot lateral faces of a 3D cone?
Calculating Cone Dimensions
Assuming that center of sphere is located on [0 0 0]:
d = sqrt(Ax^2+Ay^2+Az^2);
l = sqrt(d^2-rs^2);
t = asin(rs/d);
h = l * cos(t);
rc = l * sin(t);
Plotting the Cone
The following function returns coordinates of lateral faces of cone with give apex point, axis direction, base radius and height, and the number of lateral faces.
function [X, Y, Z] = cone3(A, V, r, h, n)
% A: apex, [x y z]
% V: axis direction, [x y z]
% r: radius, scalar
% h: height, scalar
% n: number of lateral surfaces, integer
% X, Y, Z: coordinates of lateral points of the cone, all (n+1) by 2. You draw the sphere with surf(X,Y,Z) or mesh(X,Y,Z)
v1 = V./norm(V);
B = h*v1+A;
v23 = null(v1);
th = linspace(0, 2*pi, n+1);
P = r*(v23(:,1)*cos(th)+v23(:,2)*sin(th));
P = bsxfun(#plus, P', B);
zr = zeros(n+1, 1);
X = [A(1)+zr P(:, 1)];
Y = [A(2)+zr P(:, 2)];
Z = [A(3)+zr P(:, 3)];
end
The Results
rs = 6371.0087714; % globe radius
A = rs * 2 * [1 1 1]; % sattelite location
V = -A; % vector from sat to the globe center
% calculating cone dimensions
d = norm(A); % distance from cone apex to sphere center
l = (d^2-rs^2)^.5; % length of generating line of cone
sint = rs/d; % sine of half of apperture
cost = l/d; % cosine of half of apperture
h = l * cost; % cone height
rc = l * sint; % cone radius
% globe surface points
[XS,YS,ZS] = sphere(32);
% cone surface points
[XC, YC, ZC] = cone3(A, V, rc, h, 32);
% plotting results
hold on
surf(XS*rs,YS*rs,ZS*rs, 'facecolor', 'b', 'facealpha', 0.5, 'edgealpha', 0.5)
surf(XC, YC, ZC, 'facecolor', 'r', 'facealpha', 0.5, 'edgealpha', 0.5);
axis equal
grid on
Animating the satellite
The simplest way to animate objects is to clear the whole figure by clf and plot objects again in new positions. But a way more efficient method is to plot all objects once and in each frame, only update positioning data of moving objects:
clc; close all; clc
rs = 6371.0087714; % globe radius
r = rs * 1.2;
n = 121;
t = linspace(0, 2*pi, n)';
% point on orbit
Ai = [r.*cos(t) r.*sin(t) zeros(n, 1)];
[XS,YS,ZS] = sphere(32);
surf(XS*rs,YS*rs,ZS*rs, 'facecolor', 'b', 'facealpha', 0.5, 'edgealpha', 0.5)
hold on
[XC, YC, ZC] = cone3(Ai(1, :), Ai(1, :), 1, 1, 32);
% plot a cone and store handel of surf object
hS = surf(XC, YC, ZC, 'facecolor', 'r', 'facealpha', 0.5, 'edgealpha', 0.5);
for i=1:n
% calculating new point coordinates of cone
A = Ai(i, :);
V = -A;
d = norm(A);
l = (d^2-rs^2)^.5;
sint = rs/d;
cost = l/d;
h = l * cost;
rc = l * sint;
[XC, YC, ZC] = cone3(A, V, rc, h, 32);
% updating surf object
set(hS, 'xdata', XC, 'ydata', YC, 'zdata', ZC);
pause(0.01); % wait 0.01 seconds
drawnow(); % repaint figure
end
Another sample with 3 orbiting satellites:
I have this piece of MATLAB code that outputs x,y, and z angles and I would like draw a line using them. Can someone point me in the right direction on how to do this?
C = pi;
A = pi;
B = pi;
Z = [cos(C),-sin(C),0; sin(C),cos(C),0; 0,0,1];
X = [1,0,0;0,cos(A),-sin(A);0,sin(A),cos(A)];
Y = [cos(B),0,sin(B);0,1,0;-sin(B),0,cos(B)];
R =(X*Y)*Z;
yaw=atan2(R(2,1),R(1,1))
pitch=atan2(-R(3,1),sqrt(R(3,2)^2+R(3,3)^2))
roll=atan2(R(3,2),R(3,3))
X, Y, and Z are not angles, they are rotation matrices defined by the angles A, B, and C.
it's not clear what's the meaning of "draw a line using them", they are just used to rotate vectors in the 3D space.
here is an example of drawing a rotated vector with them:
% define rotation angles (around the axes)
C = pi/2;
A = pi/4;
B = pi/4;
% generate rotation matrices
Z = [cos(C),-sin(C),0; sin(C),cos(C),0; 0,0,1];
X = [1,0,0;0,cos(A),-sin(A);0,sin(A),cos(A)];
Y = [cos(B),0,sin(B);0,1,0;-sin(B),0,cos(B)];
R =(X*Y)*Z;
% generate a vector and rotate it
v = [1;1;1];
u = R*v;
% plot
quiver3(0,0,0,v(1),v(2),v(3));
hold on
quiver3(0,0,0,u(1),u(2),u(3));
xlim([-1 1]); ylim([-1 1]); zlim([-1 1])
axis square
legend('original','rotated')
I have polar coordinates, radius 0.05 <= r <= 1 and 0 ≤ θ ≤ 2π. The radius r is 50 values between 0.05 to 1, and polar angle θ is 24 values between 0 to 2π.
How do I interpolate r = 0.075 and theta = pi/8?
I dunno what you have tried, but interp2 works just as well on polar data as it does on Cartesian. Here is some evidence:
% Coordinates
r = linspace(0.05, 1, 50);
t = linspace(0, 2*pi, 24);
% Some synthetic data
z = sort(rand(50, 24));
% Values of interest
ri = 0.075;
ti = pi/8;
% Manually interpolate
rp = find(ri <= r, 1, 'first');
rm = find(ri >= r, 1, 'last');
tp = find(ti <= t, 1, 'first');
tm = find(ti >= t, 1, 'last');
drdt = (r(rp) - r(rm)) * (t(tp) - t(tm));
dr = [r(rp)-ri ri-r(rm)];
dt = [t(tp)-ti ti-t(tm)];
fZ = [z(rm, tm) z(rm, tp)
z(rp, tm) z(rp, tp)];
ZI_manual = (dr * fZ * dt.') / drdt
% Interpolate with MATLAB
ZI_MATLAB = interp2(r, t, z', ri, ti, 'linear')
Result:
ZI_manual =
2.737907208525297e-002
ZI_MATLAB =
2.737907208525298e-002
Based on comments you have the following information
%the test point
ri=0.53224;
ti = pi/8;
%formula fo generation of Z
g=9.81
z0=#(r)0.01*(g^2)*((2*pi)^-4)*(r.^-5).*exp(-1.25*(r/0.3).^-4);
D=#(t)(2/pi)*cos(t).^2;
z2=#(r,t)z0(r).*D(t) ;
%range of vlaues of r and theta
r=[0.05,0.071175,0.10132,0.14422,0.2053, 0.29225,0.41602,0.5922,0.84299,1.2];
t=[0,0.62832,1.2566,1.885, 2.5133,3.1416,3.7699,4.3982,5.0265,5.6549,6.2832];
and you want interplation of the test point.
When you sample some data to use them for interpolation you should consider how to sample data according to your requirements.
So when you are sampling a regular grid of polar coordinates ,those coordinates when converted to rectangular will form a circular shape that
most of the points are concentrated in the center of the cricle and when we move from the center to outer regions distance between the points increased.
%regular grid generated for r and t
[THETA R] = meshgrid(t ,r);
% Z for polar grid
Z=z2(R,THETA);
%convert coordinate from polar to cartesian(rectangular):
[X, Y] = pol2cart (THETA, R);
%plot points
plot(X, Y, 'k.');
axis equal
So when you use those point for interpolation the accuracy of the interpolation is greater in the center and lower in the outer regions where the distance between points increased.
In the other word with this sampling method you place more importance on the center region related to outer ones.
To increase accuracy density of grid points (r and theta) should be increased so if length of r and theta is 11 you can create r and theta with size 20 to increase accuracy.
In the other hand if you create a regular grid in rectangular coordinates an equal importance is given to each region . So accuracy of the interpolation will be the same in all regions.
For it first you create a regular grid in the polar coordinates then convert the grid to rectangular coordinates so you can calculate the extents (min max) of the sampling points in the rectangular coordinates. Based on this extents you can create a regular grid in the rectangular coordinates
Regular grid of rectangular coordinates then converted to polar coordinated to get z for grid points using z2 formula.
%get the extent of points
extentX = [min(X(:)) max(X(:))];
extentY = [min(Y(:)) max(Y(:))];
%sample 100 points(or more or less) inside a region specified be the extents
X_samples = linspace(extentX(1),extentX(2),100);
Y_samples = linspace(extentY(1),extentY(2),100);
%create regular grid in rectangular coordinates
[XX YY] = meshgrid(X_samples, Y_samples);
[TT RR] = cart2pol(XX,YY);
Z_rect = z2(RR,TT);
For interpolation of a test point say [ri ti] first it converted to rectangular then using XX ,YY value of z is interpolated
[xi yi] = pol2cart (ti, ri);
z=interp2(XX,YY,Z_rect,xi,yi);
If you have no choice to change how you sample the data and only have a grid of polar points as discussed with #RodyOldenhuis you can do the following:
Interpolate polar coordinates with interp2 (interpolation for gridded data)
this approach is straightforward but has the shortcoming that r and theta are not of the same scale and this may affect the accuracy of the interpolation.
z = interp2(THETA, R, Z, ti, ri)
convert polar coordinates to rectangular and then apply an interpolation method that is for scattered data.
this approach requires more computations but result of it is more reliable.
MATLAB has griddata function that given scattered points first generates a triangulation of points and then creates a regular grid on top of the triangles and interpolates values of grid points.
So if you want to interpolate value of point [ri ti] you should then apply a second interpolation to get value of the point from the interpolated grid.
With the help of some information from spatialanalysisonline and Wikipedia linear interpolation based on triangulation calculated this way (tested in Octave. In newer versions of MATLAB use of triangulation and pointLocation recommended instead of delaunay and tsearch ):
ri=0.53224;
ti = pi/8;
[THETA R] = meshgrid(t ,r);
[X, Y] = pol2cart (THETA, R);
[xi yi] = pol2cart (ti, ri);
%generate triangulation
tri = delaunay (X, Y);
%find the triangle that contains the test point
idx = tsearch (X, Y, tri, xi, yi);
pts= tri(idx,:);
%create a matrix that repesents equation of a plane (triangle) given its 3 points
m=[X(pts);Y(pts);Z(pts);ones(1,3)].';
%calculate z based on det(m)=0;
z= (-xi*det(m(:,2:end)) + yi*det([m(:,1) m(:,3:end)]) + det(m(:,1:end-1)))/det([m(:,1:2) m(:,end)]);
More refinement:
Since it is known that the search point is surrounded by 4 points we can use only those point for triangulation. these points form a trapezoid. Each diagonal of trapezoid forms two triangles so using vertices of the trapezoid we can form 4 triangles, also a point inside a trapezoid can lie in at least 2 triangles.
the previous method based on triangulation only uses information from one triangle but here z of the test point can be interpolated two times from data of two triangles and the calculated z values can be averaged to get a better approximation.
%find 4 points surrounding the test point
ft= find(t<=ti,1,'last');
fr= find(cos(abs(diff(t(ft+(0:1))))/2) .* r < ri,1,'last');
[T4 R4] = meshgrid(t(ft+(0:1)), r(fr+(0:1)));
[X4, Y4] = pol2cart (T4, R4);
Z4 = Z(fr+(0:1),ft+(0:1));
%form 4 triangles
tri2= nchoosek(1:4,3);
%empty vector of z values that will be interpolated from 4 triangles
zv = NaN(4,1);
for h = 1:4
pts = tri2(h,:);
% test if the point lies in the triangle
if ~isnan(tsearch(X4(:),Y4(:),pts,xi,yi))
m=[X4(pts) ;Y4(pts) ;Z4(pts); [1 1 1]].';
zv(h)= (-xi*det(m(:,2:end)) + yi*det([m(:,1) m(:,3:end)]) + det(m(:,1:end-1)))/det([m(:,1:2) m(:,end)]);
end
end
z= mean(zv(~isnan(zv)))
Result:
True z:
(0.0069246)
Linear Interpolation of (Gridded) Polar Coordinates :
(0.0085741)
Linear Interpolation with Triangulation of Rectangular Coordinates:
(0.0073774 or 0.0060992) based on triangulation
Linear Interpolation with Triangulation of Rectangular Coordinates(average):
(0.0067383)
Conclusion:
Result of interpolation related to structure of original data and the sampling method. If the sampling method matches pattern of original data result of interpolation is more accurate, so in cases that grid points of polar coordinates follow pattern of data result of interpolation of regular polar coordinate can be more reliable. But if regular polar coordinates do not match the structure of data or structure of data is such as an irregular terrain, method of interpolation based on triangulation can better represent the data.
please check this example, i used two for loops, inside for loop i used condition statement, if u comment this condition statement and run the program, u'll get correct answer, after u uncomment this condition statement and run the program, u'll get wrong answer. please check it.
% Coordinates
r = linspace(0.05, 1, 10);
t = linspace(0, 2*pi, 8);
% Some synthetic data
%z = sort(rand(50, 24));
z=zeros();
for i=1:10
for j=1:8
if r(i)<0.5||r(i)>1
z(i,j)=0;
else
z(i,j) = r(i).^3'*cos(t(j)/2);
end
end
end
% Values of interest
ri = 0.55;
ti = pi/8;
% Manually interpolate
rp = find(ri <= r, 1, 'first');
rm = find(ri >= r, 1, 'last');
tp = find(ti <= t, 1, 'first');
tm = find(ti >= t, 1, 'last');
drdt = (r(rp) - r(rm)) * (t(tp) - t(tm));
dr = [r(rp)-ri ri-r(rm)];
dt = [t(tp)-ti ti-t(tm)];
fZ = [z(rm, tm) z(rm, tp)
z(rp, tm) z(rp, tp)];
ZI_manual = (dr * fZ * dt.') / drdt
% Interpolate with MATLAB
ZI_MATLAB = interp2(r, t, z', ri, ti, 'linear')
Result:
z1 =
0.1632
ZI_manual =
0.1543
ZI_MATLAB =
0.1582
I have line, or trajectory in 3D space. I also have a 2D shape. I want to take this shape and move it along the curve keeping the surface normal parallel to the tangent of the curve. Based on a post here I have successfully done this that produces something which 'looks right' for shapes with rotational symmetry like a circle. See the code and figure below for the example.
% create data
npts = 30;
tend = 8*pi;
t = linspace(0,tend,npts)';
z = linspace(-1,1,npts)';
omz = sqrt(1-z.^2);
x = cos(t).*omz;
y = sin(t).*omz;
scatter3 (x,y,z, 'x');
hold on
% fit 3 slms to data in each direction
xslm = slmengine (t, x, 'knots', ceil(npts/1.5));
yslm = slmengine (t, y, 'knots', ceil(npts/1.5));
zslm = slmengine (t, z, 'knots', ceil(npts/1.5));
% test points
tq = linspace(0,tend,200 * npts)';
dx = slmeval (t, xslm, 1, false);
dy = slmeval (t, yslm, 1, false);
dz = slmeval (t, zslm, 1, false);
quiver3(x,y,z,dx,dy,dz);
plot3 (slmeval(tq, xslm, 0, false), slmeval(tq, yslm, 0, false), slmeval(tq, zslm, 0, false));
hold off
axis equal
% The following taken from post on matlab central
%
% http://uk.mathworks.com/matlabcentral/newsreader/view_thread/159522
%
% P10 = P1-P0;
%
% P20 = P2-P0;
% N = dot(P10,P10)*P20-dot(P20,P20)*P10; % <-- Approx. tangent direction
R = 0.05;
P0 = [x,y,z];
N = [dx, dy, dz];
% circle points
% theta = linspace(0,2*pi).';
box_x = [ -R; R; R; -R; -R ];
box_y = [ -R/2; -R; R; R/2; -R/2 ];
for ind = 1:size(P0,1)
T = null(N(ind,:)).'; % Get two orthogonal unit vectors which are orthog. to N
% V = bsxfun ( #plus, ...
% R * ( cos(theta) * T(1,:) + sin(theta) * T(2,:) ), ...
% P0(ind,:) );
V = bsxfun ( #plus, ...
box_x * T(1,:) + box_y * T(2,:), ...
P0(ind,:) );
hold on
quiver3(P0(ind,1),P0(ind,2),P0(ind,3),T(1,1),T(1,2),T(1,3), 0.5, 'b');
quiver3(P0(ind,1),P0(ind,2),P0(ind,3),T(2,1),T(2,2),T(2,3), 0.5, 'b');
plot3(V(:,1),V(:,2),V(:,3));
hold off
end
Note that this code makes use of the Shape Language Modelling functions from the Matlab file exchange to make the curve and get it's tangent at various points, but this isn't crucial to the problem.
However, as you can see, the rotation of the shape flips as you move around the curve with this method. I need to keep the rotation of the shape consistent as I actually wish to sample values from scattered 3D data on the shape surface which represents the inside of a 'tube' or whatever the shape is.
So how can I control the orientation of my shape as I move along the curve?
You need 2 vectors and position! The tangent (you have it) and one vector to align to like up which must be not parallel to tangent. Then just construct 3D transform matrix use tangent as Z-axis and exploit the cross product to get the other two for example:
Up = (0,1,0)
Z = tangent/|tangent|
X = cross(Z,Up)
Y = cross(X,Z)
O = position
Position is just the actual position at the trajectory +/- shape offset. You can adjust the sign of the X,Y,Z axises by negating them or by changing the cross product operand order to obtain the mirroring of shape you want. Now to construct the transform matrix see:
Understanding 4x4 homogenous transform matrices
Now just transform/render your shape with this matrix.
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
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)
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