I am doing ray tracing and I have A screen described in the world coordinates as Matrices(I had before the X,Y,Z in the screen coordinates and by using transformation and rotation I got it in the world coordinates)
Xw (NXM Matrix)
Yw (NXM Matrix)
Zw (I have got this polynomial (5th order polynomial)by fitting the 3D data Xw and Yw. I have it as f(Xw,Yw))
I have the rays equations too described as usual:
X = Ox + t*Dx
Y = Oy + t*Dy
Z = Oz + t*Dz %(O is the origin point and D is the direction)
So what I did is that I replaced the X and Y in the Polynomial equation f(Xw,Yw) and solved it for t so I can then get the intersection point.
But apparently the method that I used is wrong(The intersection points that I got were somewhere else).
Could any one please help me and tell me what is the mistake. Please support me.
Thanks
This is part of the code:
X_World_coordinate_scr = ScreenXCoordinates.*Rotation_matrix_screen(1,1) + ScreenYCoordinates.*Rotation_matrix_screen(1,2) + ScreenZCoordinates.*Rotation_matrix_screen(1,3) + Zerobase_scr(1);
Y_World_coordinate_scr = ScreenXCoordinates.*Rotation_matrix_screen(2,1) + ScreenYCoordinates.*Rotation_matrix_screen(2,2) + ScreenZCoordinates.*Rotation_matrix_screen(2,3) + Zerobase_scr(2);
Z_World_coordinate_scr = ScreenXCoordinates.*Rotation_matrix_screen(3,1) + ScreenYCoordinates.*Rotation_matrix_screen(3,2) + ScreenZCoordinates.*Rotation_matrix_screen(3,3) + Zerobase_scr(3); % converting the screen coordinates to the world coordinates using the rotation matrix and the translation vector
polymodel = polyfitn([X_World_coordinate_scr(:),Y_World_coordinate_scr(:)],Z_World_coordinate_scr(:),5); % using a function from the MAtlab file exchange and I trust this function. I tried it different data and it gives me the f(Xw,Yw).
ScreenPoly = polyn2sym(polymodel); % Function from Matlab file exchange to give the symbolic shape of the polynomial.
syms X Y Z t Dx Ox Dy Oy oz Dz z;
tsun = matlabFunction(LayerPoly, 'vars',[X,Y,Z]); % just to substitue the symboles from X , Y and Z to (Ox+t*Dx) , (Oy+t*Dy) and (Oz+t*Dz) respectively
Equation = tsun((Ox+t*Dx),(Oy+t*Dy),(Oz+t*Dz));
Answer = solve(Equation,t); % solving it for t but the equation that it is from the 5th order and the answer is RootOf(.... for z)
a = char(Answer); % preparing it to find the roots (Solutions of t)
R = strrep(a,'RootOf(','');
R1 = strrep(R,', z)','');
b = sym(R1);
PolyCoeffs = coeffs(b,z); % get the coefficient of the polynomail
tfun = matlabFunction(PolyCoeffs, 'vars',[Ox,Oy,oz,Dx,Dy,Dz]);
tCounter = zeros(length(Directions),1);
NaNIndices = find(isnan(Surface(:,1))==1); %I have NaN values and I am taking them out
tCounter(NaNIndices) = NaN;
NotNaNIndices = find(isnan(Surface(:,1))==0);
for i = NotNaNIndices' % for loop to calc
OxNew = Surface(i,1);
OyNew = Surface(i,2);
OzNew = Surface(i,3);
DxNew = Directions(i,1);
DyNew = Directions(i,2);
DzNew = Directions(i,3);
P = tfun(OxNew,OyNew,OzNew ,DxNew,DyNew,DzNew);
t = roots(P);
t(imag(t) ~= 0) = []; % getting rid of the complex solutions
tCounter(i) = t;
end
Please support
Thanks in advance
Related
I want to solve the following PDE which describes the time evolution of a membrane
The PDE is discretised as follows
where
and x(s_i) = x_i, j=0,1, and x^j=x, x^j=y. We are ignoring the pressure term for now as well as constants like k_t etc.
We wish to find x(t) using a forward Euler method, by setting x = x + h*dx/dt, with h=1e-6.
My solution (for x(t)) is as follows (ignoring the y terms for ease of answering)
l = [359,1:358];
r = [2:359,1]; %left and right indexing vectors
l1 = [358,359,1:357]; %twice left neighbour
r1 = [3:359,1,2]; %twice right neighbour
%create initial closed contour with coordinates x and y
phi = linspace(0,2*pi,360); phi(end) = []; %since closed curve
x = (5 + 0.5*sin(20*phi)).*cos(phi);
y = (5+0.5*sin(20*phi).*cos(phi);
ds2 = (1/360)^2;
for i = 1:2e5
lengths = sqrt( (x-x(r)).^2 + (y-y(r)).^2 );
Tx=(1/10)/ds2*(x(r) -2*x +x(l) - x0*(((x(r)-x)/lengths)-((x-x(l))/lengths(l)) ) );
%tension term
Bx = 1/ds2^2*(x(r1) - 2*x(r) + x -2(x(r) -2*x + x(l)) + x -2*x(l) + x(l1) ); %bending term
x = x + 1/(1e6)*(Tx); % - Bx);
% Euler forward step
end
Currently, the code runs, but in the last line, if I decomment out the Bx term, the program fails to run. It seems as though my Bx matches what's in the discretisation, but obviously not.
Supposed I have two random double array, which means that one x coordinate might have multiple y value.
X = randi([-10 10],1,1000);
Y = randi([-10 10],1,1000);
Then I give a line equation: y=ax+b.
I want to find the nearest point to the nearest point to the line based on every x point. And when I find this point, I will find it's neighborhood points within specific range. Please forgive my poor English, maybe following picture can help more.
Because I have a lot of data points, I hope there is an efficient way to deal with this problem.
if your X's are discrete you can try something like:
xrng = [-10 10];
yrng = [-10 10];
a = rand;
b = rand;
f = #(x) a*x + b;
X = randi(xrng,1,1000);
Y = randi(yrng,1,1000);
ezplot(f,xrng);
hold on;
plot(X,Y,'.');
xx = xrng(1):xrng(2);
nbrSz = 2;
nx = diff(xrng);
nearestIdx = zeros(nx,1);
nbrIdxs = cell(nx,1);
for ii = 1:nx
x = xx(ii);
y = f(x);
idx = find(X == x);
[~,idxidx] = min(abs(y - Y(idx)));
nearestIdx(ii) = idx(idxidx);
nbrIdxIdxs = abs(Y(nearestIdx(ii)) - Y(idx)) <= nbrSz;
nbrIdxs{ii} = idx(nbrIdxIdxs);
plot(X(nearestIdx(ii)),Y(nearestIdx(ii)),'og');
plot(X(nearestIdx(ii)) + [0 0],Y(nearestIdx(ii)) + [-nbrSz nbrSz],'g')
plot(X(nbrIdxs{ii}),Y(nbrIdxs{ii}),'sy')
end
I need to generate a fixed number of non-overlapping circles located randomly. I can display circles, in this case 20, located randomly with this piece of code,
for i =1:20
x=0 + (5+5)*rand(1)
y=0 + (5+5)*rand(1)
r=0.5
circle3(x,y,r)
hold on
end
however circles overlap and I would like to avoid this. This was achieved by previous users with Mathematica https://mathematica.stackexchange.com/questions/69649/generate-nonoverlapping-random-circles , but I am using MATLAB and I would like to stick to it.
For reproducibility, this is the function, circle3, I am using to draw the circles
function h = circle3(x,y,r)
d = r*2;
px = x-r;
py = y-r;
h = rectangle('Position',[px py d d],'Curvature',[1,1]);
daspect([1,1,1])
Thank you.
you can save a list of all the previously drawn circles. After
randomizing a new circle check that it doesn't intersects the previously drawn circles.
code example:
nCircles = 20;
circles = zeros(nCircles ,2);
r = 0.5;
for i=1:nCircles
%Flag which holds true whenever a new circle was found
newCircleFound = false;
%loop iteration which runs until finding a circle which doesnt intersect with previous ones
while ~newCircleFound
x = 0 + (5+5)*rand(1);
y = 0 + (5+5)*rand(1);
%calculates distances from previous drawn circles
prevCirclesY = circles(1:i-1,1);
prevCirclesX = circles(1:i-1,2);
distFromPrevCircles = ((prevCirclesX-x).^2+(prevCirclesY-y).^2).^0.5;
%if the distance is not to small - adds the new circle to the list
if i==1 || sum(distFromPrevCircles<=2*r)==0
newCircleFound = true;
circles(i,:) = [y x];
circle3(x,y,r)
end
end
hold on
end
*notice that if the amount of circles is too big relatively to the range in which the x and y coordinates are drawn from, the loop may run infinitely.
in order to avoid it - define this range accordingly (it can be defined as a function of nCircles).
If you're happy with brute-forcing, consider this solution:
N = 60; % number of circles
r = 0.5; % radius
newpt = #() rand([1,2]) * 10; % function to generate a new candidate point
xy = newpt(); % matrix to store XY coordinates
fails = 0; % to avoid looping forever
while size(xy,1) < N
% generate new point and test distance
pt = newpt();
if all(pdist2(xy, pt) > 2*r)
xy = [xy; pt]; % add it
fails = 0; % reset failure counter
else
% increase failure counter,
fails = fails + 1;
% give up if exceeded some threshold
if fails > 1000
error('this is taking too long...');
end
end
end
% plot
plot(xy(:,1), xy(:,2), 'x'), hold on
for i=1:size(xy,1)
circle3(xy(i,1), xy(i,2), r);
end
hold off
Slightly amended code #drorco to make sure exact number of circles I want are drawn
nCircles = 20;
circles = zeros(nCircles ,2);
r = 0.5;
c=0;
for i=1:nCircles
%Flag which holds true whenever a new circle was found
newCircleFound = false;
%loop iteration which runs until finding a circle which doesnt intersect with previous ones
while ~newCircleFound & c<=nCircles
x = 0 + (5+5)*rand(1);
y = 0 + (5+5)*rand(1);
%calculates distances from previous drawn circles
prevCirclesY = circles(1:i-1,1);
prevCirclesX = circles(1:i-1,2);
distFromPrevCircles = ((prevCirclesX-x).^2+(prevCirclesY-y).^2).^0.5;
%if the distance is not to small - adds the new circle to the list
if i==1 || sum(distFromPrevCircles<=2*r)==0
newCircleFound = true;
c=c+1
circles(i,:) = [y x];
circle3(x,y,r)
end
end
hold on
end
Although this is an old post, and because I faced the same problem before I would like to share my solution, which uses anonymous functions: https://github.com/davidnsousa/mcsd/blob/master/mcsd/cells.m . This code allows to create 1, 2 or 3-D cell environments from user-defined cell radii distributions. The purpose was to create a complex environment for monte-carlo simulations of diffusion in biological tissues: https://www.mathworks.com/matlabcentral/fileexchange/67903-davidnsousa-mcsd
A simpler but less flexible version of this code would be the simple case of a 2-D environment. The following creates a space distribution of N randomly positioned and non-overlapping circles with radius R and with minimum distance D from other cells. All packed in a square region of length S.
function C = cells(N, R, D, S)
C = #(x, y, r) 0;
for n=1:N
o = randi(S-R,1,2);
while C(o(1),o(2),2 * R + D) ~= 0
o = randi(S-R,1,2);
end
f = #(x, y) sqrt ((x - o(1)) ^ 2 + (y - o(2)) ^ 2);
c = #(x, y, r) f(x, y) .* (f(x, y) < r);
C = #(x, y, r) + C(x, y, r) + c(x, y, r);
end
C = #(x, y) + C(x, y, R);
end
where the return C is the combined anonymous functions of all circles. Although it is a brute force solution it is fast and elegant, I believe.
From a couple references (i.e., http://en.wikipedia.org/wiki/Rotation_matrix "Rotation matrix from axis and angle", and exercise 5.15 in "Computer Graphics - Principles and Practice" by Foley et al, 2nd edition in C), I've seen this definition of a rotation matrix (implemented below in Octave) that rotates points by a specified angle about a specified vector. Although I have used it before, I'm now seeing rotation problems that appear to be related to orientation. The problem is recreated in the following Octave code that
takes two unit vectors: src (green in figures) and dst (red in figures),
calculates the angle between them: theta,
calculates the vector normal to both: pivot (blue in figures),
and finally attempts to rotate src into dst by rotating it about vector pivot by angle theta.
% This test fails: rotated unit vector is not at expected location and is no longer normalized.
s = [-0.49647; -0.82397; -0.27311]
d = [ 0.43726; -0.85770; -0.27048]
test_rotation(s, d, 1);
% Determine rotation matrix that rotates the source and normal vectors to the x and z axes, respectively.
normal = cross(s, d);
normal /= norm(normal);
R = zeros(3,3);
R(1,:) = s;
R(2,:) = cross(normal, s);
R(3,:) = normal;
R
% After rotation of the source and destination vectors, this test passes.
s2 = R * s
d2 = R * d
test_rotation(s2, d2, 2);
function test_rotation(src, dst, iFig)
norm_src = norm(src)
norm_dst = norm(dst)
% Determine rotation axis (i.e., normal to two vectors) and rotation angle.
pivot = cross(src, dst);
theta = asin(norm(pivot))
theta_degrees = theta * 180 / pi
pivot /= norm(pivot)
% Initialize matrix to rotate by an angle theta about pivot vector.
ct = cos(theta);
st = sin(theta);
omct = 1 - ct;
M(1,1) = ct - pivot(1)*pivot(1)*omct;
M(1,2) = pivot(1)*pivot(2)*omct - pivot(3)*st;
M(1,3) = pivot(1)*pivot(3)*omct + pivot(2)*st;
M(2,1) = pivot(1)*pivot(2)*omct + pivot(3)*st;
M(2,2) = ct - pivot(2)*pivot(2)*omct;
M(2,3) = pivot(2)*pivot(3)*omct - pivot(1)*st;
M(3,1) = pivot(1)*pivot(3)*omct - pivot(2)*st;
M(3,2) = pivot(2)*pivot(3)*omct + pivot(1)*st;
M(3,3) = ct - pivot(3)*pivot(3)*omct;
% Rotate src about pivot by angle theta ... and check the result.
dst2 = M * src
dot_dst_dst2 = dot(dst, dst2)
if (dot_dst_dst2 >= 0.99999)
"success"
else
"FAIL"
end
% Draw the vectors: green is source, red is destination, blue is normal.
figure(iFig);
x(1) = y(1) = z(1) = 0;
ubounds = [-1.25 1.25 -1.25 1.25 -1.25 1.25];
x(2)=src(1); y(2)=src(2); z(2)=src(3);
plot3(x,y,z,'g-o');
hold on
x(2)=dst(1); y(2)=dst(2); z(2)=dst(3);
plot3(x,y,z,'r-o');
x(2)=pivot(1); y(2)=pivot(2); z(2)=pivot(3);
plot3(x,y,z,'b-o');
x(2)=dst2(1); y(2)=dst2(2); z(2)=dst2(3);
plot3(x,y,z,'k.o');
axis(ubounds, 'square');
view(45,45);
xlabel("xd");
ylabel("yd");
zlabel("zd");
hold off
end
Here are the resulting figures. Figure 1 shows an orientation that doesn't work. Figure 2 shows an orientation that works: the same src and dst vectors but rotated into the first quadrant.
I was expecting the src vector to always rotate onto the dst vector, as shown in Figure 2 by the black circle covering the red circle, for all vector orientations. However Figure 1 shows an orientation where the src vector does not rotate onto the dst vector (i.e., the black circle is not on top of the red circle, and is not even on the unit sphere).
For what it's worth, the references that defined the rotation matrix did not mention orientation limitations, and I derived (in a few hours and a few pages) the rotation matrix equation and didn't spot any orientation limitations there. I'm hoping the problem is an implementation error on my part, but I haven't been able to find it yet in either of my implementations: C and Octave. Have you experienced orientation limitations when implementing this rotation matrix? If so, how did you work around them? I would prefer to avoid the extra translation into the first quadrant if it isn't necessary.
Thanks,
Greg
Seems two minus signs have escaped:
M(1,1) = ct - P(1)*P(1)*omct;
M(1,2) = P(1)*P(2)*omct - P(3)*st;
M(1,3) = P(1)*P(3)*omct + P(2)*st;
M(2,1) = P(1)*P(2)*omct + P(3)*st;
M(2,2) = ct + P(2)*P(2)*omct; %% ERR HERE; THIS IS THE CORRECT SIGN
M(2,3) = P(2)*P(3)*omct - P(1)*st;
M(3,1) = P(1)*P(3)*omct - P(2)*st;
M(3,2) = P(2)*P(3)*omct + P(1)*st;
M(3,3) = ct + P(3)*P(3)*omct; %% ERR HERE; THIS IS THE CORRECT SIGN
Here is a version that is much more compact, faster, and also based on Rodrigues' rotation formula:
function test
% first test: pass
s = [-0.49647; -0.82397; -0.27311];
d = [ 0.43726; -0.85770; -0.27048]
d2 = axis_angle_rotation(s, d)
% Determine rotation matrix that rotates the source and normal vectors to the x and z axes, respectively.
normal = cross(s, d);
normal = normal/norm(normal);
R(1,:) = s;
R(2,:) = cross(normal, s);
R(3,:) = normal;
% Second test: pass
s2 = R * s;
d2 = R * d
d3 = axis_angle_rotation(s2, d2)
end
function vec = axis_angle_rotation(vec, dst)
% These following commands are just here for the function to act
% the same as your original function. Eventually, the function is
% probably best defined as
%
% vec = axis_angle_rotation(vec, axs, angle)
%
% or even
%
% vec = axis_angle_rotation(vec, axs)
%
% where the length of axs defines the angle.
%
axs = cross(vec, dst);
theta = asin(norm(axs));
% some preparations
aa = axs.'*axs;
ra = vec.'*axs;
% location of circle centers
c = ra.*axs./aa;
% first coordinate axis on the circle's plane
u = vec-c;
% second coordinate axis on the circle's plane
v = [axs(2)*vec(3)-axs(3)*vec(2)
axs(3)*vec(1)-axs(1)*vec(3)
axs(1)*vec(2)-axs(2)*vec(1)]./sqrt(aa);
% the output vector
vec = c + u*cos(theta) + v*sin(theta);
end
I would like to plot a plane using a vector that I calculated from 3 points where:
pointA = [0,0,0];
pointB = [-10,-20,10];
pointC = [10,20,10];
plane1 = cross(pointA-pointB, pointA-pointC)
How do I plot 'plane1' in 3D?
Here's an easy way to plot the plane using fill3:
points=[pointA' pointB' pointC']; % using the data given in the question
fill3(points(1,:),points(2,:),points(3,:),'r')
grid on
alpha(0.3)
You have already calculated the normal vector. Now you should decide what are the limits of your plane in x and z and create a rectangular patch.
An explanation : Each plane can be characterized by its normal vector (A,B,C) and another coefficient D. The equation of the plane is AX+BY+CZ+D=0. Cross product between two differences between points, cross(P3-P1,P2-P1) allows finding (A,B,C). In order to find D, simply put any point into the equation mentioned above:
D = -Ax-By-Cz;
Once you have the equation of the plane, you can take 4 points that lie on this plane, and draw the patch between them.
normal = cross(pointA-pointB, pointA-pointC); %# Calculate plane normal
%# Transform points to x,y,z
x = [pointA(1) pointB(1) pointC(1)];
y = [pointA(2) pointB(2) pointC(2)];
z = [pointA(3) pointB(3) pointC(3)];
%Find all coefficients of plane equation
A = normal(1); B = normal(2); C = normal(3);
D = -dot(normal,pointA);
%Decide on a suitable showing range
xLim = [min(x) max(x)];
zLim = [min(z) max(z)];
[X,Z] = meshgrid(xLim,zLim);
Y = (A * X + C * Z + D)/ (-B);
reOrder = [1 2 4 3];
figure();patch(X(reOrder),Y(reOrder),Z(reOrder),'b');
grid on;
alpha(0.3);
Here's what I came up with:
function [x, y, z] = plane_surf(normal, dist, size)
normal = normal / norm(normal);
center = normal * dist;
tangents = null(normal') * size;
res(1,1,:) = center + tangents * [-1;-1];
res(1,2,:) = center + tangents * [-1;1];
res(2,2,:) = center + tangents * [1;1];
res(2,1,:) = center + tangents * [1;-1];
x = squeeze(res(:,:,1));
y = squeeze(res(:,:,2));
z = squeeze(res(:,:,3));
end
Which you would use as:
normal = cross(pointA-pointB, pointA-pointC);
dist = dot(normal, pointA)
[x, y, z] = plane_surf(normal, dist, 30);
surf(x, y, z);
Which plots a square of side length 60 on the plane in question
I want to add to the answer given by Andrey Rubshtein, his code works perfectly well except at B=0. Here is the edited version of his code
Below Code works when A is not 0
normal = cross(pointA-pointB, pointA-pointC);
x = [pointA(1) pointB(1) pointC(1)];
y = [pointA(2) pointB(2) pointC(2)];
z = [pointA(3) pointB(3) pointC(3)];
A = normal(1); B = normal(2); C = normal(3);
D = -dot(normal,pointA);
zLim = [min(z) max(z)];
yLim = [min(y) max(y)];
[Y,Z] = meshgrid(yLim,zLim);
X = (C * Z + B * Y + D)/ (-A);
reOrder = [1 2 4 3];
figure();patch(X(reOrder),Y(reOrder),Z(reOrder),'r');
grid on;
alpha(0.3);
Below Code works when C is not 0
normal = cross(pointA-pointB, pointA-pointC);
x = [pointA(1) pointB(1) pointC(1)];
y = [pointA(2) pointB(2) pointC(2)];
z = [pointA(3) pointB(3) pointC(3)];
A = normal(1); B = normal(2); C = normal(3);
D = -dot(normal,pointA);
xLim = [min(x) max(x)];
yLim = [min(y) max(y)];
[Y,X] = meshgrid(yLim,xLim);
Z = (A * X + B * Y + D)/ (-C);
reOrder = [1 2 4 3];
figure();patch(X(reOrder),Y(reOrder),Z(reOrder),'r');
grid on;
alpha(0.3);