How to determine if a segment passes through the positive X axis?

A segment is defined by two endpoints $(x_1, y_1)$ and $(x_2, y_2)$.

The positive X axis for simplicity can be defined as the segment going from $(0, 0)$ to $(10^9, 0)$ (I'm working on a program where the maximum $X$ value is $10^6$ therefore this is good enough). Although a formula that doesn't rely on endpoints (for the axis) is also acceptable (and probably even better).

What I currently have is an algorithm that detects segment intersections, so I use the segment from $(0, 0)$ to $(10^9, 0)$ explained above. The algorithm I'm using is a $O(1)$ check that uses cross product for getting the orientation of points (code below).

But I'm wondering if there's a more elegant way to do this. I just want to refactor the code since this is the only intersection I need to do, and it looks a bit ugly to have all this code just for this. I suspect even a more elegant way would need some cross product stuff, but any idea is appreciated!

C++ code of my current algorithm.

struct Segment {
  Point p, q;

  int orientation(Point p, Point q, Point r) {
    ll val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
    if (val == 0) return 0;
    return (val > 0) ? 1 : 2;
  }

  bool intersect(const Segment& s) {
    Point p1 = p;
    Point q1 = q;
    Point p2 = s.p;
    Point q2 = s.q;
    int o1 = orientation(p1, q1, p2);
    int o2 = orientation(p1, q1, q2);
    int o3 = orientation(p2, q2, p1);
    int o4 = orientation(p2, q2, q1);
    return (o1 != o2 && o3 != o4);
  }
};

If you trace the intersect function step by step, and if you exclude the cases where $y_1 = 0$ and where $y_2 = 0,$ you might notice that two of the calls to orientation are merely testing whether $y_1 > 0$ and whether $y_2 > 0.$ The return statement then checks whether those two results are different, that is, whether $y_1$ and $y_2$ are on opposite sides of the $x$ axis.

In the cases where $y_1 = 0$ and where $y_2 = 0,$ the two calls to orientation will return two different values (indicating an intersection with the $x$ axis) except, bizarrely, in the case where $y_1 = y_2 = 0$ (in which case the algorithm will tell you that the segment does not intersect the $x$ axis).

One of the two remaining calls to orientation -- the one that compares the point $(10^9, 0)$ with the points $(x_1, y_1)$ and $(x_2, y_2)$ -- is always going to return the same value as every other time, unless you're so unlucky that someone gives you points like $(x_1, y_1) = (10^9+1, 1)$ and $(x_2, y_2) = (10^9+1, -1).$ So ideally you would skip this call to orientation and merely check whether the one remaining call -- which compares $(0,0)$ with $(x_1, y_1)$ and $(x_2, y_2)$ -- returns the result that puts $(0,0)$ on the desired side of the line through $(x_1, y_1)$ and $(x_2, y_2).$

This last call to orientation will use $(x_1, y_1)$ and $(x_2, y_2)$ as the first two arguments (in one order or the other) and $(0,0)$ as the third argument, so it will set val to either

$$(y_1 - y_2) x_1 - (x_1 - x_2) y_1$$

or the exact opposite of that quantity (changing the sign), and it will reliably always set val to the same sign for every example where the line through $(x_1, y_1)$ and $(x_2, y_2)$ intersects the positive $x$ axis. Which sign that is just depends on the order in which it receives the first two arguments.

Since all we need to do is get a consistent sign, we can assume we will compute $(y_1 - y_2) x_1 - (x_1 - x_2) y_1$ rather than the opposite. To find out which sign is the one that says "intersects positive $x$ axis," we just need to work out one example. Let's suppose $(x_1, y_1) = (1, 1)$ and $(x_2, y_2) = (1, -1).$ This is a segment that intersects the positive $x$ axis, and

$$ (y_1 - y_2) x_1 - (x_1 - x_2) y_1 = 2\times 1 - 0 \times 1 = 2. $$

One more detail to figure out. Consider the segment defined by $(x_1, y_1) = (0, 1)$ and $(x_2, y_2) = (0, -1).$ This segment passes through the $x$ axis at $(0,0).$ Do you consider that to be an intersection with "the positive $x$ axis" or not? Note that the algorithm you are currently using will tell you that this segment does intersect the positive $x$ axis; I am merely unsure whether that is the answer you wanted or a defect in the implementation.

So what I would do is to write a function that returned the result true if both of the following two conditions are true:

  • First condition: either $y_1 = 0,$ or $y_2 = 0,$ or $y_1$ and $y_2$ have opposite signs.
  • Second condition: $(y_1 - y_2) x_1 - (x_1 - x_2) y_1 > 0$ (unless you think passing through $(0,0)$ should count as an intersection with "the positive $x$ axis," in which case use $\geq$ instead of $>$).

Here is a simple code segment to implement my suggestion.

The first line returns false if the segment is entirely above or below the x axis. Otherwise, the second line returns true if, and only if, the x-intercept is non-negative, as you desire.

{
  if(p.y*q.y>0)return 0;
  return p.x+(q.x-p.x)*p.y/(p.y-q.y)>=0;
}