[英]Calculating the intersection between two angle intervals
我正在嘗試計算兩個角度間隔之間的交點,如下圖所示。 不幸的是,-pi的分支使代碼比我希望的更加丑陋。 這是我的初稿。 請注意,我沒有測試此代碼的正確性,而是剛剛經歷了我腦海中的場景。
正如你在函數branchify
看到的branchify
,角度區間被約束為從逆時針方向的(p)a1 -> (p)a2
開始,差異最多為pi。 否則,間隔由角度的最小差異限定。 [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]
...
}
這段代碼感覺很笨,而且“如果是這樣的話”。 有沒有更好的辦法? 一種更純粹的數學方法,避免在角度區間穿過分支時保持跟蹤?
謝謝!
我們的結束角度是a1, a2
和b1, b2
da = (a2 - a1)/ 2
db = (b2 - b1)/ 2
ma = (a2 + a1)/ 2
mb = (b2 + b1)/ 2
cda = Cos(da)
cdb = Cos(db)
然后角度間隔相交
Cos(ma - b1) >= cda or
Cos(ma - b2) >= cda or
Cos(mb - a1) >= cdb or
Cos(mb - a2) >= cdb
(第一個條件 - 扇區A
和矢量OB1
平分線之間的角度小於半角da
)
我最近在游戲項目中遇到過這個問題。 我的解決方案是首先將角度歸一化到[0到360]度之間,然后檢查是否有任何段穿過邪惡的分支。 如果他們這樣做,只需將他們分成兩個部分在邪惡的分支,然后總結他們獨立的重疊角度。 我使用遞歸來簡化分支方案。
這是我用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;
}
如果兩個段的重疊角度足夠接近0,則可以輕松檢查兩個段是否重疊。
bool areOverlapping = OverlapAngle(al, ar, bl, br) < 1e-6;
假設您將角度標准化為范圍[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.