Point inside triangle(s)?
27 Ansichten (letzte 30 Tage)
Ältere Kommentare anzeigen
Hi all, here's a little puzzle that I've confused myself over for the last hour... it's time to clear my head.
I have 4 triangles and one query point. I want to determine which of these triangles contain my query point. A secondary goal will be to identify which triangles have an edge that crosses the query point. This is basically inpolygon(), but for multiple-geometries-per-point, rather than multiple-points-per-geometry (and I'm sure I can make something more efficient than inpolygon since I'm only dealing with triangles).
I thought I'd go about it this way:
- Each triangle has 3 edges.
- Work around the triangles (starting at edge A), get the equation of the line (ie, y=mx+b) that makes up that edge.
- Plug my query point into that equation (ie, result = mx+b-y)
- If result is positive, my query point is below that edge.
- If result is negative, my query point is above that edge.
- If result is zero, my query point is coincident with that edge
- If my query point is above one line but below the other two, it should be inside the triangle
All this is neatly encapsulated in the (commented... running) code below:
qPtXY = [-0.9414 -0.2134];
candVerts = [
-0.9360 -0.2325
-0.9511 -0.1625
-0.9511 -0.1681
-0.9612 -0.1918
-0.9511 -0.1625
-0.9239 -0.2012
-0.9444 -0.2616
-0.9450 -0.2000
-0.9239 -0.2012
-0.9444 -0.2616
-0.9612 -0.1918
-0.9511 -0.1625];
vertxsA = candVerts(1:3:end,:); vertxsB = candVerts(2:3:end,:); vertxsC = candVerts(3:3:end,:);
% Work out the equation y = mx + b, for the lines from vert1 to vert2
% First, get m as ydiff/xdiff
edgeVecs = vertxsB - vertxsA;
m = edgeVecs(:,2)./edgeVecs(:,1);
% Next, get b as b=y-mx
b = vertxsA(:,2) - vertxsA(:,1).*m;
% Now get the "other side" of 0 = mx + b - y, using the query pt's xy
testA = m*qPtXY(1) + b -qPtXY(2);
% Do the same for the second edgeB
edgeVecs = vertxsC - vertxsB;
m = edgeVecs(:,2)./edgeVecs(:,1);
b = vertxsB(:,2) - vertxsB(:,1).*m;
testB = m*qPtXY(1) + b -qPtXY(2);
% Do the same for edgeC
edgeVecs = vertxsA - vertxsC;
m = edgeVecs(:,2)./edgeVecs(:,1);
b = vertxsC(:,2) - vertxsC(:,1).*m;
testC = m*qPtXY(1) + b -qPtXY(2);
% If the qPt is "above" one line and "below" two lines, it should be
% inside the triangle
testSet = [testA testB testC];
inMask = sum(testSet>0,2)==1 & sum(testSet<0,2)==2;
And the result can be visualised with the following code. Bold lines if the triangle encloses the query point, thin lines if it does not:
figure, plot(qPtXY(1),qPtXY(2),'r*'), hold on
triaCols = lines(length(testC));
lineWidths = 4*inMask + 0.5;
for ptNo = 1:length(inMask)
pts3 = [vertxsA(ptNo,:); vertxsB(ptNo,:); vertxsC(ptNo,:)];
plot(pts3([1:end 1],1), pts3([1:end 1],2), '.b-','Color',triaCols(ptNo,:),'LineWidth',lineWidths(ptNo))
waitforbuttonpress
end
It seemed to work, up until the last triangle which obviously doesn't enclose the query point but still meets my criteria for doing so. Obviously my criteria is wrong (or there's a typo somewhere), but I've just gotten myself in a muddle about it. Can someone pick out the errors in my assumptions?
I would also be open to alternative approaches (such as using dot products rather than line equations... that may overcome the issue my method has with vertical edges).
For context, I want to do things in an efficient way so bonus votes for keeping things vectorised. This code will run for between 2 to 20 triangles each time, but looped many many times.
Thanks, Sven.
1 Kommentar
Antworten (5)
Image Analyst
am 10 Aug. 2012
So you mean like it might take 2 seconds instead of 2 milliseconds? Have you actually tried inpolygon to see how long it would take? I did it (used inpolygon) to check if a point was inside of 10 thousand triangles and it took only 0.55 seconds. What kind of speed must you have to declare success? Copy, paste, and run the following demo.
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables.
workspace; % Make sure the workspace panel is showing.
format longg;
format compact;
fontSize = 20;
qPtXY = [0.7 0.8];
numberOfTriangles = 10000;
trianglesX = rand(numberOfTriangles, 3);
trianglesY = rand(numberOfTriangles, 3);
% START OF ALGORITHM
tic;
itsInside = false(numberOfTriangles, 1);
for t = 1 : numberOfTriangles
oneTriangleX = [trianglesX(t, 1), trianglesX(t, 2), trianglesX(t, 3), trianglesX(t, 1)];
oneTriangleY = [trianglesY(t, 1), trianglesY(t, 2), trianglesY(t, 3), trianglesY(t, 1)];
if inpolygon(qPtXY(1), qPtXY(2), oneTriangleX, oneTriangleY)
itsInside(t) = true;
end
end
toc
% ALL DONE!
% Now plot them all
% First set up the plots.
figure;
subplot(1, 2, 1);
plot(qPtXY(1), qPtXY(2), 'r*');
hold on;
grid on;
axis on;
xlim([0 1]);
ylim([0 1]);
title('Enclosing Triangles', 'FontSize', fontSize);
subplot(1, 2, 2);
plot(qPtXY(1), qPtXY(2), 'r*');
hold on;
grid on;
axis on;
xlim([0 1]);
ylim([0 1]);
title('Non-Enclosing Triangles', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
% Give a name to the title bar.
set(gcf,'name','Demo by ImageAnalyst','numbertitle','off')
drawnow;
% Now draw individual triangles, in groups of 100.
inCount = 0;
outCount = 0;
for t = 1 : numberOfTriangles
oneTriangleX = [trianglesX(t, 1), trianglesX(t, 2), trianglesX(t, 3), trianglesX(t, 1)];
oneTriangleY = [trianglesY(t, 1), trianglesY(t, 2), trianglesY(t, 3), trianglesY(t, 1)];
if itsInside(t)
subplot(1, 2, 1);
plot(oneTriangleX, oneTriangleY, 'g-');
if mod(t, 100)
inCount = inCount + 1;
inPlotTitle = sprintf('Showing %d enclosing triangles', inCount);
end
else
subplot(1, 2, 2);
plot(oneTriangleX, oneTriangleY, 'b-');
if mod(t, 100)
outCount = outCount + 1;
outPlotTitle = sprintf('Showing %d non-enclosing triangles', outCount);
end
outCount = outCount + 1;
end
if mod(t, 100) == 0
subplot(1, 2, 1);
title(inPlotTitle, 'FontSize', fontSize);
subplot(1, 2, 2);
title(outPlotTitle, 'FontSize', fontSize);
promptMessage = sprintf('Do you want to Continue displaying triangles,\nor Quit?');
titleBarCaption = 'Continue?';
button = questdlg(promptMessage, titleBarCaption, 'Continue', 'Quit', 'Continue');
if strcmp(button, 'Quit')
return;
end
end
end
0 Kommentare
Anton Semechko
am 20 Aug. 2012
Bearbeitet: Anton Semechko
am 20 Aug. 2012
Hi Sven,
I have encountered this problem many times in the past. I am aware of at least two approaches to solve it. The first one is outlined in this paper: Fast, Minimum Storage Ray-Triangle Intersection (Moller & Thumbore, 1997). If the point lies in the same plane as the triangle, the problem can be reduced to a 2D case. Let {x_ji}, i=1,2,3 be the vertices of the triangle T_j, such that x_ji is an element of R2 space. Using barycentric coordinates an arbitrary point inside the triangle can be defined as: x=x_j1+u*(x_j2-x_j1)+v*(x_j3-x_j1), where 0>=u,v>=1 and u+v<=1. So to check if some point p is inside T_j, solve x-x_j1=[(x_j2-x_j1)+(x_j3-x_j1)][u;v], for u and v and check that 0>=u,v>=1 and u+v<=1.
The second approach is a little different. Suppose that d_12, d_23, and d_31 are the direction vectors perpendicular to the triangle edges, lying in the same plane as the triangle. For example, let d_12 be perpendicular to the edge passing trough vertices x_1 and x_2, and let the remaining two direction vectors be defined similarly. Then to test if an arbitrary point p is inside the triangle, compute three dot products:
dot(p-x_1,d_12)
dot(p-x_2,d_23)
dot(p-x_3,d_31)
If all three are negative then the point is inside the triangle.
I haven't read any of the replies above. So I apologize if the is any redundancy is my response.
Hope this helps.
Azzi Abdelmalek
am 10 Aug. 2012
Bearbeitet: Azzi Abdelmalek
am 10 Aug. 2012
instead "waitforbuttonpress" use "pause"; i executed your code, it works with "pause"
1 Kommentar
Sven
am 10 Aug. 2012
Bearbeitet: Sven
am 10 Aug. 2012
7 Kommentare
Matt Kindig
am 10 Aug. 2012
Hi Sven,
I have used an entry on the File Exchange named 'inpoly' as a replacement for inpolygon. It is MUCH faster than inpolygon in my experience; also it is able to detect whether a point is on the edge. I would image that looping through all triangles with this function would be fairly fast.
Matt
Siehe auch
Kategorien
Mehr zu Graphics Objects finden Sie in Help Center und File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!