Finding the first point of great circle Intersection - matlab

I have a problem I've been trying to solve and I cannot come up with the answer. I have written a function in Matlab which, given two lat/lon points and two bearings, will return the two great circle points of intersection.
However, what I really need is the first great circle point of intersection along the two initial headings. I.e. if two airplanes begin at lat/lon points 1 and 2, with initial bearings of bearing1 and bearing2, which of the two great circle intersection points is the first one they encounter? There are many solutions (using haversine) which will give me the closer of the two points, but I don't actually care about which is closer, I care about which I will encounter first given specific start points and headings. There are many cases where the closer of the two intersections is actually the second intersection encountered.
Now, I realize I could do this with lots of conditional statements for handling the different cases, but I figure there's got to be a way to handle it with regard to the order I take all my cross products (function code given below), but I simply can't come up with the right solution! I should also mention that this function is going to be used in a large computationally intensive model, and so I'm thinking the solution to this problem needs to be rather elegant/speedy. Can anyone help me with this problem?
The following is not my function code (I can't list that here), but it is the pseudo code that my function was based off of:
%Given inputs of lat1,lon1,Bearing1,lat2,lon2,Bearing2:
%Calculate arbitrary secondary point along same initial bearing from first
%point
dAngle = 45;
lat3 = asind( sind(lat1)*cosd(dAngle) + cosd(lat1)*sind(dAngle)*cosd(Bearing1));
lon3 = lon1 + atan2( sind(Bearing1)*sind(dAngle)*cosd(lat1), cosd(dAngle)-sind(lat1)*sind(lat3) )*180/pi;
lat4 = asind( sind(lat2)*cosd(dAngle) + cosd(lat2)*sind(dAngle)*cosd(Bearing2));
lon4 = lon2 + atan2( sind(Bearing2)*sind(dAngle)*cosd(lat2), cosd(dAngle)-sind(lat2)*sind(lat4) )*180/pi;
%% Calculate unit vectors
% We now have two points defining each of the two great circles. We need
% to calculate unit vectors from the center of the Earth to each of these
% points
[Uvec1(1),Uvec1(2),Uvec1(3)] = sph2cart(lon1*pi/180,lat1*pi/180,1);
[Uvec2(1),Uvec2(2),Uvec2(3)] = sph2cart(lon2*pi/180,lat2*pi/180,1);
[Uvec3(1),Uvec3(2),Uvec3(3)] = sph2cart(lon3*pi/180,lat3*pi/180,1);
[Uvec4(1),Uvec4(2),Uvec4(3)] = sph2cart(lon4*pi/180,lat4*pi/180,1);
%% Cross product
%Need to calculate the the "plane normals" for each of the two great
%circles
N1 = cross(Uvec1,Uvec3);
N2 = cross(Uvec2,Uvec4);
%% Plane of intersecting line
%With two plane normals, the cross prodcut defines their intersecting line
L = cross(N1,N2);
L = L./norm(L);
L2 = -L;
L2 = L2./norm(L2);
%% Convert normalized intersection line to geodetic coordinates
[lonRad,latRad,~]=cart2sph(L(1),L(2),L(3));
lonDeg = lonRad*180/pi;
latDeg = latRad*180/pi;
[lonRad,latRad,~]=cart2sph(L2(1),L2(2),L2(3));
lonDeg2 = lonRad*180/pi;
latDeg2 = latRad*180/pi;
UPDATE: A user on the Mathworks forums pointed this out:
Actually they might each reach a different point. I might have misunderstood the question, but the way you worded it suggests that both trajectories will converge towards the same point, which is not true. If you image the first point being a little after one intersection and the second point being a little after the other intersection, you have a situation were each "plane" will travel towards the intersection that was the closest to the other plane at the beginning.
I didn't think about this. It is actually very possible that each of the planes intersects a different great circle intersection first. Which just made things much more complicated...

Presumably you have a velocity vector for the direction of the plane (the direction in which you want to look for your first intersection - the "bearing vector on the surface of the earth"). You didn't actually specify this. Let us call it v.
You also have the cartesian coordinates of two points, P1 and P2, as well as the initial position of the plane, P0. Each is assumed to be already on the unit sphere (length = 1).
Now we want to know which is the shorter distance - to P1, or P2 - so we need to know the angle "as seen from the direction of the normal vector". For this we need both the sin and the cos - then we can find the angle using atan2. We obtain sin from the cross product (remember all vectors were normalized), and cos from the dot product. To get the sin "as seen from the right direction", we take dot product with the normal vector.
Here is a piece of code that puts it all together - I am using very simple coordinates for the points and direction vector so I can confirm in my head that this gives the correct answer:
% sample points of P0...P2 and v
P0 = [1 0 0];
P1 = [0 1 0];
P2 = [0 -1 0];
v = [0 1 0];
% compute the "start direction normal":
n0 = cross(P0, v);
n0 = n0 / norm( n0 ); % unit vector
% compute cross and dot products:
cr01 = cross(P0, P1);
cr02 = cross(P0, P2);
cos01 = dot(P0, P1);
cos02 = dot(P0, P2);
% to get sin with correct sign, take dot product with direction normal:
sin01 = dot(cr01, n0);
sin02 = dot(cr02, n0);
% note - since P0 P1 and P2 are all in the same plane
% cr02 and cr02 are either pointing in the same direction as n0
% or the opposite direction. In the latter case we get a sign flip for the sin
% in the former case this does nothing
% Now get the angle, mapped from 0 to 2 pi:
ang1 = mod(atan2(sin01, cos01), 2*pi);
ang2 = mod(atan2(sin02, cos02), 2*pi);
if( ang1 < ang2 )
fprintf(1,'point 1 is reached first\n');
% point 1 is the first one reached
else
fprintf(1,'point 2 is reached first\n');
% point 2 is the first
end
When you change the direction of the velocity vector (pointing towards P2 instead of P1), the program correctly tells you "point 2 is reached first".
Let me know if this works for you!

Related

Fitting largest circle in free area in image with distributed particle

I am working on images to detect and fit the largest possible circle in any of the free areas of an image containing distributed particles:
(able to detect the location of particle).
One direction is to define a circle touching any 3-point combination, checking if the circle is empty, then finding the largest circle among all empty circles. However, it leads to a huge number of combination i.e. C(n,3), where n is the total number of particles in the image.
I would appreciate if anyone can provide me any hint or alternate method that I can explore.
Lets do some maths my friend, as maths will always get to the end!
Wikipedia:
In mathematics, a Voronoi diagram is a partitioning of a plane into
regions based on distance to points in a specific subset of the plane.
For example:
rng(1)
x=rand(1,100)*5;
y=rand(1,100)*5;
voronoi(x,y);
The nice thing about this diagram is that if you notice, all the edges/vertices of those blue areas are all to equal distance to the points around them. Thus, if we know the location of the vertices, and compute the distances to the closest points, then we can choose the vertex with highest distance as our center of the circle.
Interestingly, the edges of a Voronoi regions are also defined as the circumcenters of the triangles generated by a Delaunay triangulation.
So if we compute the Delaunay triangulation of the area, and their circumcenters
dt=delaunayTriangulation([x;y].');
cc=circumcenter(dt); %voronoi edges
And compute the distances between the circumcenters and any of the points that define each triangle:
for ii=1:size(cc,1)
if cc(ii,1)>0 && cc(ii,1)<5 && cc(ii,2)>0 && cc(ii,2)<5
point=dt.Points(dt.ConnectivityList(ii,1),:); %the first one, or any other (they are the same distance)
distance(ii)=sqrt((cc(ii,1)-point(1)).^2+(cc(ii,2)-point(2)).^2);
end
end
Then we have the center (cc) and radius (distance) of all possible circles that have no point inside them. We just need the biggest one!
[r,ind]=max(distance); %Tada!
Now lets plot
hold on
ang=0:0.01:2*pi;
xp=r*cos(ang);
yp=r*sin(ang);
point=cc(ind,:);
voronoi(x,y)
triplot(dt,'color','r','linestyle',':')
plot(point(1)+xp,point(2)+yp,'k');
plot(point(1),point(2),'g.','markersize',20);
Notice how the center of the circle is on one vertex of the Voronoi diagram.
NOTE: this will find the center inside [0-5],[0-5]. you can easily modify it to change this constrain. You can also try to find the circle that fits on its entirety inside the interested area (as opposed to just the center). This would require a small addition in the end where the maximum is obtained.
I'd like to propose another solution based on a grid search with refinement. It's not as advanced as Ander's or as short as rahnema1's, but it should be very easy to follow and understand. Also, it runs quite fast.
The algorithm contains several stages:
We generate an evenly-spaced grid.
We find the minimal distances of points in the grid to all provided points.
We discard all points whose distances are below a certain percentile (e.g. 95th).
We choose the region which contains the largest distance (this should contain the correct center if my initial grid is fine enough).
We create a new meshgrid around the chosen region and find distances again (this part is clearly sub-optimal, because the distances are computed to all points, including far and irrelevant ones).
We iterate the refinement within the region, while keeping an eye on the variance of the top 5% of values -> if it drops below some preset threshold we break.
Several notes:
I have made the assumption that circles cannot go beyond the scattered points' extent (i.e. the bounding square of the scatter acts as an "invisible wall").
The appropriate percentile depends on how fine the initial grid is. This will also affect the amount of while iterations, and the optimal initial value for cnt.
function [xBest,yBest,R] = q42806059
rng(1)
x=rand(1,100)*5;
y=rand(1,100)*5;
%% Find the approximate region(s) where there exists a point farthest from all the rest:
xExtent = linspace(min(x),max(x),numel(x));
yExtent = linspace(min(y),max(y),numel(y)).';
% Create a grid:
[XX,YY] = meshgrid(xExtent,yExtent);
% Compute pairwise distance from grid points to free points:
D = reshape(min(pdist2([XX(:),YY(:)],[x(:),y(:)]),[],2),size(XX));
% Intermediate plot:
% figure(); plot(x,y,'.k'); hold on; contour(XX,YY,D); axis square; grid on;
% Remove irrelevant candidates:
D(D<prctile(D(:),95)) = NaN;
D(D > xExtent | D > yExtent | D > yExtent(end)-yExtent | D > xExtent(end)-xExtent) = NaN;
%% Keep only the region with the largest distance
L = bwlabel(~isnan(D));
[~,I] = max(table2array(regionprops('table',L,D,'MaxIntensity')));
D(L~=I) = NaN;
% surf(XX,YY,D,'EdgeColor','interp','FaceColor','interp');
%% Iterate until sufficient precision:
xExtent = xExtent(~isnan(min(D,[],1,'omitnan')));
yExtent = yExtent(~isnan(min(D,[],2,'omitnan')));
cnt = 1; % increase or decrease according to the nature of the problem
while true
% Same ideas as above, so no explanations:
xExtent = linspace(xExtent(1),xExtent(end),20);
yExtent = linspace(yExtent(1),yExtent(end),20).';
[XX,YY] = meshgrid(xExtent,yExtent);
D = reshape(min(pdist2([XX(:),YY(:)],[x(:),y(:)]),[],2),size(XX));
D(D<prctile(D(:),95)) = NaN;
I = find(D == max(D(:)));
xBest = XX(I);
yBest = YY(I);
if nanvar(D(:)) < 1E-10 || cnt == 10
R = D(I);
break
end
xExtent = (1+[-1 +1]*10^-cnt)*xBest;
yExtent = (1+[-1 +1]*10^-cnt)*yBest;
cnt = cnt+1;
end
% Finally:
% rectangle('Position',[xBest-R,yBest-R,2*R,2*R],'Curvature',[1 1],'EdgeColor','r');
The result I'm getting for Ander's example data is [x,y,r] = [0.7832, 2.0694, 0.7815] (which is the same). The execution time is about half of Ander's solution.
Here are the intermediate plots:
Contour of the largest (clear) distance from a point to the set of all provided points:
After considering distance from the boundary, keeping only the top 5% of distant points, and considering only the region which contains the largest distance (the piece of surface represents the kept values):
And finally:
You can use bwdist from Image Processing Toolbox to compute the distance transform of the image. This can be regarded as a method to create voronoi diagram that well explained in #AnderBiguri's answer.
img = imread('AbmxL.jpg');
%convert the image to a binary image
points = img(:,:,3)<200;
%compute the distance transform of the binary image
dist = bwdist(points);
%find the circle that has maximum radius
radius = max(dist(:));
%find position of the circle
[x y] = find(dist == radius);
imshow(dist,[]);
hold on
plot(y,x,'ro');
The fact that this problem can be solved using a "direct search" (as can be seen in another answer) means one can look at this as a global optimization problem. There exist various ways to solve such problems, each appropriate for certain scenarios. Out of my personal curiosity I have decided to solve this using a genetic algorithm.
Generally speaking, such an algorithm requires us to think of the solution as a set of "genes" subject to "evolution" under a certain "fitness function". As it happens, it's quite easy to identify the genes and the fitness function in this problem:
Genes: x , y, r.
Fitness function: technically, maximum area of circle, but this is equivalent to the maximum r (or minimum -r, since the algorithm requires a function to minimize).
Special constraint - if r is larger than the euclidean distance to the closest of the provided points (that is, the circle contains a point), the organism "dies".
Below is a basic implementation of such an algorithm ("basic" because it's completely unoptimized, and there is lot of room for optimizationno pun intended in this problem).
function [x,y,r] = q42806059b(cloudOfPoints)
% Problem setup
if nargin == 0
rng(1)
cloudOfPoints = rand(100,2)*5; % equivalent to Ander's initialization.
end
%{
figure(); plot(cloudOfPoints(:,1),cloudOfPoints(:,2),'.w'); hold on; axis square;
set(gca,'Color','k'); plot(0.7832,2.0694,'ro'); plot(0.7832,2.0694,'r*');
%}
nVariables = 3;
options = optimoptions(#ga,'UseVectorized',true,'CreationFcn',#gacreationuniform,...
'PopulationSize',1000);
S = max(cloudOfPoints,[],1); L = min(cloudOfPoints,[],1); % Find geometric bounds:
% In R2017a: use [S,L] = bounds(cloudOfPoints,1);
% Here we also define distance-from-boundary constraints.
g = ga(#(g)vectorized_fitness(g,cloudOfPoints,[L;S]), nVariables,...
[],[], [],[], [L 0],[S min(S-L)], [], options);
x = g(1); y = g(2); r = g(3);
%{
plot(x,y,'ro'); plot(x,y,'r*');
rectangle('Position',[x-r,y-r,2*r,2*r],'Curvature',[1 1],'EdgeColor','r');
%}
function f = vectorized_fitness(genes,pts,extent)
% genes = [x,y,r]
% extent = [Xmin Ymin; Xmax Ymax]
% f, the fitness, is the largest radius.
f = min(pdist2(genes(:,1:2), pts, 'euclidean'), [], 2);
% Instant death if circle contains a point:
f( f < genes(:,3) ) = Inf;
% Instant death if circle is too close to boundary:
f( any( genes(:,3) > genes(:,1:2) - extent(1,:) | ...
genes(:,3) > extent(2,:) - genes(:,1:2), 2) ) = Inf;
% Note: this condition may possibly be specified using the A,b inputs of ga().
f(isfinite(f)) = -genes(isfinite(f),3);
%DEBUG:
%{
scatter(genes(:,1),genes(:,2),10 ,[0, .447, .741] ,'o'); % All
z = ~isfinite(f); scatter(genes(z,1),genes(z,2),30,'r','x'); % Killed
z = isfinite(f); scatter(genes(z,1),genes(z,2),30,'g','h'); % Surviving
[~,I] = sort(f); scatter(genes(I(1:5),1),genes(I(1:5),2),30,'y','p'); % Elite
%}
And here's a "time-lapse" plot of 47 generations of a typical run:
(Where blue points are the current generation, red crosses are "insta-killed" organisms, green hexagrams are the "non-insta-killed" organisms, and the red circle marks the destination).
I'm not used to image processing, so it's just an Idea:
Implement something like a gaussian filter (blur) which transforms each particle (pixels) to a round gradiant with r=image_size (all of them overlapping). This way, you should get a picture where the most white pixels should be the best results. Unfortunately, the demonstration in gimp failed because the extreme blurring made the dots disappearing.
Alternatively, you could incrementelly extend all existing pixels by marking all neighbour pixels in an area (example: r=4), the pixels left would be the same result (those with the biggest distance to any pixel)

How do I find exact rest points?

I have a displacement and a time data of a movement of an object.
The object oscillates around zero. That is, first - it gets set into motion by a small amount of force, then it comes to rest. again, a little force is applied and object gets set into motion.
I have found out the velocity and acceleration using
V= [0 ; diff(disp) ./ diff(times)];
A= [0; diff(V) ./ diff(times)];
I was thinking of finding points where velocity is zero. But i guess there are more than required such instances. Find the graph below:
velocity plot
I am interested in only circles time values. Is there a way to get these?
I observe a pattern
velocity increases then decreases by almost same amount.
Then due to friction, it crosses zero by a smaller amount and again becomes negative
finally comes to rest, but a very little velocity is still present.
It is this touch point to zero that I want. Then again force is applied and the same cycle repeats.
Pl note that I do not have a time of when force is applied. Otherwise there was nothing to be done.
Also, I did plot the acceleration. But is seems so useless..
I am using matlab.
Here's one way to find approximate zeros in gridded data:
% some dummy synthetic data
x = linspace(0, 10, 1e3);
y = exp(-0.3*x) .* sin(x) .* cos(pi*x);
% its derivative (presumably your "acceleration")
yp = diff(y) ./ diff(x);
% Plot data to get an overview
plot(x,y), hold on
% Find zero crossings (product of two consecutive data points is negative)
zero_x = y(1:end-1) .* y(2:end) < 0;
% Use derivative for linear interpolation between those points
x_cross = x(zero_x) + y(zero_x)./yp(zero_x);
% Plot those zeros
plot(x_cross, zeros(size(x_cross)), 'ro')
Result:
It is then up to you to select which zeros you need, because I could not understand from the question what made those points in the circles so special...
The resting points you asked have the following property:
dx / dt = v = 0
d^2 x / dt^2 = a = 0 # at the instance that the object becomes v = 0, there is no force on it.
So you may want to check also the second formula to filter the resting points.

Create a spiral between two cartesian points in MATLAB

Perhaps this is a basic question but I haven't been able to find anything specifically like this and I'm wondering how to do it in the best way.
I have two sets of points (x1,y1,z1) and (x2,y2,z2) and I have converted them into polar coordinates. I would like to create a counter-clockwise helix of decreasing radius to reach the second point.
I would also like to specify how many revolutions it takes.
All of the examples I have seen are two points on the x axis and going clockwise.
Any suggestions would be very much appreciated!
Thanks.
This example code generates a counter clockwise spiral from p1 to p2 that isn't on x-axis and you can specify the number of revolutions. However it is in 2D and initial points are in cartesian coordinates. I'm not sure how to do it in 3D but I hope this can help you with the offsetting and the counter-clockwising.
%random 2d points
p1 = [3,5];
p2 = [1,4];
%radius of first point to second
r = norm(p1-p2);
%angle between two point wrt the y-axis
theta_offset = tan((p1(2)- p2(2))/(p1(1)-p2(1)));
rez = 150; % number of points
rev = 5; % number of revolutions
t = linspace(0,r,rez); %radius as spiral decreases
theta = linspace(0,2*pi*rev,rez) + theta_offset; %angle as spiral decreases
x = cos(theta).*t+p2(1);
y = sin(theta).*t+p2(2);
plot(x,y)

Documenting matlab code for generating random shapes

Can you please help me document this Matlab code that is supposed to produce random shapes?? The wiggliness of the shapes is supposed to be controlled by the variable degree...
But how the rho (radius values) are produced... I can't really get it....
degree = 5;
numPoints = 1000;
blobWidth = 5;
theta = 0:(2*pi)/(numPoints-1):2*pi;
coeffs = rand(degree,1);
rho = zeros(size(theta));
for i = 1:degree
rho = rho + coeffs(i)*sin(i*theta);
end
phase = rand*2*pi;
[x,y] = pol2cart(theta+phase, rho+blobWidth);
plot(x,y)
axis equal
set(gca,'Visible','off')
theta = 0:(2*pi)/(numPoints-1):2*pi;
So this is just a vector of angles in a revolution, if you plot this theta against a constant rho (after calling pol2cart) you will get a circle:
r = ones(size(theta));
[x,y] = pol2cart(theta, r);
plot(x,y)
axis equal
This should be obvious if you understand what pol2cart does because you have a series of all the angles in a circle and a constant radius for all of them. If you don't understand that (i.e. polar coordinates) then that's a very basic mathematical concept you need to go and read up on your own before trying to understand this code.
OK so now a circle in cartesian coords is just a line in polar coords (i.e. plot(theta, r) noting that the horizontal axis now represents angle and the vertical represents radius). So if we want to randomly mess up our circle, we could randomly mess up our line. Using sin does this in a nice smooth way. Adding random frequencies of many sin waves adds less and less predictable "jitter". I think it would help you to understand if you add the following line to your code:
rho = zeros(size(theta));
hold all
for i = 1:degree
rho = rho + coeffs(i)*sin(i*theta);
plot(theta, rho)
end
and contrast this to (be sure to close your figure window before running this)
rho = zeros(size(theta));
hold all
for i = 1:degree
rho = rho + coeffs(i)*sin(i*theta);
plot(theta, coeffs(i)*sin(i*theta))
end
The second one shows you the different frequencies of sin waves used and the first shows how these sum to create unpredictable wavy lines. Now think of the pol2rect function as bending these lines around to make a "circle". If the line is dead straight you get a perfect circle, if it's wavy you get a "wavy" circle.
degree in your code just controls how many sin waves to add up.
finally phase = rand*2*pi; just randomly rotates your shape after it has been created.
Well, this was an amazing piece of code! But regarding rho. What is done is that you have a circle with base radius of 5 (blobwidth) and then you have a random offset coeffs. Then the offset is added to rho in rho = rho + coeffs(i)*sin(i*theta);. This means that the first loop an offset is added to the circle with frequency 1Hz. This then yields a constant offset. The next loop the frequency increases to 2Hz. Then the offset will be added to every second point and the offset may be negative as well. Then it goes on like this. Finally the coordinate is transformed to polar.
A few comments though. The most readable and the easiest way to create theta is to use linspace. And also, since rho is overwritten in the loop, you may as well define it just as rho = 0;

Connect points and compute area

thats my first post, so please be kind.
I have a matrix with 3~10 coordinates and I want to connect these points to become a polygone with maximum size.
I tried fill() [1] to generate a plot but how do I calculate the area of this plot? Is there a way of converting the plot back to an matrix?
What would you reccomend me?
Thank you in advance!
[1]
x1 = [ 0.0, 0.5, 0.5 ];
y1 = [ 0.5, 0.5, 1.0 ];
fill ( x1, y1, 'r' );
[update]
Thank you for your answer MatlabDoug, but I think I did not formulate my question clear enough. I want to connect all of these points to become a polygone with maximum size.
Any new ideas?
x1 = rand(1,10)
y1 = rand(1,10)
vi = convhull(x1,y1)
polyarea(x1(vi),y1(vi))
fill ( x1(vi), y1(vi), 'r' );
hold on
plot(x1,y1,'.')
hold off
What is happening here is that CONVHULL is telling us which verticies (vi) are on the convex hull (the smallest polygon that encloses all the points). Knowing which ones are on the convex hull, we ask MATLAB for the area with POLYAREA.
Finally, we use your FILL command to draw the polygon, then PLOT to place the points on there for confirmation.
I second groovingandi's suggestion of trying all polygons; you just have to be sure to check the validity of the polygon (no self-intersections, etc).
Now, if you want to work with lots of points... As MatlabDoug pointed out, the convex hull is a good place to start. Notice that the convex hull gives a polygon whose area is the maximum possible. The problem, of course, is that there could be points in the interior of the hull that are not part of the polygon. I propose the following greedy algorithm, but I am not sure if it guarantees THE maximum area polygon.
The basic idea is to start with the convex hull as a candidate final polygon, and carve out triangles corresponding to the unused points until all the points belong to the final polygon. At each stage, the smallest possible triangle is removed.
Given: Points P = {p1, ... pN}, convex hull H = {h1, ..., hM}
where each h is a point that lies on the convex hull.
H is a subset of P, and it is also ordered such that adjacent
points in the list of H are edges of the convex hull, and the
first and last points form an edge.
Let Q = H
while(Q.size < P.size)
% For each point, compute minimum area triangle
T = empty heap of triangles with value of their area
For each P not in Q
For each edge E of Q
If triangle formed by P and E does not contain any other point
Add triangle(P,E) with value area(triangle(P,E))
% Modify the current polygon Q to carve out the triangle
Let t=(P,E) be the element of T with minimum area
Find the ordered pair of points that form the edge E within Q
(denote them Pa and Pb)
Replace the pair (Pa,Pb) with (Pa,E,Pb)
Now, in practice you don't need a heap for T, just append the data to four lists: one for P, one for Pa, one for Pb, and one for the area. To test if a point lies within a triangle, you only need to test each point against the lines forming the sides of the triangle, and you only need to test points not already in Q. Finally, to compute the area of the final polygon, you can triangulate it (like with the delaunay function, and sum up the areas of each triangle in the triangulation), or you can find the area of the convex hull, and subtract out the areas of the triangles as you carve them out.
Again, I don't know if this greedy algorithm is guaranteed to find the maximum area polygon, but I think it should work most of the time, and is interesting nonetheless.
You said you only have 3...10 points to connect. In this case, I suggest you just take all possible combinations, compute the areas with polyarea and take the biggest one.
Only if your number of points increases or if you have to compute it frequently so that compuation time matters, it's worth investing some time in a better algorithm. However I think it's difficult to come up with an algorithm and prove its completeness.
Finding the right order for the points is the hard part, as Amro commented. Does this function suffice?
function [idx] = Polyfy(x, y)
% [idx] = Polyfy(x, y)
% Given vectors x and y that contain pairs of points, find the order that
% joins them into a polygon. fill(x(idx),y(idx),'r') should show no holes.
%ensure column vectors
if (size(x,1) == 1)
x = x';
end
if (size(y,1) == 1)
y = y';
end
% vectors from centroid of points to each point
vx = x - mean(x);
vy = y - mean(y);
% unit vectors from centroid towards each point
v = (vx + 1i*vy)./abs(vx + 1i*vy);
vx = real(v);
vy = imag(v);
% rotate all unit vectors by first
rot = [vx(1) vy(1) ; -vy(1) vx(1)];
v = (rot*[vx vy]')';
% find angles from first vector to each vector
angles = atan2(v(:,2), v(:,1));
[angles, idx] = sort(angles);
end
The idea is to find the centroid of the points, then find vectors from the centroid to each point. You can think of these vectors as sides of triangles. The polygon is made up the set of triangles where each vector is used as the "left" and "right" only once, and no vectors are skipped. This boils down to ordering the vectors by angle around the centroid.
I chose to do this by normalizing the vectors to unit length, choosing one of them as a rotation vector, and rotating the rest. This allowed me to simply use atan2 to find the angles. There's probably a faster and/or more elegant way to do this, but I was confusing myself with trig identities. Finally, sorting those angles provides the correct order for the points to form the desired polygon.
This is the test function:
function [x, y] = TestPolyArea(N)
x = rand(N,1);
y = rand(N,1);
[indexes] = Polyfy(x, y);
x2 = x(indexes);
y2 = y(indexes);
a = polyarea(x2, y2);
disp(num2str(a));
fill(x2, y2, 'r');
hold on
plot(x2, y2, '.');
hold off
end
You can get some pretty wild pictures by passing N = 100 or so!