I want to find the point at distance d
on the right side of a line defined by P1(x1,y1)
and P2(x2,y2)
(the distance is calculated from the middle of the line). I came up with the following code, which works well, but I think I have made unnecessary calculations, and it can be done faster.
#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
double x2, x1, y1, y2, px, py, p1x, p1y, p2x, p2y, d, ax, ay, b, dx, dy;
d = 2.0; // given distance
ax = (x1 x2) / 2; // middle point
ay = (y1 y2) / 2; // middle point
b = tan(atan2(y2 - y1, x2 - x1) PI / 2); // slope of the perpendicular line
dx = (d / sqrt(1 (b * b)));
dy = b * dx;
p1x = ax dx;
p1y = ay dy;
p2x = ax - dx;
p2y = ay - dy;
// cross product
if (((x2 - x1) * (p1y - y1) - (y2 - y1) * (p1x - x1)) > 0)
{
px = p1x;
py = p1y;
}
else
{
px = p2x;
py = p2y;
}
CodePudding user response:
You don't need atan
, b
value, cross product to check orientation (moreover, b
might be zero and cause division error).
Instead calculate normalized (unit length) direction vector and get right normal to it:
d = 2.0; // given distance
ax = (x1 x2) / 2; // middle point
ay = (y1 y2) / 2; // middle point
dx = x2 - x1;
dy = y2 - y1;
scale = d / sqrt(dx*dx dy*dy); //distance/vector length
px = ax dy * scale; // add normal vector to the right side of p1-p2 direction
py = ay - dx * scale; //note minus sign
CodePudding user response:
For generating a 2D vector perpendicular to another, one result that falls out from a special case of the dot product is that you can swap the two components of the vector and negate one of them.
For example, let's say you have the vector d
which points from p1
to p2
:
dx = p2x - p1x;
dy = p2y - p1y;
And now you want to generate right
which is perpendicular, it is simply:
rightx = dy;
righty = -dx;
Now, let's do a quick visual check for our definition of "on the right", in case we actually want to negate those two values...
o p2 = [2, 3]
/
o p1 = [0, 0]
Above, d
is simple: [2, 3]
. Intuitively, we would think of (as viewed from above) walking from p1
to p2
and looking to the right, which would mean a vector in the positive X direction and the negative Y direction. So yes, that looks fine.
Note: If your co-ordinate system is screen-based (i.e. positive Y direction is down), then the inverse is true (and you would negate both the terms in the calculation of the right
vector). This is due to the handedness of the co-ordinate system being left instead of right.
Now, you can calculate the midpoint mid
as either (p1 p2) / 2
or p1 d / 2
.
midx = (p1x p2x) / 2;
midy = (p1y p2y) / 2;
And finally to generate p3
you start from mid
and extend down the vector right
by an amount height
, you need to normalize that vector by dividing by its length and scale by height
. Formally, the final point will be mid right * height / length(right)
.
This is the only particularly expensive part of the calculation, because it needs a square root.
rdist = height / sqrt(rightx * rightx righty * righty);
p3x = midx rightx * rdist;
p3y = midy righty * rdist;
Congratulations! You now have an isosceles triangle!