The first thing to note is that no new angle is created when you discover the intersection sector of two sectors 'A' and 'B'. Sector 'A' is defined by two limits, Acw and Acc, and sector 'B' is defined by two limits, Bcw and Bcc. Here 'cw' and 'cc' denote 'ClockWise' and 'CounterClockwise'.
The boundaries of the intersection sector will be made from at most two out of these four angles. The problem is entirely concerned with selecting two out of these four angles to be the limits of the intersection sector, let's call them Icw and Icc.
It is important to distinguish between "cw" and "cc" and keep them straight throughout the problem, because any pair of angles actually defines two sectors, right? This is as you have shown in your picture at the top of this answer. The issue of "angle wraparound" will arise naturally as the problem is solved.
Some Helper Functions
OK, so we have our four angles, and we have to select two of the four to be the limits of our intersection sector. In order to do this, we need an operator that determines whether an angle dAngle falls between two limits, let's call them dLimitCW and dLimitCC.
It is in this operator that the issue of "angle wraparound" arises. I did mine by constraining all angles to the range of -π to π. To determine whether dAngle falls between dLimitCW and dLimitCC, we subtract dAngle from dLimitCW and dLimitCC, and then constrain the result to fall within the [-π, π] range by adding or subtracting 2π. This is just like rotating the polar coordinate system by the angle dAngle , so that what was dAngle is now zero; what was dLimitCW is now dLimitCWRot, and what was dLimitCC is now dLimitCCRot.
The code looks like this:
bool AngleLiesBetween(double dAngle, double dLimitCW, double dLimitCC)
{
double dLimitCWRot, dLimitCCRot;
// Rotate everything so that dAngle is on zero axis.
dLimitCWRot = constrainAnglePlusMinusPi(dLimitCW - dAngle);
dLimitCCRot = constrainAnglePlusMinusPi(dLimitCC - dAngle);
if (dLimitCWRot > dLimitCCRot)
return (signbit(dLimitCWRot * dLimitCCRot));
else
return (!signbit(dLimitCWRot * dLimitCCRot));
}
where the function constrainAnglePlusMinusPi is
double constrainAnglePlusMinusPi(double x)
{
x = fmod(x + pi, 2*pi);
if (x < 0)
x += 2*pi;
return x - pi;
}
Once we have our "angle lies between" function, we use it to select which of the four angles that made up the limit angles of the two sectors make up the intersection sector.
The Nitty Gritty
To do this, we must first detect the case in which the two angular ranges do not overlap; we do this by calling our AngleLiesBetween() function four times; if it returns a "false" all four times, there is no overlap and the intersection is undefined:
if (
(AngleLiesBetween(Acw, Bcw, Bcc) == false) &&
(AngleLiesBetween(Acc, Bcw, Bcc) == false) &&
(AngleLiesBetween(Bcw, Acw, Acc) == false) &&
(AngleLiesBetween(Bcc, Acw, Acc) == false)
)
{
// Ranges have no overlap, result is undefined.
*this = CAngleRange(0.0f, 0.0f);
return;
}
Here I'm returning a CAngleRange object containing (0.0, 0.0) to indicate "no overlap," but you can do it some other way if you like, like by having your "intersection" function return a value of "false."
Once you've handled the "no overlap" case, the rest is easy. You check each of the six remaining combinations one at a time and determine which two limits are the limits of I by their outcomes:
if ((AngleLiesBetween(Acw, Bcw, Bcc) == true) && (AngleLiesBetween(Acc, Bcw, Bcc) == false)) then Icw = Acw and Icc = Bcc;
if ((AngleLiesBetween(Acw, Bcw, Bcc) == false) && (AngleLiesBetween(Acc, Bcw, Bcc) == true)) then Icw = Bcw and Icc = Acc;
if ((AngleLiesBetween(Acw, Bcw, Bcc) == true) && (AngleLiesBetween(Acc, Bcw, Bcc) == true)) then Icw = Acw and Icc = Acc;
if ((AngleLiesBetween(Bcw, Acw, Acc) == true) && (AngleLiesBetween(Bcc, Acw, Acc) == false)) then Icw = Bcw and Icc = Acc;
if ((AngleLiesBetween(Bcw, Acw, Acc) == false) && (AngleLiesBetween(Bcc, Acw, Acc) == true)) then Icw = Acw and Icc = Bcc;
and finally
if ((AngleLiesBetween(Bcw, Acw, Acc) == true) && (AngleLiesBetween(Bcc, Acw, Acc) == true)) then Icw = Bcw and Icc = Bcc.
You don't have to constrain the results to [-π, π] or [0, 2π] because you haven't changed them; each of the result angles is just one of the angles you presented as input to the function in the first place.
Of course you can optimize and streamline the code I've given in various ways that take advantage of the symmetries inherent in the problem, but I think when all is said and done you have to compare eight separate angle combinations for "between-ness" no matter how you optimize things. I like to keep things simple and straightforward in my code, in case I have made an error and have to come back and debug it in a few years when I've forgotten all my clever optimizations and streamlining efforts.
About Angle Wraparound
Notice that the issue of "angle wraparound" got handled in function AngleLiesBetween(); we rotated the coordinate system to put the angle we are checking (which we called dAngle) for "between-ness" at zero degrees. This naturally puts the two angle limits (dLimitCW and dLimitCC) on either side of the polar origin in the case in which dAngle is between those limits. Thus, the wrap-around issue disappears; we've "rotated it out of the way," so to speak.
About the Polar Coordinate System
It may be worth noting that in the polar coordinate system I'm using, CW angles are more positive than CC angles (unless the wrap-around is between them). This is the opposite of the polar coordinates we learn in calculus class, where angles increase in the CC (counterclockwise) direction.
This is because of the quandary that results from the decision — made long, long ago — that computer devices (like display surfaces, originally implemented on cathode ray tubes) would count the vertical direction as increasing downward, instead of upward, as we learn when we study analytic geometry and calculus.
The people who made this decision did so because they wanted to display text on the screen, with the "first" line at the top, the "second" line (line 2) below the "first" line (line 1), and so forth. To make this easier to keep track of this in their early machine code, they had the +y direction go "down," as in "toward the bottom of the screen." This decision has far-reaching and annoying consequences for people who do image geometry in code.
One of the consequences is that by flipping the direction of +y, the "sense of rotation" also flipped from the conventional "angle increases counterclockwise" sense we're used to from high-school math. This issue is mostly invisible at the coding level; it only comes out when you look at your results on the screen.
This is why it is very important to write code to visualize your results on the screen before you trust them. Here "trust them" is certainly necessary before you let your customer see them.