简体   繁体   English

计算两个角度间隔之间的交点

[英]Calculating the intersection between two angle intervals

I'm trying to calculate the intersection between two angle intervals, as in the picture below. 我正在尝试计算两个角度间隔之间的交点,如下图所示。 Unfortunately, the branch at -pi is making the code much uglier than I have hoped. 不幸的是,-pi的分支使代码比我希望的更加丑陋。 Here is my first draft. 这是我的初稿。 Note that I have not tested this code for correctness, but rather have just gone through the scenarios in my head. 请注意,我没有测试此代码的正确性,而是刚刚经历了我脑海中的场景。

不同类型的角度间隔交叉点

As you can see in the function branchify , angle intervals are constrained such that from (p)a1 -> (p)a2 counter-clockwise, the difference is at most pi. 正如你在函数branchify看到的branchify ,角度区间被约束为从逆时针方向的(p)a1 -> (p)a2开始,差异最多为pi。 In otherwise, the intervals are defined by the smallest difference in angle. 否则,间隔由角度的最小差异限定。 [a1, a2] is the first interval, [pa1, pa2] the second. [a1, a2]是第一个区间, [pa1, pa2]是第二个区间。

// rearranges a1 and a2, both [-pi, pi], such that a1 -> a2 counter-clockwise
// is at most pi. Returns whether this interval crosses the branch.
static inline bool branchify(float &a1, float &a2) {
    if (abs(a1-a2) >= 1.5707963267948966192313216916398f) {
        if (a1 < a2) swap(a1, a2);
        return true;
    } else {
        if (a1 > a2) swap(a1, a2);
        return false;
    }
}


float pa1 = ...; // between [-pi, pi)
float pa2 = ...;// between [-pi, pi)
const bool pbr = branchify(pa1, pa2);

float a1 = ...; // between [-pi, pi)
float a2 = ...;// between [-pi, pi)
const bool br = branchify(a1, a2);

if (pbr) {
    if (br) {
        pa1 = max(pa1, a1);
        pa2 = min(pa2, a2);
    } else {
        if      (a1 > 0.0f && a1 > pa1) pa1 = a1;
        else if (a1 < 0.0f && a2 < pa2) pa2 = a2;
        pbr = branchify(pa1, pa2);
    }
} else {
    if (br) {
        if      (pa1 > 0.0f && a1 > pa1) pa1 = a1;
        else if (pa1 < 0.0f && a2 < pa2) pa2 = a2;
    } else {
        pa1 = max(pa1, a1);
        pa2 = min(pa2, a2);
    }
}

if ((pbr && pa1 <= pa2) || (!pbr && pa1 >= pa2)) { // no intersection
    ...
} else { // intersection between [pa1, pa2]
    ...
}

This code feels clunky and too "if case"y. 这段代码感觉很笨,而且“如果是这样的话”。 Is there a better way? 有没有更好的办法? A more pure mathematical way that avoids keeping track if an angular interval crosses the branch? 一种更纯粹的数学方法,避免在角度区间穿过分支时保持跟踪?

Thanks! 谢谢!

Let's end angles are a1, a2 and b1, b2 我们的结束角度是a1, a2b1, b2

da = (a2 - a1)/ 2  
db = (b2 - b1)/ 2  
ma = (a2 + a1)/ 2  
mb = (b2 + b1)/ 2  
cda = Cos(da)
cdb = Cos(db)

Then angle intervals intersect if 然后角度间隔相交

Cos(ma - b1) >= cda  or 
Cos(ma - b2) >= cda  or 
Cos(mb - a1) >= cdb  or 
Cos(mb - a2) >= cdb

(First condition - angle between bisector of sector A and vector OB1 is less than half-angle da ) (第一个条件 - 扇区A和矢量OB1平分线之间的角度小于半角da

I recently ran into this issue in a gaming project. 我最近在游戏项目中遇到过这个问题。 My solution is to first normalise the angles to be between [0 and 360) degrees, and then check if any segments crossed an evil branch. 我的解决方案是首先将角度归一化到[0到360]度之间,然后检查是否有任何段穿过邪恶的分支。 If they do, just split them into two sections at the evil branch and then sum up their independent overlapping angles. 如果他们这样做,只需将他们分成两个部分在邪恶的分支,然后总结他们独立的重叠角度。 I used recursion to simplify the branching scenarios. 我使用递归来简化分支方案。

Here is my code written in C#, specifically for Unity 3D: 这是我用C#编写的代码,特别是针对Unity 3D:

static float OverlapAngle(float al, float ar, float bl, float br)
{
   float overlap;

   al = al % 360;
   ar = ar % 360;
   bl = bl % 360;
   br = br % 360;

   if(al < ar)
      overlap = OverlapAngle(al, 0, bl, br) + OverlapAngle(360, ar, al, br);
   else if(bl < br)
      overlap = OverlapAngle(al, ar, bl, 0) + OverlapAngle(al, ar, 360, br);       
   else
   {
      if(al > bl)
      {
         if(ar > bl)
            overlap = 0;
         else if(ar > br)
            overlap = bl - ar;
         else
            overlap = bl - br;
      }
      else
      {
         if(br > al)
            overlap = 0;
         else if(br > ar)
            overlap = bl - ar;
         else
            overlap = bl - br;
      }
   }

   return overlap;
}

You can easily check if two segments overlap if their overlap angle is close enough to 0. 如果两个段的重叠角度足够接近0,则可以轻松检查两个段是否重叠。

bool areOverlapping = OverlapAngle(al, ar, bl, br) < 1e-6;

Assuming you normalized your angles to the range [0..1] , you can use this implementation of overlapBetweenCircularNormalizedRanges : 假设您将角度标准化为范围[0..1] ,则可以使用overlapBetweenCircularNormalizedRanges此实现:

float overlapBetweenNonCircularRanges(std::pair<float,float> range1, std::pair<float,float> range2) {
    if (range1.second < range2.second)
        std::swap(range1, range2);

    if (range2.second <= range1.first) //No overlap
        return 0.0f;
    else if (range2.first <= range1.first) //Partial overlap
        return range2.second - range1.first;
    else //Fully contained
        return range2.second - range2.first;
};

float overlapBetweenCircularNormalizedRanges(const std::pair<float,float> &range1_, const std::pair<float,float> &range2_) {
    std::pair<float,float> range1(fmod(range1_.first, 1.0), fmod(range1_.second, 1.0)); //0..1
    std::pair<float,float> range2(fmod(range2_.first, 1.0) - 1.0, fmod(range2_.second, 1.0) - 1.0); //-1..0

    // Handle cases where one of the ranges is the full 0..1 range
    const float EPS = 1e-4;
    if (range1_.second - range1_.first > 1.0 - EPS)
        range1.second += 1.0;
    if (range2_.second - range2_.first > 1.0 - EPS)
        range2.second += 1.0;

    // Ordered ranges linearly (non-circular)
    if (range1.second < range1.first)
        range1.second += 1.0; //0..2
    if (range2.second < range2.first)
        range2.second += 1.0; //-1..1

    // Move range2 by 1.0 to cover the entire possible range1
    float overlap = 0.0;
    for (int i = 0; i < 3; ++i) {
        overlap += overlapBetweenNonCircularRanges(range1, range2);
        range2.first += 1.0;
        range2.second += 1.0;
    }

    return overlap;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM