Related
I am trying to recreate a Voronoi diagram without actually using the Voronoi function plot. I have a predefined 1x1 square that acts as my "test" area. My code knows where the lines of the diagram intersect the perimeter, but the line doesn't stop there. It keeps going until it reaches a random point.
x1 = 6
y1 = x1
x = gallery('uniformdata',[1,x1],0)
y = gallery('uniformdata',[1,y1],1)
sizeofx = size(x,2)
sizeofy = size(y,2)
reshapedx = reshape(x,sizeofx,1)
reshapedy = reshape(y,sizeofy,1)
[vxx,vyy] = voronoi(x,y)
hold on
[v,c] = voronoin([x(:) y(:)]) %intersection point matrix
for i=1:numel(c)
v(c{i},:)
ans = ans( ~any( isnan( ans ) | isinf( ans ), 2 ),: )%deletes infs or nans from polygon points
plot(ans(:,1),ans(:,2),'b-','linewidth',2) %%this code plots the points
end
%for our test purposes , the voronoi diagram will only be in a 1x1 square.
v(v<0) = inf
v(v>1) = inf
v = v( ~any( isnan( v ) | isinf( v ), 2 ),: ) %deletes the inf or nan
DT = delaunayTriangulation(reshapedx,reshapedy)
[V,R] = voronoiDiagram(DT)
sizeofR = size(R,1)
rectangle('Position',[0,0,1,1])
axis equal
xlimit = [0 1];
ylimit = [0 1];
xbox = xlimit([1 1 2 2 1]);
ybox = ylimit([1 2 2 1 1]);
%finds intersection from diagram to square
for j=1:length(vxx(1,:))
line([vxx(1,j) vxx(2,j)],[vyy(1,j) vyy(2,j)]);
[xi, yi, ii] = ...
polyxpoly([vxx(1,j) vxx(2,j)], [vyy(1,j) vyy(2,j)], xbox, ybox);
if ~isempty(xi)
intersectx(j) = xi
intersecty(j) = yi
plot(xi,yi,'r*');
axis equal
end
end
I want the line to stop plotting once it reaches a perimeter point.
You can replace the coordinate of line's edge with the intersection point.
Assume line goes from (x1,y1) to (x2,y2).
Check if (x1,y1) is outside the boundaries of the rectangle.
I your case (x1,y1) is outside the boundaries if (x1<0) || (y1<0) || (x1>1) || (y1>1).
In case (x1,y1) is outside, replace (x1,y1) with intersection point (xi,yi).
Note: Replace it only if there is an intersection point.
I ignored the case of two intersection points, because this case never happens in your case (in that case, you need to replace both).
Here is the complete code with the modifications:
x1 = 6;
y1 = x1;
x = gallery('uniformdata',[1,x1],0);
y = gallery('uniformdata',[1,y1],1);
sizeofx = size(x,2);
sizeofy = size(y,2);
reshapedx = reshape(x,sizeofx,1);
reshapedy = reshape(y,sizeofy,1);
[vxx,vyy] = voronoi(x,y);
hold on
[v,c] = voronoin([x(:) y(:)]); %intersection point matrix
for i=1:numel(c)
v(c{i},:);
ans = ans( ~any( isnan( ans ) | isinf( ans ), 2 ),: );%deletes infs or nans from polygon points
plot(ans(:,1),ans(:,2),'b-','linewidth',2); %%this code plots the points
end
%for our test purposes , the voronoi diagram will only be in a 1x1 square.
v(v<0) = inf;
v(v>1) = inf;
v = v( ~any( isnan( v ) | isinf( v ), 2 ),: ); %deletes the inf or nan
DT = delaunayTriangulation(reshapedx,reshapedy);
[V,R] = voronoiDiagram(DT);
sizeofR = size(R,1);
rectangle('Position',[0,0,1,1]);
axis equal
xlimit = [0 1];
ylimit = [0 1];
xbox = xlimit([1 1 2 2 1]);
ybox = ylimit([1 2 2 1 1]);
%Vxx and Vyy are going to be a new set of line coordinates, replacing vxx and vyy.
Vxx = vxx;
Vyy = vyy;
%finds intersection from diagram to square
for j=1:length(vxx(1,:))
%line([vxx(1,j) vxx(2,j)],[vyy(1,j) vyy(2,j)]);
[xi, yi, ii] = ...
polyxpoly([vxx(1,j) vxx(2,j)], [vyy(1,j) vyy(2,j)], xbox, ybox);
if ~isempty(xi)
intersectx(j) = xi;
intersecty(j) = yi;
plot(xi,yi,'r*', 'MarkerSize', 10);
axis equal
%Replace line edges outsize the rectangle with the inersection point.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
x1 = vxx(1,j);
x2 = vxx(2,j);
y1 = vyy(1,j);
y2 = vyy(2,j);
is_outsize1 = (x1 < 0) || (y1 < 0) || (x1 > 1) || (y1 > 1);
is_outsize2 = (x2 < 0) || (y2 < 0) || (x2 > 1) || (y2 > 1);
%Assume rectangle's boundaries are (0,0) to (1,1)
if is_outsize1
%Replace the coordinate [vxx(1,j), vyy(1,j)] with [xi, yi].
Vxx(1,j) = xi;
Vyy(1,j) = yi;
end
if is_outsize2
%Replace the coordinate [vxx(2,j), vyy(2,j)] with [xi, yi].
Vxx(2,j) = xi;
Vyy(2,j) = yi;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end
%Plot the line with the replaces coordinates
line([Vxx(1,j) Vxx(2,j)],[Vyy(1,j) Vyy(2,j)], 'color', 'g', 'LineWidth', 1, 'Marker', '+');
end
Result:
Original plot:
I want to fill this ellipse with N random points inside it
any help I'd be glad
clear ,close all;
xCenter = 15;
yCenter = 10;
xRadius = 3.5;
yRadius = 8;
theta = 0 : 0.01 : 2*pi;
N = 100; % N rand points
x = xRadius * cos(theta) + xCenter;
y = yRadius * sin(theta) + yCenter;
plot(x, y, 'LineWidth', 1);
axis square;
grid on;
I tried this code to generate 100 points inside the ellipse with specific parameters but I did not achieve my goal,
xCenter = 5;
yCenter = 3;
xRadius = 3.5;
yRadius = 8;
theta = 0 : 0.01 : 2*pi;
N = 100;
x = xRadius * cos(theta) + xCenter;
y = yRadius * sin(theta) + yCenter;
xq=(rand(N,1)*(2*yRadius) - yRadius);
yq=(rand(N,1)*(2*yRadius) - yRadius);
in = inpolygon(xq,yq,x,y);
hold on
inX = xq(in);
inY = yq(in);
outX = xq(~in);
outY = yq(~in);
plot(inX, inY , 'ro');
plot(outX, outY, 'b*');
plot(x, y, 'LineWidth', 1);
axis square;
grid on;
Sardar's answer produces points not evenly distributed within the ellipse. This code produces an even distribution of points:
xCenter = 15;
yCenter = 10;
xRadius = 3.5;
yRadius = 8;
N = 100;
% Generate points in the ellipse
t = 2*pi * rand(N,1);
d = sqrt(rand(N,1));
x = xCenter + xRadius * d .* cos(t);
y = yCenter + yRadius * d .* sin(t);
plot(x,y,'o')
The difference is the sqrt on the normalized (0 to 1) distance from the origin d. By computing this square root, you increase the density of points closer to the edge of the ellipse. This compensates for points otherwise being too dense close to the center. The uniform distribution of points along that normalized distance is what causes higher density of points near the center.
Generate random numbers for x and y axes between the specified limits, i.e. xRadius and yRadius, for the respective axes. Read Random Numbers Within a Specific Range to understand how to generate those random numbers.
hold on;
RndAngles = rand(N,1); %Same angle should be used
Xpoints = (xRadius.*rand(N,1) .*cos(2*pi*RndAngles))+ xCenter;
Ypoints = (yRadius.*rand(N,1) .*sin(2*pi*RndAngles))+ yCenter;
plot(Xpoints,Ypoints,'o'); %Plot those points
Output:
I'm looking for a simple way for creating a random unit vector constrained by a conical region. The origin is always the [0,0,0].
My solution up to now:
function v = GetRandomVectorInsideCone(coneDir,coneAngleDegree)
coneDir = normc(coneDir);
ang = coneAngleDegree + 1;
while ang > coneAngleDegree
v = [randn(1); randn(1); randn(1)];
v = v + coneDir;
v = normc(v);
ang = atan2(norm(cross(v,coneDir)), dot(v,coneDir))*180/pi;
end
My code loops until the random generated unit vector is inside the defined cone. Is there a better way to do that?
Resultant image from test code bellow
Resultant frequency distribution using Ahmed Fasih code (in comments).
I wonder how to get a rectangular or normal distribution.
c = [1;1;1]; angs = arrayfun(#(i) subspace(c, GetRandomVectorInsideCone(c, 30)), 1:1e5) * 180/pi; figure(); hist(angs, 50);
Testing code:
clearvars; clc; close all;
coneDir = [randn(1); randn(1); randn(1)];
coneDir = [0 0 1]';
coneDir = normc(coneDir);
coneAngle = 45;
N = 1000;
vAngles = zeros(N,1);
vs = zeros(3,N);
for i=1:N
vs(:,i) = GetRandomVectorInsideCone(coneDir,coneAngle);
vAngles(i) = subspace(vs(:,i),coneDir)*180/pi;
end
maxAngle = max(vAngles);
minAngle = min(vAngles);
meanAngle = mean(vAngles);
AngleStd = std(vAngles);
fprintf('v angle\n');
fprintf('Direction: [%.3f %.3f %.3f]^T. Angle: %.2fº\n',coneDir,coneAngle);
fprintf('Min: %.2fº. Max: %.2fº\n',minAngle,maxAngle);
fprintf('Mean: %.2fº\n',meanAngle);
fprintf('Standard Dev: %.2fº\n',AngleStd);
%% Plot
figure;
grid on;
rotate3d on;
axis equal;
axis vis3d;
axis tight;
hold on;
xlabel('X'); ylabel('Y'); zlabel('Z');
% Plot all vectors
p1 = [0 0 0]';
for i=1:N
p2 = vs(:,i);
plot3ex(p1,p2);
end
% Trying to plot the limiting cone, but no success here :(
% k = [0 1];
% [X,Y,Z] = cylinder([0 1 0]');
% testsubject = surf(X,Y,Z);
% set(testsubject,'FaceAlpha',0.5)
% N = 50;
% r = linspace(0, 1, N);
% [X,Y,Z] = cylinder(r, N);
%
% h = surf(X, Y, Z);
%
% rotate(h, [1 1 0], 90);
plot3ex.m:
function p = plot3ex(varargin)
% Plots a line from each p1 to each p2.
% Inputs:
% p1 3xN
% p2 3xN
% args plot3 configuration string
% NOTE: p1 and p2 number of points can range from 1 to N
% but if the number of points are different, one must be 1!
% PVB 2016
p1 = varargin{1};
p2 = varargin{2};
extraArgs = varargin(3:end);
N1 = size(p1,2);
N2 = size(p2,2);
N = N1;
if N1 == 1 && N2 > 1
N = N2;
elseif N1 > 1 && N2 == 1
N = N1
elseif N1 ~= N2
error('if size(p1,2) ~= size(p1,2): size(p1,2) and/or size(p1,2) must be 1 !');
end
for i=1:N
i1 = i;
i2 = i;
if i > N1
i1 = N1;
end
if i > N2
i2 = N2;
end
x = [p1(1,i1) p2(1,i2)];
y = [p1(2,i1) p2(2,i2)];
z = [p1(3,i1) p2(3,i2)];
p = plot3(x,y,z,extraArgs{:});
end
Here’s the solution. It’s based on the wonderful answer at https://math.stackexchange.com/a/205589/81266. I found this answer by googling “random points on spherical cap”, after I learned on Mathworld that a spherical cap is this cut of a 3-sphere with a plane.
Here’s the function:
function r = randSphericalCap(coneAngleDegree, coneDir, N, RNG)
if ~exist('coneDir', 'var') || isempty(coneDir)
coneDir = [0;0;1];
end
if ~exist('N', 'var') || isempty(N)
N = 1;
end
if ~exist('RNG', 'var') || isempty(RNG)
RNG = RandStream.getGlobalStream();
end
coneAngle = coneAngleDegree * pi/180;
% Generate points on the spherical cap around the north pole [1].
% [1] See https://math.stackexchange.com/a/205589/81266
z = RNG.rand(1, N) * (1 - cos(coneAngle)) + cos(coneAngle);
phi = RNG.rand(1, N) * 2 * pi;
x = sqrt(1-z.^2).*cos(phi);
y = sqrt(1-z.^2).*sin(phi);
% If the spherical cap is centered around the north pole, we're done.
if all(coneDir(:) == [0;0;1])
r = [x; y; z];
return;
end
% Find the rotation axis `u` and rotation angle `rot` [1]
u = normc(cross([0;0;1], normc(coneDir)));
rot = acos(dot(normc(coneDir), [0;0;1]));
% Convert rotation axis and angle to 3x3 rotation matrix [2]
% [2] See https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
crossMatrix = #(x,y,z) [0 -z y; z 0 -x; -y x 0];
R = cos(rot) * eye(3) + sin(rot) * crossMatrix(u(1), u(2), u(3)) + (1-cos(rot))*(u * u');
% Rotate [x; y; z] from north pole to `coneDir`.
r = R * [x; y; z];
end
function y = normc(x)
y = bsxfun(#rdivide, x, sqrt(sum(x.^2)));
end
This code just implements joriki’s answer on math.stackexchange, filling in all the details that joriki omitted.
Here’s a script that shows how to use it.
clearvars
coneDir = [1;1;1];
coneAngleDegree = 30;
N = 1e4;
sol = randSphericalCap(coneAngleDegree, coneDir, N);
figure;plot3(sol(1,:), sol(2,:), sol(3,:), 'b.', 0,0,0,'rx');
grid
xlabel('x'); ylabel('y'); zlabel('z')
legend('random points','origin','location','best')
title('Final random points on spherical cap')
Here is a 3D plot of 10'000 points from the 30° spherical cap centered around the [1; 1; 1] vector:
Here’s 120° spherical cap:
Now, if you look at the histogram of the angles between these random points at the coneDir = [1;1;1], you will see that the distribution is skewed. Here’s the distribution:
Code to generate this:
normc = #(x) bsxfun(#rdivide, x, sqrt(sum(x.^2)));
mysubspace = #(a,b) real(acos(sum(bsxfun(#times, normc(a), normc(b)))));
angs = arrayfun(#(i) mysubspace(coneDir, sol(:,i)), 1:N) * 180/pi;
nBins = 16;
[n, edges] = histcounts(angs, nBins);
centers = diff(edges(1:2))*[0:(length(n)-1)] + mean(edges(1:2));
figure('color','white');
bar(centers, n);
xlabel('Angle (degrees)')
ylabel('Frequency')
title(sprintf('Histogram of angles between coneDir and random points: %d deg', coneAngleDegree))
Well, this makes sense! If you generate points from the 120° spherical cap around coneDir, of course the 1° cap is going to have very few of those samples, whereas the strip between the 10° and 11° caps will have far more points. So what we want to do is normalize the number of points at a given angle by the surface area of the spherical cap at that angle.
Here’s a function that gives us the surface area of the spherical cap with radius R and angle in radians theta (equation 16 on Mathworld’s spherical cap article):
rThetaToH = #(R, theta) R * (1 - cos(theta));
rThetaToS = #(R, theta) 2 * pi * R * rThetaToH(R, theta);
Then, we can normalize the histogram count for each bin (n above) by the difference in surface area of the spherical caps at the bin’s edges:
figure('color','white');
bar(centers, n ./ diff(rThetaToS(1, edges * pi/180)))
The figure:
This tells us “the number of random vectors divided by the surface area of the spherical segment between histogram bin edges”. This is uniform!
(N.B. If you do this normalized histogram for the vectors generated by your original code, using rejection sampling, the same holds: the normalized histogram is uniform. It’s just that rejection sampling is expensive compared to this.)
(N.B. 2: note that the naive way of picking random points on a sphere—by first generating azimuth/elevation angles and then converting these spherical coordinates to Cartesian coordinates—is no good because it bunches points near the poles: Mathworld, example, example 2. One way to pick points on the entire sphere is sampling from the 3D normal distribution: that way you won’t get bunching near poles. So I believe that your original technique is perfectly appropriate, giving you nice, evenly-distributed points on the sphere without any bunching. This algorithm described above also does the “right thing” and should avoid bunching. Carefully evaluate any proposed algorithms to ensure that the bunching-near-poles problem is avoided.)
it is better to use spherical coordinates and convert it to cartesian coordinates:
coneDirtheta = rand(1) * 2 * pi;
coneDirphi = rand(1) * pi;
coneAngle = 45;
N = 1000;
%perfom transformation preventing concentration of points around the pole
rpolar = acos(cos(coneAngle/2*pi/180) + (1-cos(coneAngle/2*pi/180)) * rand(N, 1));
thetapolar = rand(N,1) * 2 * pi;
x0 = rpolar .* cos(thetapolar);
y0 = rpolar .* sin(thetapolar);
theta = coneDirtheta + x0;
phi = coneDirphi + y0;
r = rand(N, 1);
x = r .* cos(theta) .* sin(phi);
y = r .* sin(theta) .* sin(phi);
z = r .* cos(phi);
scatter3(x,y,z)
if all points should be of length 1 set r = ones(N,1);
Edit:
since intersection of cone with sphere forms a circle first we create random points inside a circle with raduis of (45 / 2) in polar coordinates and as #Ahmed Fasih commented to prevent concentration of points near the pole we should first transform this random points, then convert polar to cartesian 2D coordinates to form x0 and y0
we can use x0 and y0 as phi & theta angle of spherical coordinates and add coneDirtheta & coneDirphi as offsets to these coordinates.
then convert spherical to cartesian 3D coordinates
In the code below, I am attempting to move a ball from 90 degrees to 44 degrees and back to 90 degrees. I wanted to be able to track the position of the ball's center point. I am trying to do this by plotting a marker that follows the ball. I would also like to keep seeing the (x,y) position of the ball being updated as the ball moves.
Can you help me modify my code so that:
1. The marker remains in the center of the ball, and
2. Keep displaying the (x,y) position of the marker (ball's center) as it moves?
Here is what I have done so far:
close all; clc; clear;
% Define position of arm
theta=0:10:360; %theta is spaced around a circle (0 to 360).
r=0.04; %The radius of our circle.
%Define a circular magenta patch.
x=r*cosd(theta) + 0.075;
y=r*sind(theta) + 1;
% Size figure and draw arms
figure('position', [800, 300, 600, 550]);
point = plot(0.075,1, 'bo', 'markers', 3);
hold on
myShape2=patch(x,y,'m');
set(myShape2,'Xdata',x,'Ydata',y);
axis([-1.5 3 -1 3]); grid on;
n = 1;
T=0.15; %Delay between images
for theta = pi/2:-pi/90:0,
if theta >= pi/4;
theta = theta;
else
theta = pi/2 - theta;
end
Arot = [sin(theta) cos(theta); -cos(theta) sin(theta)];
xyRot = Arot * [x; y]; % rotates the points by theta
xyTrans = xyRot;
point = plot(xyTrans(1, n),xyTrans(2, n), 'bo', 'markers', 3);
hold on;
set(myShape2,'Xdata',(xyTrans(1, :)),'Ydata',(xyTrans(2, :)));
pause(T); %Wait T seconds
end
Thanks for your time and help!
close all; clc; clear;
% Define position of arm
theta=0:10:360; %theta is spaced around a circle (0 to 360).
r=0.04; %The radius of our circle.
%Define a circular magenta patch.
x=r*cosd(theta) + 0.075;
y=r*sind(theta) + 1;
% Size figure and draw arms
figure('position', [800, 300, 600, 550]);
hold on
myShape2=patch(x,y,'m');
set(myShape2,'Xdata',x,'Ydata',y);
point = plot(0.075,1, 'bo', 'markers', 1);
dim = [.2 .5 .3 .3];
axis([-1.5 3 -1 3]); grid on;
T=0.15;
% Create annotation object to display object center
ano = annotation('textbox',dim,'String',['center position: (','0.075, 1'],'FitBoxToText','on');
for theta = pi/2:-pi/90:0,
if theta >= pi/4;
theta = theta;
else
theta = pi/2 - theta;
end
Arot = [sin(theta) cos(theta); -cos(theta) sin(theta)];
xyTrans = Arot * [x; y];
% Remove plot and annotation object from previous frame
delete(point)
delete(ano)
set(myShape2,'Xdata',(xyTrans(1, :)),'Ydata',(xyTrans(2, :)));
% Calculate the center of the patch as mean x and y position
x_pos = mean(xyTrans(1, :));
y_pos = mean(xyTrans(2, :));
point = plot(x_pos, y_pos, 'bo', 'markers', 1);
ano = annotation('textbox',dim,'String',['center position: (',sprintf('%0.3f', x_pos),', ',...
sprintf('%0.3f', y_pos),')'],'FitBoxToText','on');
axis([-1.5 3 -1 3]); grid on;
pause(T);
end
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?