Zig-Zag Triangle Traversal Implementation - algorithm

I was trying to follow "On the Hardware Implementation of Triangle Traversal Algorithms for Graphics Processing" (Royer, Ituero, Lopez-Vallejo, & Barrio) (page 4) to implement Zig-Zag traversal algorithm for triangle triversal/rasterization. However, the explanation in the paper is counter-intuitive for me and I was not able to make it work.
I tried to implement a finite state machine but I can't quite figure out the exact states. For now, I'm having (direction, e_1, e_2, e_3) where e_n represent edge test output for each edge. Pseudo code:
if (right, true, true, true):
x++; // Move towards right
else if (left, true, true, true):
x--; // Move towards left
else:
// This is where I stuck. There should be two cases where in one of them
// y goes down and x doesn't change direction, in the other case x simply
// flips its direction. But I wasn't able to figure it out.
Any help would be appreciated!
Edit: My effort so far:
While the edge test is working correctly, only few parts of the graph was rasterized.
/// Zig Zag (not working)
int top_row = floor(fmin(y0, fmin(y1, y2)));
int bot_row = floor(fmax(y0, fmax(y1, y2)));
if (y0 > y1) {
swap(x0, x1); swap(y0, y1);
}
if (y0 > y2) {
swap(x0, x2); swap(y0, y2);
}
if (y1 > y2) {
swap(x1, x2); swap(y1, y2);
}
assert(top_row == floor(y0));
assert(bot_row == floor(y2));
bool direction = true;
bool changed = false;
int x = floor(x0); int y = floor(y0);
while (y <= bot_row) {
bool e1, e2, e3;
e1 = edge_test((float)x+0.5, (float)y+0.5, x0, y0, x1, y1) < 0.0f;
e2 = edge_test((float)x+0.5, (float)y+0.5, x1, y1, x2, y2) < 0.0f;
e3 = edge_test((float)x+0.5, (float)y+0.5, x2, y2, x0, y0) < 0.0f;
if ((e1 == e2) && (e2 == e3)) {
if ( x < 0 || x >= width ) continue;
if ( y < 0 || y >= height ) continue;
samplebuffer[y][x].fill_pixel(color);
if (direction) x++;
else x--;
} else if (changed) {
y++;
changed = false;
} else {
direction = !direction;
changed = true;
if (direction) x++;
else x--;
}
}

How I see state machine:
Dir = +1 / -1
State 0: (moving inside)
EdgeTest:
0 => State 1 ; y++
1 => State 0 ; x = x + Dir
State 1: (outside)
EdgeTest:
0 => State 3 ; Dir = - Dir
1 => State 2 ;
State 2: (inside moving the same dir)
EdgeTest:
0 => State 3 ; Dir = - Dir
1 => State 2 ; x= x + Dir
State 3: (outside)
EdgeTest:
0 => State 3 ; x = x + Dir
1 => State 0 ;

Related

Does two sets of three numbers have at least two numbers in common?

I just had to write a function that seemed simple to write, but when I actually did it, it turned out way gorier than I had expected. It's really bugging me, I feel like there's a better solution, but my brain is going crazy trying to think of it, so therefore I'm turning to you fine folks.
Basically, I have 2 triangles, and I want to know if they share a common edge. The triangles are indexed by their vertices (i.e. their vertices is just an index to an array containing the actual coordinates), so it comes down to finding if two sets of three numbers have two numbers in common. I.e. triangles (1,2,3) and (3,1,5) do share an edge, the (1,3) edge. However, triangles (1,2,3) and (1,5,6) does not share an edge (only a vertex) and neither does (1,2,3) and (4,5,6).
How would you write this "two numbers in common function"? You can assume all values inside each set are distinct (i.e. (1, 1, 2) is not going to be an input) and you can also assume that two sets don't equal each other (i.e. I'm not going to compare (1,2,3) and (1,3,2), because those two are the same triangle). However, no assumptions can be made regarding order, they are not sorted or anything like that.
This is basically what I came up with (assuming the sets are (x0, x1, x2) and (y0, y1, y2)):
// If the x0 is equal to any of (y0, y1, y2), make sure at least one of (x1, x2)
// is equal to one of the y numbers
if (x0 == y0) {
return x1 == y1 || x1 == y2 || x2 == y1 || x2 == y2;
} else if (x0 == y1) {
return x1 == y0 || x1 == y2 || x2 == y0 || x2 == y2;
} else if (x0 == y2) {
return x1 == y0 || x1 == y1 || x2 == y0 || x2 == y1;
} else {
// if x0 is not equal to any of (y0, y1, y2), then x1 and x2 both have
// to be equal to two of the y numbers.
return (x1 == y0 && x2 == y1)
|| (x1 == y0 && x2 == y2)
|| (x1 == y1 && x2 == y0)
|| (x1 == y1 && x2 == y2)
|| (x1 == y2 && x2 == y0)
|| (x1 == y2 && x2 == y1);
}
but it feels so gory to me! So many branches and such long boolean statements! I feel like i'm missing an obvious easy solution, and it's driving me insane.
In addition, this happens in an inner loop in a very performance sensitive application, so performance (branching, arithmetic, whatever) matters.
BTW, the code I'm writing is C#, but the question is the same in more or less any imperative language.
EDIT:
I put together a quick benchmark (here's the code) with the suggestions so far. Here are the results (running it at a million random pairs of triangles):
Original method result: 7035, time elapsed in ms: 8.6252
qwertyman method result: 7035, time elapsed in ms: 8.2537
midjji method result: 7035, time elapsed in ms: 8.7984
Single HashSet method result: 7035, time elapsed in ms: 184.4984
Many HashSets method result: 7035, time elapsed in ms: 190.5785
The numbers remain fairly consistent run to run, with #qwertyman's method always being a bit faster than my original version or #midjii's. It also has the advantage of being the cleanest and nicest of them all, so I'm going to go with that one.
I was actually a bit surprised that the "Many HashSets" was so close to "Single HashSet", I would have thought constructing a million HashSets would have a bigger overhead than around 16 milliseconds (though this obviously doesn't count the increased pressure on the garbage collector), though they're both obviously far behind the other methods.
Thanks for the help!
You can do something like this:
int n = 0;
// check if x0 is among the values in the second set
if (x0 == y0 || x0 == y1 || x0 == y2) {
n++;
}
// check if x1 is among the values in the second set
if (x1 == y0 || x1 == y1 || x1 == y2) {
n++;
}
// check if x2 is among the values in the second set
if (x2 == y0 || x2 == y1 || x2 == y2) {
n++;
}
return n >= 2;
This relies on the fact that (as you mentioned) the numbers in each set are distinct, resulting in a simpler logic.
If you are using C, you could write it shorter:
int n = 0;
n += x0 == y0 || x0 == y1 || x0 == y2;
n += x1 == y0 || x1 == y1 || x1 == y2;
n += x2 == y0 || x2 == y1 || x2 == y2;
return n >= 2;
I would use:
...
{
//ti= xi in y
bool t0= (x0==y0) ||(x0==y1)|| (x0==y2);
bool t1= (x1==y0) ||(x1==y1)|| (x1==y2);
bool t2= (x2==y0) ||(x2==y1)|| (x2==y2);
return (t0 && t1) || (t0 && t2) || (t1 && t2);
}
Mostly because I think its easier to read.
Performance-wise it is rather likely that with the right settings it should be as fast. Compilers are fantastic at optimizing self enclosed, no side effect, logical statements and use lazy evaluation for bool(assuming nothing silly has been done to ==).

Matlab image rotation

I am new to image processing, I have implemented a code for image warping and it works perfectly. I would like to improve the code by using linear interpolation to rotate the image WITHOUT using the built-in function (interp). Here is my code:
close all;
clear all;
img = 'woods.jpg';
input_image =double(imread(img))./255;
H=size(input_image,1);
W=size(input_image,2);
th=pi/4;
s0 = 2;
s1 = 2;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*R;
N = inv(M);
output_image=zeros(H,W,3);
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
a = round(a);
b = round(b);
if (a>0 && a<=W && b>0 && b<=H)
output_image(j,i,:)=input_image(b,a,:);
end
end
end
imgshow(output_image);
Check the following solution:
I verified implementation by comparing to Matalb build in function [imwarp][1].
close all;
clear all;
img = 'peppers.png';
input_image =double(imread(img))./255;
H=size(input_image,1); % height
W=size(input_image,2); % width
th=pi/4;
s0 = 2;
s1 = 2;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*RST;
N = inv(M);
output_image=zeros(H,W,3);
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
%Nearest neighbor
%a = round(a);
%b = round(b);
x1 = floor(a);
y1 = floor(b);
x2 = x1 + 1;
y2 = y1 + 1;
%Bi-linear interpolation ilsutration:
%Image coordinates style (horizontal index first)
%
%(x1,y1) | (x2,y1)
% | 1-dy
% 1-dx | dx
% ------(a,b)------------
% |
% |
% |
% | dy
% |
% |
%(x1,y2) | (x2,y2)
if ((x1 >= 1) && (y1 >= 1) && (x2 <= W) && (y2 <= H))
%Load 2x2 pixels
i11 = input_image(y1, x1, :); %Top left pixel
i21 = input_image(y2, x1, :); %Bottom left pixel
i12 = input_image(y1, x2, :); %Top right pixel
i22 = input_image(y2, x2, :); %Bottom right pixel
%Interpolation wieghts
dx = x2 - a;
dy = y2 - b;
%Bi-lienar interpolation
output_image(j, i, :) = i11*dx*dy + i21*dx*(1-dy) + i12*(1-dx)*dy + i22*(1-dx)*(1-dy);
end
end
end
imshow(output_image);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Verify implementation by comparing with Matalb build in function imwarp:
tform = affine2d(M');
ref_image = imwarp(input_image, tform, 'OutputView', imref2d(size(input_image)), 'Interp', 'linear');
figure;imshow(ref_image)
figure;imshow(output_image - ref_image)
max_diff = max(abs(output_image(:) - ref_image(:)));
disp(['Maximum difference from imwarp = ', num2str(max_diff)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Result:
Remark:
I missed the following execelnt post that resizes an image using bilinear interpolation (bilinear interpolation is explained better):
Resize an image with bilinear interpolation without imresize

Algorithm to find all line segment intersections given n lines

I'm looking for a algorithm to find all the intersection points given n line segments.
Below is the pseudo code from http://jeffe.cs.illinois.edu/teaching/373/notes/x06-sweepline.pdf
The input S[1 .. n] is an array of line
segments.
label[i] is the label of the ith leftmost endpoint.
sort the endpoints of S from left to right
create an empty label sequence
for i ← 1 to 2n
line ← label[i]
if isLeftEndPoint[i]
Insert(line)
if Intersect(S[line], S[Successor(line)])
return TRUE
if Intersect(S[line], S[Predecessor(line)])
return TRUE
else
if Intersect(S[Successor(line)], S[Predecessor(line)])
return TRUE
Delete(label[i])
return FALSE
Apply the algorithm to the line set below, only one intersection point is checked. What should I do to know the existence of the other 2 intersection points?
line[1] enters
line[2] enters, intersection between line[1] and line[2] is checked.
line[3] enters, intersection between line[2] and line[3] is checked.
line[4] enters, intersection between line[4] and line[1] is checked. Intersection A is found.
line[4] leaves, nothing is checked.
line[1] leaves, nothing is checked.
line[2] leaves, nothing is checked.
line[3] leaves, nothing is checked.
Standard line equation
Ax+By=C
The slope(m) of a line defined by the standard line of equation is
m = -(A/B)
Point-slope line equation
y-y1=m(x-x1)
Substituting m = (-A/B) in the point-slope line equation
y2-y1 = (A/-B)*(x2-x1)
(y2-y1)/(x2-x1) = A/-B
thus:
A = y2-y1
B = x1-x2
C = Ax+By
x = (C-By)/A
y = (C-Ax)/B
Given two lines with equation
A1x1+B1y1=C1 and A2x2+B2y2=C2.
Then the point of intersection between the lines is specified
by the points that make A1x+B1y-C1 = A2x+B2y-C2
A1x+B1y=C1
A2x+B2y=C2
A1B2x+B1B2y=B2C1 (multiply the first equation by B2)
A1B2x+B1B2y-B2C1=0
A2B1x+B1B2y=B1C2 (multiply the second equation by B1)
A2B1x+B1B2y-B1C2=0
Equating the two equations
A1B2x+B1B2y-B2C1=A2B1x+B1B2y-B1C2
A1B2x+B1B2y-B2C1-A2B1x-B1B2y+B1C2=0
A1B2x-B2C1-A2B1x+B1C2=0
A1B2x-A2B1x=B2C1-B1C2
x(A1B2-A2B1)=B2C1-B1C2
x = (B2C1-B1C2)/A1B2-A2B1
A1x+B1y=C1
A2x+B2y=C2
A1A2x+A2B1y=A2C1 (multiply the first equation by A2)
A1A2x+A2B1y-A2C1=0
A1A2x+A1B2y=A1C2 (multiply the second equation by A1)
A1A2x+A1B2y-A1C2=0
Equating the two equations
A1A2x+A2B1y-A2C1=A1A2x+A1B2y-A1C2
A1A2x+A2B1y-A2C1-A1A2x-A1B2y+A1C2=0
A1C2-A2C2=A1B2y-A2B1y
A1B2y-A2B1y=A1C2-A2C2
y(A1B2-A2B1)=A1C2-A2C1
y(A1B2-A2B1)=A1C2-A2C1
y = (A1C2-A2C1)/(A1B1-A2B1)
the denominator in y and in x are the same so
denominator = A1B1-A2B1
thus:
x = (B2C1-B1C2)/denominator
y = (A1C2-A2C1)/denominator
These are the x and y coordinates of the intersection of two lines with points (x1, y1), (x2, y2) and (x3, y3), (x4, y4)
Now for a line segment it's the same but we need to check that the x or y coordinate is in both segments. That means between the x coordinate of both segments with lesser value and the x coordinate of both segments with greater value
This is a C++ program that returns true if the segments intersect and returns false if they don't. If the segments intersect it stores the point of intersection in a variable i.
struct Point
{
float x, y;
};
//p1 and p2 are the points of the first segment
//p3 and p4 are the points of the second segment
bool intersection(Point p1, Point p2, Point p3, Point p4, Point &i)
{
float max1; //x-coordinate with greater value in segment 1
float min1; //x-coordinate with lesse value in segment 1
float max2; //x-coordinate with greater value in segment 2
float min2; //x-coordinate with lesser value in segment 2
float A1 = p2.y - p1.y;
float B1 = p1.x - p2.x;
float C1 = A1 * p1.x + B1 * p1.y;
float A2 = p4.y - p3.y;
float B2 = p3.x - p4.x;
float C2 = A2 * p3.x + B2 * p3.y;
float denom = A1 * B2 - A2 * B1;
if (denom == 0.0) //When denom == 0, is because the lines are parallel
return false; //Parallel lines do not intersect
i.x = (C1 * B2 - C2 * B1) / denom;
i.y = (A1 * C2 - A2 * C1) / denom;
if (p1.x > p2.x)
{
max1 = p1.x;
min1 = p2.x;
}
else
{
max1 = p2.x;
min1 = p1.x;
}
if (p3.x > p4.x)
{
max2 = p3.x;
min2 = p4.x;
}
else
{
max2 = p4.x;
min2 = p3.x;
}
//check if x coordinate is in both segments
if (i.x >= min1 && i.x <= max1 &&
i.x >= min2 && i.x <= max2)
return true;
return false; //Do no intersect, intersection of the lines is not between the segments
}
Now you just need to compare on a loop all the segments and store the intersection point on array.

Two Rectangles intersection

I have two rectangles characterized by 4 values each :
Left position X, top position Y, width W and height H:
X1, Y1, H1, W1
X2, Y2, H2, W2
Rectangles are not rotated, like so:
+--------------------> X axis
|
| (X,Y) (X+W, Y)
| +--------------+
| | |
| | |
| | |
| +--------------+
v (X, Y+H) (X+W,Y+H)
Y axis
What is the best solution to determine whether the intersection of the two rectangles is empty or not?
if (X1+W1<X2 or X2+W2<X1 or Y1+H1<Y2 or Y2+H2<Y1):
Intersection = Empty
else:
Intersection = Not Empty
If you have four coordinates – ((X,Y),(A,B)) and ((X1,Y1),(A1,B1)) – rather than two plus width and height, it would look like this:
if (A<X1 or A1<X or B<Y1 or B1<Y):
Intersection = Empty
else:
Intersection = Not Empty
Best example..
/**
* Check if two rectangles collide
* x_1, y_1, width_1, and height_1 define the boundaries of the first rectangle
* x_2, y_2, width_2, and height_2 define the boundaries of the second rectangle
*/
boolean rectangle_collision(float x_1, float y_1, float width_1, float height_1, float x_2, float y_2, float width_2, float height_2)
{
return !(x_1 > x_2+width_2 || x_1+width_1 < x_2 || y_1 > y_2+height_2 || y_1+height_1 < y_2);
}
and also one other way see this link ... and code it your self..
Should the two rectangles have the same dimensions you can do:
if (abs (x1 - x2) < w && abs (y1 - y2) < h) {
// overlaps
}
I just tried with a c program and wrote below.
#include<stdio.h>
int check(int i,int j,int i1,int j1, int a, int b,int a1,int b1){
return (\
(((i>a) && (i<a1)) && ((j>b)&&(j<b1))) ||\
(((a>i) && (a<i1)) && ((b>j)&&(b<j1))) ||\
(((i1>a) && (i1<a1)) && ((j1>b)&&(j1<b1))) ||\
(((a1>i) && (a1<i1)) && ((b1>j)&&(b1<j1)))\
);
}
int main(){
printf("intersection test:(0,0,100,100),(10,0,1000,1000) :is %s\n",check(0,0,100,100,10,0,1000,1000)?"intersecting":"Not intersecting");
printf("intersection test:(0,0,100,100),(101,101,1000,1000) :is %s\n",check(0,0,100,100,101,101,1000,1000)?"intersecting":"Not intersecting");
return 0;
}
Using a coordinate system where (0, 0) is the left, top corner.
I thought of it in terms of a vertical and horizontal sliding windows
and come up with this:
(B.Bottom > A.Top && B.Top < A.Bottom) && (B.Right > A.Left && B.Left < A.Right)
Which is what you get if you apply DeMorgan’s Law to the following:
Not (B.Bottom < A.Top || B.Top > A.Bottom || B.Right < A.Left || B.Left > A.Right)
B is above A
B is below A
B is left of A
B is right of A
If the rectangles' coordinates of the lower left corner and upper right corner are :
(r1x1, r1y1), (r1x2, r1y2) for rect1 and
(r2x1, r2y1), (r2x2, r2y2) for rect2
(Python like code below)
intersect = False
for x in [r1x1, r1x2]:
if (r2x1<=x<=r2x2):
for y in [r1y1, r1y2]:
if (r2y1<=y<=r2y2):
intersect = True
return intersect
else:
for Y in [r2y1, r2y2]:
if (r1y1<=Y<=r1y2):
intersect = True
return intersect
else:
for X in [r2x1, r2x2]:
if (r1x1<=X<=r1x2):
for y in [r2y1, r2y2]:
if (r1y1<=y<=r1y2):
intersect = True
return intersect
else:
for Y in [r1y1, r1y2]:
if (r2y1<=Y<=r2y2):
intersect = True
return intersect
return intersect
Circle approach is more straightforward. I mean when you define a circle as a center point and radius. Same thing here except you have a horizontal radius (width / 2) and a vertical one (height /2) and 2 conditions for horizontal and for vertical distance.
abs(cx1 – cx2) <= hr1 + hr2 && abs(cy1 - cy2) <= vr1 + vr2
If you need to exclude the case with sides not intersecting filter out these with one rectangle smaller in both dimensions and not enough distance (between centers) from the bigger one to reach one of its edges.
abs(cx1 – cx2) <= hr1 + hr2 && abs(cy1 - cy2) <= vr1 + vr2 &&
!(abs(cx1 – cx2) < abs(hr1 - hr2) && abs(cy1 - cy2) < abs(vr1 - vr2) && sign(hr1 - hr2) == sign(vr1 – vr2))
Rectangle = namedtuple('Rectangle', 'x y w h')
def intersects(rect_a: Rectangle, rect_b: Rectangle):
if (rect_a.x + rect_a.w < rect_b.x) or (rect_a.x > rect_b.x + rect_b.w) or (rect_a.y + rect_a.h < rect_b.y) or (rect_a.y > rect_b.y + rect_b.h):
return False
else:
return True
if( X1<=X2+W2 && X2<=X1+W1 && Y1>=Y2-H2 && Y2>=Y1+H1 )
Intersect
In the question Y is the top position..
Note: This solution works only if rectangle is aligned with X / Y Axes.

How to check if a point lies on a line between 2 other points

How would I write this function? Any examples appreciated
function isPointBetweenPoints(currPoint, point1, point2):Boolean {
var currX = currPoint.x;
var currY = currPoint.y;
var p1X = point1.x;
var p1y = point1.y;
var p2X = point2.x;
var p2y = point2.y;
//here I'm stuck
}
Assuming that point1 and point2 are different, first you check whether the point lies on the line. For that you simply need a "cross-product" of vectors point1 -> currPoint and point1 -> point2.
dxc = currPoint.x - point1.x;
dyc = currPoint.y - point1.y;
dxl = point2.x - point1.x;
dyl = point2.y - point1.y;
cross = dxc * dyl - dyc * dxl;
Your point lies on the line if and only if cross is equal to zero.
if (cross != 0)
return false;
Now, as you know that the point does lie on the line, it is time to check whether it lies between the original points. This can be easily done by comparing the x coordinates, if the line is "more horizontal than vertical", or y coordinates otherwise
if (abs(dxl) >= abs(dyl))
return dxl > 0 ?
point1.x <= currPoint.x && currPoint.x <= point2.x :
point2.x <= currPoint.x && currPoint.x <= point1.x;
else
return dyl > 0 ?
point1.y <= currPoint.y && currPoint.y <= point2.y :
point2.y <= currPoint.y && currPoint.y <= point1.y;
Note that the above algorithm if entirely integral if the input data is integral, i.e. it requires no floating-point calculations for integer input. Beware of potential overflow when calculating cross though.
P.S. This algorithm is absolutely precise, meaning that it will reject points that lie very close to the line but not precisely on the line. Sometimes this is not what's needed. But that's a different story.
Distance(point1, currPoint)
+ Distance(currPoint, point2)
== Distance(point1, point2)
But be careful if you have floating point values, things are different for them...
When concerned about the computational cost of computing "the square roots", don't:
Just compare "the squares".
This is independent of Javascript. Try the following algorithm, with points p1=point1 and p2=point2, and your third point being p3=currPoint:
v1 = p2 - p1
v2 = p3 - p1
v3 = p3 - p2
if (dot(v2,v1)>0 and dot(v3,v1)<0) return between
else return not between
If you want to be sure it's on the line segment between p1 and p2 as well:
v1 = normalize(p2 - p1)
v2 = normalize(p3 - p1)
v3 = p3 - p2
if (fabs(dot(v2,v1)-1.0)<EPS and dot(v3,v1)<0) return between
else return not between
You want to check whether the slope from point1 to currPoint is the same as the slope from currPoint to point2, so:
m1 = (currY - p1Y) / (currX - p1X);
m2 = (p2Y - currY) / (p2X - currX);
You also want to check whether currPoint is inside the box created by the other two, so:
return (m1 == m2) && (p1Y <= currY && currY <= p2Y) && (p1X <= currX && currX <= p2X);
Edit: This is not a very good method; look at maxim1000's solution for a much more correct way.
I'll use Triangle approach:
First, I'll check the Area, if the Area is close to 0, then the Point lies on the Line.
But think about the case where the length of AC is so great, then the Area increases far from 0, but visually, we still see that B is on AC: that when we need to check the height of the triangle.
To do this, we need to remember the formula we learn from first grade: Area = Base * Height / 2
Here is the code:
bool Is3PointOn1Line(IList<Vector2> arrVert, int idx1, int idx2, int idx3)
{
//check if the area of the ABC triangle is 0:
float fArea = arrVert[idx1].x * (arrVert[idx2].y - arrVert[idx3].y) +
arrVert[idx2].x * (arrVert[idx3].y - arrVert[idx1].y) +
arrVert[idx3].x * (arrVert[idx1].y - arrVert[idx2].y);
fArea = Mathf.Abs(fArea);
if (fArea < SS.EPSILON)
{
//Area is zero then it's the line
return true;
}
else
{
//Check the height, in case the triangle has long base
float fBase = Vector2.Distance(arrVert[idx1], arrVert[idx3]);
float height = 2.0f * fArea / fBase;
return height < SS.EPSILON;
}
}
Usage:
Vector2[] arrVert = new Vector2[3];
arrVert[0] = //...
arrVert[1] = //...
arrVert[2] = //...
if(Is3PointOn1Line(arrVert, 0, 1, 2))
{
//Ta-da, they're on same line
}
PS: SS.EPSILON = 0.01f and I use some function of Unity (for ex: Vector2.Distance), but you got the idea.
Ready for what seems to be infinitely simpler than some of these other solutions?
You pass it three points (three objects with an x and y property). Points 1 and 2 define your line, and point 3 is the point you are testing.
function pointOnLine(pt1, pt2, pt3) {
const dx = (pt3.x - pt1.x) / (pt2.x - pt1.x);
const dy = (pt3.y - pt1.y) / (pt2.y - pt1.y);
const onLine = dx === dy
// Check on or within x and y bounds
const betweenX = 0 <= dx && dx <= 1;
const betweenY = 0 <= dy && dy <= 1;
return onLine && betweenX && betweenY;
}
console.log('pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2})');
console.log(pointOnLine({ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }));
console.log('pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 0.5, y: 0.5})');
console.log(pointOnLine({ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 0.5, y: 0.5 }));
Edit: Simplified further according to RBarryYoung's observation.
This approach is similar to Steve's approach, just shorter and improved to use as little memory and process power as possible. But first the mathematical idea:
Let a, b be the ends of the line, ab the difference between them and p the point to check. Then p is exactly then on the line, if
a + i * ab = p
with i being a number in the interval [0;1] representing the index on the line. We can write that as two separate equations (for 2D):
a.x + i * ab.x = p.x
a.y + i * ab.y = p.y
⇔
i = (p.x - a.x) / ab.x
i = (p.y - a.y) / ab.y
Which gives us to requirements for p to be on the line from a to b:
(p.x - a.x) / ab.x = (p.y - a.y) / ab.y
and
0 ≤ i ≤ 1
In code:
function onLine(a, b, p) {
var i1 = (p.x - a.x) / (b.x - a.x), i2 = (p.y - a.y) / (b.y - a.y);
return i1 == i2 && i1 <= 0 && i1 >= 1;
}
Technically you could even inline i2 but that makes it even harder to read.
private static boolean pointInLine(int2 lineBegin, int2 lineEnd, int2 point)
{ boolean result = false;
int2 b = int2(min(lineBegin.x, lineEnd.x), min(lineBegin.y, lineEnd.y));
int2 e = int2(max(lineBegin.x, lineEnd.x), max(lineBegin.y, lineEnd.y));
if (point.x >= b.x && point.x <= e.x &&
point.y >= b.y && point.y <= e.y)
{ int2 normal = lineEnd.sub(lineBegin).perp();
int2 d0 = lineBegin.sub(point);
if (d0.dot(normal) == 0x0)
{ result = true;
}
}
return result;
}
This works for any slope, even if lineBegin == lineEnd == point.
First, create two points b and e, which ensures b <= e. (This is two support lines that have a negative slope) Check if point lands on the box (inclusive) created by those two points.
Then, get the normal of the line, which is a vector perpendicular to it.
You can do this by negating the x and transposing, x,y --> y, -x.
Then you can simply create a vector that points to the line from the point
or to the point from the line, it doesn't matter and doesn't have to be from the center of the line. Once you do that check if the vector is perpendicular to the normal by getting the dot product of the normal and the point vector.
It also makes it a bit easier on you if you've got some sort of math lib or at least a struct with x,y components. But you can do this with scalar components aswell. But getting the dot product and the normal are very simple calculations.
Here's the dot product:
.dot(int2 v) = (x * v.x + y * v.y)
Here's the perpendicular:
.perp() = new int2(y, -x)
.sub() .add() Do what you'd expect and params are in the same order.

Resources