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:
Consider the polygon with vertices (0, 0), (1, 0), (7/10, 1), (1/2, 1/2), and (3/10, 1). Make a plot of this polygon in Matlab using the fill function. Rotate this polygon by an angle of 100 degrees using matrix-vector multiplication in Matlab with an appropriately chosen rotation matrix R. Make another plot of the rotated polygon using fill.
% Makes original polygon
X = [0 1 7/10 1/2 3/10];
Y = [0 0 1 1/2 1];
norm_poly = fill(X,Y,'k');
thetad = 100;
R = [cosd(thetad) -sind(thetad); sind(thetad) cosd(thetad)];
C = repmat([0 0], 5, 1)';
axis([-10 10 -10 10])
V = get(norm_poly,'Vertices')'; % get the current set of vertices
V = R*(V - C) + C; % do the rotation relative to the centre of the
square
set(norm_poly,'Vertices',V'); % update the vertices
How would I make to different plots to show them? Does the code to rotate make sense and answer all the requirements?
The rotation itself makes sense. To plot multiple things to the same graph use hold on after making the first plot. Alternatively you can make a new figure and plot a new fill there.
P = [0, 1, 7/10, 1/2, 3/10;
0, 0, 1, 1/2, 1];
fill(P(1,:), P(2,:), 'k');
theta = 100;
R = #(t) [cosd(t) -sind(t); sind(t) cosd(t)];
axis([-3 3 -3 3])
grid on
V = R(theta)*P;
hold on;
fill(V(1,:), V(2,:), 'k');
I am trying to find a point on the plane that is closest to the origin. When I plot the normal, somehow it is not perpendicular to the plane! Also, the point on the plane closest to origin does not appear correct from the plot. I cannot figure out what is wrong. Any ideas?
c = 2;
x1 = [1, 0, 0] * c;
x2 = [0, 1, 0] * c;
x3 = [0, 0, 1] * c;
x = [x1(1), x2(1), x3(1)];
y = [x1(2), x2(2), x3(2)];
z = [x1(3), x2(3), x3(3)];
figure(1); plot3(x,y,z,'*r'); hold on; grid on;
normal = cross(x1-x2, x1-x3);
% Find all coefficients of plane equation
D = -dot(normal, x1);
range = [-10 10]; [X, Z] = meshgrid(range, range);
Y = (-normal(1) * X - normal(3) * Z - D)/ normal(2);
order = [1 2 4 3]; patch(X(order),Y(order),Z(order),'b');
alpha(0.3);
plot3([x(1), x(3)], [y(1), y(3)], [z(1), z(3)]);
plot3([x(1), x(2)], [y(1), y(2)], [z(1), z(2)]);
x1=x1-x3;
plot3([normal(1), x1(1)], [normal(2), x1(2)], [normal(3), x1(3)], 'k');
%% Get the point on the plane closest point to (0,0,0)
p = normal * (-D / sum(normal.^2,2));
plot3(p(1), p(2), p(3), '*g');
I appreciate your help.
normalize your normal:
normal = normal /norm(normal);
and plot the normal correctly:
plot3([x1(1)+normal(1), x1(1)], [x1(2)+normal(2), x1(2)], [x1(3)+normal(3), x1(3)], 'k');
and for a proper visualization, the axis should be of equal scale:
axis equal
Does not look bad, does it?
I have a point in 3d space (x,y,z). I want to move radially outward from that point discretely (say for r=1 and r=2). In the x,y plane I can simply move outward by stepping ((x+r cos(theta)), (y+r sin(theta)), z) with r = 1 or 2 and theta varying every, say 10 degrees.
However, I am unsure how to describe this movement if I want to have lines moving outward on a tilted plane and step my lines within this plane.
I thought it would be just using spherical coordinates. But if I'm drawing lines from a center point using (x=rho sin phi cos theta, y=..., z=...) won't that form a cone rather than a circle tilted on a plane?
P.S. Will be implementing this in MATLAB
You could first make the coordinates going outwards from P0 and then rotate the coordinates using a rotation matrix.
So you take points P for all R's and thetas, as MBo pointed out:
P = [ P0x + R * cos(theta); P0y + R * sin(theta); 0 ]
Then you make a rotation matrix that rotates the XY plane with the angles you want
If you multiply that with your coordinates you get the rotated coordinates. For example a 90 degree rotation about the Z axis for the point [1,0,0]:
However you probably want to rotate about the point P0 and not about the origin, then you have to make an affine matrix with the following translation:
tx = x- r00 * x - r01 * y - r02 * z
ty = y- r10 * x - r11 * y - r12 * z
tz = z- r20 * x - r21 * y - r22 * z
And then make an affine transformation matrix with T and R (designated as M in the figure, sorry):
In this figure Q are the old coordinates and Q' the new coordinates.
I had a similar problem and used this answer and adjusted it to your problem:
%input point and rotated plane
p0 = [10;10;10;1]; % the last entry is your homogeneous dimension
r0 = [45,45,45]; r0 = r0*pi/180;
%rotation to plane
Rx=[1 0 0 0;
0 cos(r0(1)) sin(r0(1)) 0;
0 -sin(r0(1)) cos(r0(1)) 0;
0 0 0 1];
Ry=[cos(r0(2)) 0 -sin(r0(2)) 0;
0 1 0 0;
sin(r0(2)) 0 cos(r0(2)) 0;
0 0 0 1];
Rz=[cos(r0(3)) sin(r0(3)) 0 0;
-sin(r0(3)) cos(r0(3)) 0 0;
0 0 1 0;
0 0 0 1];
R = Rz*Ry*Rx; A = R;
T = ( eye(3)-R(1:3,1:3) ) * p0(1:3); %calculate translation to rotate about the point P0
A(1:3,4) = T; % to rotate about the origin just leave out this line
%make coordinates for the points going outward from p0
nangles = 36; anglestep = 2*pi/nangles;
nradii = 2; radiistep = 1;
thetas = anglestep:anglestep:2*pi;
rs = radiistep:radiistep:nradii*radiistep;
npoints = nradii*nangles;
coordinates = zeros(4,npoints); curpoint = 0;
for itheta = 1:nangles; for iradius = 1:nradii;
curpoint = curpoint+1;
coordinates(:, curpoint) = p0+rs(iradius)*[cos(thetas(itheta));sin(thetas(itheta));0;0];
end; end
coordinates_tilted = A*coordinates; %rotate the coordinates to the new plane
Which results in this figure:
figure;
scatter3(coordinates_tilted(1,:),coordinates_tilted(2,:),coordinates_tilted(3,:), 'MarkerEdgeColor', 'green')
hold on
scatter3(coordinates(1,:),coordinates(2,:),coordinates(3,:), 'MarkerEdgeColor', 'red')
legend('tilted', 'original')
Or plot them as lines:
%or as lines
coorarray = reshape(coordinates, [4 nradii nangles]);
Xline = squeeze(coorarray(1,:,:));
Yline = squeeze(coorarray(2,:,:));
Zline = squeeze(coorarray(3,:,:));
coorarray_tilted = reshape(coordinates_tilted, [4 nradii nangles]);
Xline_tilted = squeeze(coorarray_tilted(1,:,:));
Yline_tilted = squeeze(coorarray_tilted(2,:,:));
Zline_tilted = squeeze(coorarray_tilted(3,:,:));
figure;
plot3(Xline,Yline,Zline, 'r');
hold on
plot3(Xline_tilted,Yline_tilted,Zline_tilted, 'g');
legend( 'original', 'tilted')
Does this answer your question? These are now points at all multiples of 36 degree angles at a distance of one and two from point P0 in the plane that is tilted 45 degrees on all axes around the point P0. If you need individual 'pixels' to designate your line (so integer coordinates) you can round the coordinates and that would be sort of a nearest neighbour approach:
coordinates_tilted_nearest = round(coordinates_tilted);
How is your tilted plane defined?
Define it with base point P0 and two perpendicular unit vectors U and V. It is not hard to get this representation from any other. For example, if normal vector of your plane have angles ax, ay, az with axes OX, OY, OZ respectively, it's normalized form is N = (nx, ny, nz) = (Cos(ax), Cos(ay), Cos(az)). You can choose arbitrary vector U (lying in the plane) as described here, and find V vector as vector product V = U x N
Then needed points are:
P = P0 + U * R * Cos(Theta) + V * R * Sin(Theta)
I made star using this code:
t = 0:4/5*pi:4*pi;
x = sin(t);
y = cos(t);
star = plot(x, y);
axis([-1 11 -1 11])
Now I need to rotate and move this star at the same time. I tried this:
for i=1:0.1:10;
zAxis = [0 0 1];
center = [0 0 0];
rotate(star, zAxis, 5, center);
x = x+0.1;
y = y+0.1;
set(star, 'x', x, 'y', y);
pause(0.1);
end
But this code only moves star and doesn't rotate it. If I delete "set" command then it rotates. How can I combine those two actions?
This can do the job..
t = 0:4/5*pi:4*pi;
x = sin(t);
y = cos(t) ;
y = y-mean(y);
x = x-mean(x); % # barycentric coordinates
% # rotation and translation
trasl = #(dx,dy) [dy; dx]; % # this vector will be rigidly added to each point of the system
rot = #(theta) [cos(theta) -sin(theta); sin(theta) cos(theta)]; % # this will provide rotation of angle theta
for i = 1:50
% # application of the roto-translation
% # a diagonal translation of x = i*.1 , y = i*.1 is added to the star
% # once a rotation of angle i*pi/50 is performed
x_t = bsxfun(#plus,rot(i*pi/50)*([x;y]), trasl(i*.1,i*.1) );
star = plot(x_t(1,:), x_t(2,:));
axis([-1 11 -1 11])
pause(.1)
end
In principle, homogeneous coordinates (in this case in the 2D projective space) allow one to do the same job in a neater way; in fact, they would allow one to use just one linear operator (3x3 matrix).
Homogeneous coordinates version:
Op = #(theta,dx,dy) [ rot(theta) , trasl(dx,dy) ; 0 0 1];
for i = 1:50
x_t = Op(i*pi/50,i*.1,i*.1)*[x;y;ones(size(x))];
star = plot(x_t(1,:), x_t(2,:));
axis([-1 11 -1 11])
pause(.1)
end
You can just use a rotation matrix to compute the correct transformation on the vectors [x; y]:
theta = 5 * (pi / 180); % 5 deg in radians
Arot = [cos(theta) -sin(theta); sin(theta) cos(theta)];
xyRot = Arot * [x; y]; % rotates the points by theta
xyTrans = xyRot + 0.1; % translates all points by 0.1
set(star, 'x', xyTrans(1, :), 'y', xyTrans(2, :));