I need to be able to check whether the angle between three points (A, B and C) which make up part of a shape is reflex (> PI radians), as in the diagram below (sorry for poor paint skills!):
My points should always be anti-clockwise, and I always want to measure the angle on the inside of the shape.
I am currently doing this using the following code:
//triangle[] is an array of the three points I am testing, corresponding
// to [A, B, C] on the diagram above
//Vectors from B to A and C
PointF toA = PointFVectorTools.difference(triangle[0], triangle[1]);
PointF toC = PointFVectorTools.difference(triangle[2], triangle[1]);
double angle = Math.Atan2(toB.Y, toB.X) - Math.Atan2(toA.Y, toA.X);
//Put angle in range 0 to 2 PI
if (angle < 0) angle += 2 * Math.PI;
return angle > Math.PI;
This has worked in all the cases I have tried up until now, but with these co-ords it does not work:
(Where B=(2,3) )
The angle I get back is ~-0.5, whereas I would expect ~+0.5. Any ideas why this is wrong?
UPDATE
I've attempted to implement Nico's solution, and while I understand it in theory I'm getting a real headache trying to implement it. Here is the code so far:
//Vector A -> B
float dx = triangle[1].X - triangle[0].X;
float dy = triangle[1].Y - triangle[0].Y;
//Left normal = (y, -x)
PointF leftDir = new PointF(dy, -dx);
//Vector B -> C
dx = triangle[2].X - triangle[1].X;
dy = triangle[2].Y - triangle[1].Y;
//Dot product of B->C and Left normal
float dot = dx * leftDir.X + dy * leftDir.Y;
return dot < 0;
In the following, I assume that the x-axis points to the right and the y-axis points upwards. If this is not the case in your scenario, you might need to switch some signs.
If you have the line segment (x1, y1) - (x2, y2) and points are sorted counter-clockwise, you know that the shape is left of the line segment. The orthogonal direction vector that points to the line segment's left is:
leftDir = (y1 - y2, x2 - x1)
Together with the line segment, this direction defines a half space. If the following angle is convex, the third point must lie in this half space. If that's not the case, the angle is concave (which you apparently call reflex):
You can determine if the point lies in the same half space with the dot product:
isConcave = dot(p3 - p2, leftDir) < 0
In code:
float dx = x3 - x2;
float dy = y3 - y2;
float dot = dx * leftDir.x + dy * leftDir.y
return dot < 0;
I'm not sure how toB in your code is defined, and also I'm not familar with PointF.
Anyway you should use the cosine rule c^2 = a^2 + b^2 - 2ab cos(C) (where a,b,c are the lengths of the sides of the triangle, and C is the angle subtending c):
public bool IsReflex(... triangle)
{
var a = GetVectorLength(triangle[0].x, triangle[0].y, triangle[1].x, triangle[1].y);
var b = GetVectorLength(triangle[1].x, triangle[1].y, triangle[2].x, triangle[2].y);
var c = GetVectorLength(triangle[2].x, triangle[2].y, triangle[0].x, triangle[0].y);
var cosC = (c*c - a*a - b*b) / (2*a*b);
var C = Math.Acos(cosC); // this returns a value between 0 and pi
return Math.Abs(C) > (Math.PI/2);
}
private double GetVectorLength(double x0, double y0, double x1, double y1)
{
// using Pythagoras
var sideX = x0 - x1;
var sideY = y0 - y1;
return Math.Sqrt(sideX*sideX + sideY*sideY);
}
Related
If I have two points p1 and p2 where p1 is the pivot point and p2 is the original direction the user was headed and they have a number of possible directions to go p3...pn in random sequence. How do I get the angles between the choices and the segment formed by p1,p2 as clockwise(right hand) positive values between 0 and 360 so that I can sort them from least to greatest?
Also the points p1...pn will be in any quadrant, I can’t assume they will always be in the positive x,y direction. The grid is a standard Cartesian grid not screen coordinates so Y gets smaller as you go down not larger.
So in this example (sorry for the poor drawing but Paint was all I had on my laptop) I need to get the angles:
(p2-p1-p3)
( p2-p1-p4)
( p2-p1-p5)
( p2-p1-p6)
In this order(smallest right hand turn to largest right hand turn):
[( p2-p1-p4), ( p2-p1-p6), ( p2-p1-p5), (p2-p1-p3)]
The points in my case are a class called Vertex:
public class Vertex
{
public double X = 0;
public double Y = 0;
public Vertex() { }
public Vertex(double x, double y)
{
X = x;
Y = y;
}
}
And the code for getting the angles and sorting looks like this right now but has a problem:
private static IEnumerable<Vertex> SortByAngle(Vertex original, Vertex pivot, List<Vertex> choices)
{
choices.Sort((v1, v2) => GetTurnAngle(original, pivot, v1).CompareTo(GetTurnAngle(original, pivot, v2)));
return choices;
}
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var a = original.X - pivot.X;
var b = original.Y - pivot.Y;
var c = choice.X - pivot.X;
var d = choice.Y - pivot.Y;
var rads = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
return (180 / Math.PI * rads);
}
The problem is the above is if I check it for:
original 66,-66
pivot 280,-191
choice 200,-180
I get an angle of 22.460643124 instead of 337.539356876 which means it went counter-clockwise from the original direction to get that angle. I need it to always go clockwise to get the angle.
What am I doing wrong and how do I fix it?
Update: OK so according to what you guys are saying I can probably use some cross product like math to determine CW vs CCW so the new method would look like this:
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var a = original.X - pivot.X;
var b = original.Y - pivot.Y;
var c = choice.X - pivot.X;
var d = choice.Y - pivot.Y;
var angle = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
angle = (180 / Math.PI * angle);
var z = (choice.X - pivot.X) * (original.Y - pivot.Y) - (choice.Y - pivot.Y) * (original.X - pivot.X);
if (z < 0)
{
return 360 - angle;
}
return angle;
}
Update 2:
Using the accepted solution it now looks like so:
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var angle1 = Math.Atan2(original.Y - pivot.Y, original.X - pivot.X);
var angle2 = Math.Atan2(choice.Y - pivot.Y, choice.X - pivot.X);
var angleDiff = (180 / Math.PI * (angle2 - angle1));
if (angleDiff > 0)//It went CCW so adjust
{
return 360 - angleDiff;
}
return -angleDiff;//I need the results to be always positive so flip sign
}
So far as I can tell that works great so far. Thank you guys for the help!
Take a look at atan2 function. It takes delta y and delta x, so can distinguish all angles.
angle1 = atan2(p1.y-p0.y, p1.x-p0.x);
angle2 = atan2(p2.y-p0.y, p2.x-p0.x);
angle = angle2 - angle1;
If angle is negative, then CW, if positive CCW (or other way around depending on your axis orientation). Note |angle| may be > 180, in which case you may want to do 360-|angle| and reverse the CW CCW conclusion if you're after the shortest route.
You find the Dn=direction from p1 to pn (x=pn.x-p1.x and y=pn.y-p1.y) by the formula:
Dn=f(x,y)=180-90*(1+sign(y))* (1-sign(x^2))-45*(2+sign(y))*sign(x)
-180/pi()*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))
So the angles are Angle(p2-p1-pn)=Dn-D2.
I'm looking for a bit of math help. I have a game were a 2D heightmap is generated and then stretched over a sphere using a length/direction formula. Now I need to know how to calculate the height between 2 points on my heightmap.
What I know:
The array that holds the heightmap
The angle in radians to my object
how many points there are on the heightmap
My problem look somewhat like so:
image
more images
The red and blue lines are the 2 heightmap points, and the light blue is where I'd like to calculate the height at.
Here's my current code to do it, but it doesn't work to well.
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double offset = MathHelper.TwoPi / (heightmap.Length - 1);
//total brainfart attempt
double lowerAngle = offset * angle;
double upperAngle = offset * angle + offset;
//find heights
double height1 = heightmap[(int)lowerAngle];
double height2 = heightmap[(int)upperAngle];
//find offset angle
double u = angle - lowerAngle / (upperAngle - lowerAngle);
//return the height
return height1 + (height1 - height2) * u;
}
from my vegetation code, this seems to work okay, but is to rough to use for units and such, as they jump up/down as they move, due to it using only 1 heightmap point.
double[] hMap = planet.getHeightMap();
double i = hMap.Length / (Math.PI * 2);
this.height = hMap[(int)(angle * i)];
EDIT: example at end based on additional question info
Sounds to me like a linear interpolation - if you look at it from a 2d point of view, you've got two points:
(x1, y1) = point one on heightmap
(x2, y2) = point two on heightmap
and one point somewhere between (x1,x2) at an unknown height:
pu = (xu, yu)
A generic formula for LERP is:
pu = p0 + (p1 - p0) * u
where:
p0 = first value
p1 = second value
u = % where your unknown point lies between (p0,p1)
Here, we'll say p0 == y2 and p1 == y1. Now we need to determine "how far" the unknown point is between x1 and x2 - if you know the angles to the two heightmap points, this is easy:
u = ang(xu) - ang(x1) / (ang(x2) - ang(x1))
Alternatively, you could project your angle out to Max(y1,y2) and get the "unknown x pos" that way, then calculate the above.
So, let's try a contrived example:
p1 = point one in map = (1,2) therefore ang(p1) ~ 57 degrees
p2 = point two in map = (2,4) therefore ang(p2) ~ 114 degrees
note that here, the "x axis" is along the surface of the sphere, and the "y-axis" is the distance away from the center.
pu = object location = py #angle 100 degrees ~ 1.74 radians
px = (1.74 rad - 1 rad ) / (2 rad - 1 rad) = 0.74 / 1.0 = 0.74 => 74%
py = y0 + (y1 - y0) * u
= 2 + (4 - 2) * 0.74
= 2.96
Hopefully I didn't drop or misplace a sign there somewhere... :)
Ok, your example code - I've tweaked it a bit, here's what I've come up with:
First, let's define some helpers of my own:
public static class MathHelper
{
public const double TwoPi = Math.PI * 2.0;
public static double DegToRad(double deg)
{
return (TwoPi / 360.0) * deg;
}
public static double RadToDeg(double rad)
{
return (360.0 / TwoPi) * rad;
}
// given an upper/lower bounds, "clamp" the value into that
// range, wrapping over to lower if higher than upper, and
// vice versa
public static int WrapClamp(int value, int lower, int upper)
{
return value > upper ? value - upper - 1
: value < lower ? upper - value - 1
: value;
}
}
Our Test setup:
void Main()
{
var random = new Random();
// "sea level"
var baseDiameter = 10;
// very chaotic heightmap
heightmap = Enumerable
.Range(0, 360)
.Select(_ => random.NextDouble() * baseDiameter)
.ToArray();
// let's walk by half degrees, since that's roughly how many points we have
for(double i=0;i<360;i+=0.5)
{
var angleInDegrees = i;
var angleInRads = MathHelper.DegToRad(i);
Console.WriteLine("Height at angle {0}°({1} rad):{2} (using getheight:{3})",
angleInDegrees,
angleInRads,
heightmap[(int)angleInDegrees],
getheight(angleInRads));
}
}
double[] heightmap;
And our "getheight" method:
// assume: input angle is in radians
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double dTheta = MathHelper.TwoPi / (heightmap.Length);
// our "offset" will be how many dThetas we are
double offset = angle / dTheta;
// Figure out two reference points in heightmap
// THESE MAY BE THE SAME POINT, if angle ends up
// landing on a heightmap index!
int lowerAngle = (int)offset;
int upperAngle = (int)Math.Round(
offset,
0,
MidpointRounding.AwayFromZero);
// find closest heightmap points to angle, wrapping
// around if we go under 0 or over max
int closestPointIndex = MathHelper.WrapClamp(
lowerAngle,
0,
heightmap.Length-1);
int nextPointIndex = MathHelper.WrapClamp(
upperAngle,
0,
heightmap.Length-1);
//find heights
double height1 = heightmap[closestPointIndex];
double height2 = heightmap[nextPointIndex];
// percent is (distance from angle to closest angle) / (angle "step" per heightmap point)
double percent = (angle - (closestPointIndex * dTheta)) / dTheta;
// find lerp height = firstvalue + (diff between values) * percent
double lerp = Math.Abs(height1 + (height2 - height1) * percent);
// Show what we're doing
Console.WriteLine("Delta ang:{0:f3}, Offset={1:f3} => compare indices:[{2}, {3}]",
dTheta,
offset,
closestPointIndex,
nextPointIndex);
Console.WriteLine("Lerping {0:p} between heights {1:f4} and {2:f4} - lerped height:{3:f4}",
percent,
height1,
height2,
lerp);
return lerp;
}
I am rotating points around a center point in 2D space. The points are the center point, the old mouse position, and the new mouse position. My rotation function works fine, and I can calculate the angle perfectly. But I want to calculate a negative angle if the user is moving their mouse in a direction which should be interpreted as counter-clockwise.
For example, moving the mouse toward the right (positive x-axis) should rotate clockwise if you are above (less than) the y value of the center point, but it should rotate counter-clockwise if you are actually below (greater than) the y value of the center point.
Here's what I have:
PointF centerPoint;
PointF oldPoint;
PointF newPoint;
double Xc = centerPoint.X;
double Yc = centerPoint.Y;
double Xb = oldPoint.X;
double Yb = oldPoint.Y;
double Xa = newPoint.X;
double Ya = newPoint.Y;
double c2 = (Math.Pow(Xb - Xa, 2) + Math.Pow(Yb - Ya, 2));
double a2 = (Math.Pow(Xb - Xc, 2) + Math.Pow(Yb - Yc, 2));
double b2 = (Math.Pow(Xa - Xc, 2) + Math.Pow(Ya - Yc, 2));
double a = Math.Sqrt(a2);
double b = Math.Sqrt(b2);
double val = (a2 + b2 - c2) / (2 * a * b);
double angle = Math.Acos(val);
So I need a way to make angle negative when it needs to be, so the points rotate clockwise or counter-clockwise to follow the mouse position.
Try this, but I'm not sure:
double v1x = Xb - Xc;
double v1y = Yb - Yc;
double v2x = Xa - Xc;
double v2y = Ya - Yc;
double angle = Math.Atan2(v1x, v1y) - Math.Atan2(v2x, v2y);
private double AngleFrom3PointsInDegrees(double x1, double y1, double x2, double y2, double x3, double y3)
{
double a = x2 - x1;
double b = y2 - y1;
double c = x3 - x2;
double d = y3 - y2;
double atanA = Math.Atan2(a, b);
double atanB = Math.Atan2(c, d);
return (atanA - atanB) * (-180 / Math.PI);
// if Second line is counterclockwise from 1st line angle is
// positive, else negative
}
It seems like all you need to do is
angle = angle > Math.PI ? angle - 2*Math.PI : angle;
at the end of your code. That will give you a clockwise rotation to the right of the line defined by centerPoint and oldPoint, and counter-clockwise to the left of it, regardless of orientation.
Given vectors (x1,y1) and (x2,y2), I would suggest computing the cross product and dot product, and then using Atan2() on them. That will work in all cases where both vectors are non-zero and vector lengths are "reasonable".
I want to know how to get an angle of a line A-B from horizontal axis X. Other questions in SO do that only between two lines. I'm aware I can always draw second line A-C and calculate but I'm wondering if there's a faster method.
EDIT: I'm very sure I'm not doing a premature optimization.
You can use atan for that.
angle = atan((By-Ay)/(Bx-Ax))
private double Angulo(int x1, int y1, int x2, int y2)
{
double degrees;
// Avoid divide by zero run values.
if (x2 - x1 == 0)
{
if (y2 > y1)
degrees = 90;
else
degrees = 270;
}
else
{
// Calculate angle from offset.
double riseoverrun = (double)(y2 - y1) / (double)(x2 - x1);
double radians = Math.Atan(riseoverrun);
degrees = radians * ((double)180 / Math.PI);
// Handle quadrant specific transformations.
if ((x2 - x1) < 0 || (y2 - y1) < 0)
degrees += 180;
if ((x2 - x1) > 0 && (y2 - y1) < 0)
degrees -= 180;
if (degrees < 0)
degrees += 360;
}
return degrees;
}
If you need all four quadrants, Atan2 is more suitable than Atan.
public static int GetAngleBetweenPoints(PointF pt1, PointF pt2)
{
float dx = pt2.X - pt1.X;
float dy = pt2.Y - pt1.Y;
int deg = Convert.ToInt32(Math.Atan2(dy, dx) * (180 / Math.PI));
if (deg < 0) { deg += 360; }
return deg;
}
If
The angle is small,
you can live with small inaccuracies, and
You can use the angle in radians and not degrees,
then there is a fast solution: Under these conditions, you can assume that tan(a) = a = atan(a), and hence just omit the atan() call.
You could also use arccosine, if your line is in the form [r_x,r_y], where r_x is the change in x and r_y is the change in y.
angle = arccos( r_x/( r_x*r_x + r_y*r_y ) )
It's slightly more opaque, but it's basically the dot product law:
angle = arccos (r . v)
Where r and v are both unit vectors (vectors of length 1). In our case, v is the vector [1,0], and r is
[r_x,r_y] / (r_x^2+r_y^2)
in order to make it a unit vector.
The x-axis is actually a line with equation
y = 0
so you could use the solution you have already.
How can I tell if a point belongs to a certain line?
Examples are appreciated, if possible.
In the simplest form, just plug the coordinates into the line equation and check for equality.
Given:
Point p (X=4, Y=5)
Line l (Slope=1, YIntersect=1)
Plug in X and Y:
Y = Slope * X + YIntersect
=> 5 = 1 * 4 + 1
=> 5 = 5
So yes, the point is on the line.
If your lines are represented in (X1,Y1),(X2,Y2) form, then you can calculate slope with:
Slope = (y1 - y2) / (x1-x2)
And then get the Y-Intersect with this:
YIntersect = - Slope * X1 + Y1;
Edit: I fixed the Y-Intersect (which has been X1 / Y1 ...)
You'll have to check that x1 - x2 is not 0. If it is, then checking if the point is on the line is a simple matter of checking if the Y value in your point is equal to either x1 or x2. Also, check that the X of the point is not 'x1' or 'x2'.
I just wrote an function which handles a few extra requirements since I use this check in a drawing application:
Fuzziness - There must be some room for error since the function is used to select lines by clicking on them.
The line got an EndPoint and a StartPoint, no infinite lines.
Must handle straight vertical and horizontal lines, (x2 - x1) == 0 causes division by zero in the other answers.
private const double SELECTION_FUZZINESS = 3;
internal override bool ContainsPoint(Point point)
{
LineGeometry lineGeo = geometry as LineGeometry;
Point leftPoint;
Point rightPoint;
// Normalize start/end to left right to make the offset calc simpler.
if (lineGeo.StartPoint.X <= lineGeo.EndPoint.X)
{
leftPoint = lineGeo.StartPoint;
rightPoint = lineGeo.EndPoint;
}
else
{
leftPoint = lineGeo.EndPoint;
rightPoint = lineGeo.StartPoint;
}
// If point is out of bounds, no need to do further checks.
if (point.X + SELECTION_FUZZINESS < leftPoint.X || rightPoint.X < point.X - SELECTION_FUZZINESS)
return false;
else if (point.Y + SELECTION_FUZZINESS < Math.Min(leftPoint.Y, rightPoint.Y) || Math.Max(leftPoint.Y, rightPoint.Y) < point.Y - SELECTION_FUZZINESS)
return false;
double deltaX = rightPoint.X - leftPoint.X;
double deltaY = rightPoint.Y - leftPoint.Y;
// If the line is straight, the earlier boundary check is enough to determine that the point is on the line.
// Also prevents division by zero exceptions.
if (deltaX == 0 || deltaY == 0)
return true;
double slope = deltaY / deltaX;
double offset = leftPoint.Y - leftPoint.X * slope;
double calculatedY = point.X * slope + offset;
// Check calculated Y matches the points Y coord with some easing.
bool lineContains = point.Y - SELECTION_FUZZINESS <= calculatedY && calculatedY <= point.Y + SELECTION_FUZZINESS;
return lineContains;
}
The best way to determine if a point R = (rx, ry) lies on the line connecting points P = (px, py) and Q = (qx, qy) is to check whether the determinant of the matrix
{{qx - px, qy - py}, {rx - px, ry - py}},
namely (qx - px) * (ry - py) - (qy - py) * (rx - px) is close to 0. This solution has several related advantages over the others posted: first, it requires no special case for vertical lines, second, it doesn't divide (usually a slow operation), third, it doesn't trigger bad floating-point behavior when the line is almost, but not quite vertical.
Given two points on the line L0 and L1 and the point to test P.
(L1 - L0) * (P - L0)
n = (P - L0) - --------------------- (L1 - L0)
(L1 - L0) * (L1 - L0)
The norm of the vector n is the distance of the point P from the line through L0 and L1. If this distance is zero or small enough (in the case of rounding errors), the point lies on the line.
The symbol * represents the dot product.
Example
P = (5, 5)
L0 = (0, 10)
L1 = (20, -10)
L1 - L0 = (20, -20)
P - L0 = (5, -5)
(20, -20) * (5, -5)
n = (5, -5) - --------------------- (20, -20)
(20, -20) * (20, -20)
200
= (5, -5) - --- (20, -20)
800
= (5, -5) - (5, -5)
= (0, 0)
I think Mr.Patrick McDonald put the nearly correct answer and this is the correction of his answer:
public bool IsOnLine(Point endPoint1, Point endPoint2, Point checkPoint)
{
return (((double)checkPoint.Y - endPoint1.Y)) / ((double)(checkPoint.X - endPoint1.X))
== ((double)(endPoint2.Y - endPoint1.Y)) / ((double)(endPoint2.X - endPoint1.X));
}
and of course there are many other correct answers especially Mr.Josh but i found this is the best one.
Thankx for evryone.
y = m * x + c
This is the equation of a line. x & y are the co-ordinates. Each line is characterized by its slope (m ) and where it intersects the y-axis (c).
So given m & c for a line, you can determine if the point (x1, y1) is on the line by checking if the equation holds for x = x1 and y = y1
A 2D line is generally represented using an equation in two variables x and y here is a well known equation
Now imagine your GDI+ line is drawn from (0,0) to (100, 100) then the value of m=(0-100)/(0-100) = 1 thus the equation for your line is y-0=1*(x-0) => y=x
Now that we have an equation for the line in question its easy to test if a point belongs to this line. A given point (x3, y3) belongs to this line if it satisfies the line equation when you substitute x=x3 and y=y3. For example the point (10, 10) belongs to this line since 10=10 but (10,12) does not belong to this line since 12 != 10.
NOTE: For a vertical line the value of the slope (m) is infinite but for this special case you may use the equation for a vertical line directly x=c where c = x1 = x2.
Though I have to say I am not sure if this is the most efficient way of doing this. I will try and find a more efficient way when I have some more time on hand.
Hope this helps.
If you have a line defined by its endpoints
PointF pt1, pt2;
and you have a point that you want to check
PointF checkPoint;
then you could define a function as follows:
bool IsOnLine(PointF endPoint1, PointF endPoint2, PointF checkPoint)
{
return (checkPoint.Y - endPoint1.Y) / (endPoint2.Y - endPoint1.Y)
== (checkPoint.X - endPoint1.X) / (endPoint2.X - endPoint1.X);
}
and call it as follows:
if (IsOnLine(pt1, pt2, checkPoint) {
// Is on line
}
You will need to check for division by zero though.
Equation of the line is:
y = mx + c
So a point(a,b) is on this line if it satisfies this equation i.e. b = ma + c
As an alternative to the slope/y-intercept method, I chose this approach using Math.Atan2:
// as an extension method
public static bool Intersects(this Vector2 v, LineSegment s) {
// check from line segment start perspective
var reference = Math.Atan2(s.Start.Y - s.End.Y, s.Start.X - s.End.X);
var aTanTest = Math.Atan2(s.Start.Y - v.Y, s.Start.X - v.X);
// check from line segment end perspective
if (reference == aTanTest) {
reference = Math.Atan2(s.End.Y - s.Start.Y, s.End.X - s.Start.X);
aTanTest = Math.Atan2(s.End.Y - v.Y, s.End.X - v.X);
}
return reference == aTanTest;
}
The first check reference determines the arcTan from the start point of the line segment to it's end-point.
Then from the start point perspective, we determine the arcTan to the vector v.
If those values are equal, we check from the perspective of the end-point.
Simple and handles horizontal, vertical and all else in between.