2 डी बाउंडिंग बॉक्स चौराहे पर काम करने का सबसे तेज़ तरीका क्या है?


62

मान लें कि प्रत्येक बॉक्स ऑब्जेक्ट में गुण x, y, चौड़ाई, ऊंचाई है और उनकी उत्पत्ति उनके केंद्र में है, और यह कि न तो ऑब्जेक्ट और न ही बाउंडिंग बॉक्स घूमते हैं।


क्या ये अक्ष या वस्तु-संरेखित बाउंडिंग बॉक्स हैं?
दस

3
जब आप यह प्रश्न पूछते हैं, तो आपको भविष्य में अन्य प्रकार के चौराहों का परीक्षण करने की आवश्यकता होगी;) इसलिए मैं वस्तु / वस्तु प्रतिच्छेदन के बारे में सूची का सुझाव देता हूं । तालिका स्थिर और साथ ही गतिशील स्थितियों में सभी लोकप्रिय वस्तु प्रकारों (बक्से, गोले, त्रिकोण, चक्रवात, शंकु, ...) के बीच चौराहों को प्रदान करती है।
डेव ओ।

2
कृपया अपने प्रश्न को फिर से व्यवस्थित करने के लिए करें। मेरे दृष्टिकोण से बॉक्स का अर्थ 3 डी ऑब्जेक्ट है।
डेव ओ।

जवाबों:


55

(C-ish pseudocode - उपयुक्त रूप में भाषा अनुकूलन अनुकूलन)

bool DoBoxesIntersect(Box a, Box b) {
  return (abs(a.x - b.x) * 2 < (a.width + b.width)) &&
         (abs(a.y - b.y) * 2 < (a.height + b.height));
}

अंग्रेजी में: प्रत्येक अक्ष पर, यह देखने के लिए जांच करें कि क्या बक्से के केंद्र पर्याप्त रूप से बंद हैं या नहीं कि वे प्रतिच्छेद करेंगे। यदि वे दोनों अक्षों पर प्रतिच्छेद करते हैं, तो बक्से प्रतिच्छेद करते हैं। यदि वे नहीं करते हैं, तो वे नहीं करते हैं।

यदि आप अंतर-स्पर्श को गिनती के रूप में गिनना चाहते हैं, तो आप <= को बदल सकते हैं। यदि आप एक विशिष्ट एज-टच-ओनली फॉर्मूला चाहते हैं, तो आप == का उपयोग नहीं कर सकते - यदि कोने स्पर्श करते हैं तो आपको बताएंगे कि क्या किनारों को छूता है। आप तार्किक रूप से इसके समकक्ष कुछ करना चाहते हैं return DoBoxesIntersectOrTouch(a, b) && !DoBoxesIntersect(a, b)

यह ध्यान देने योग्य है कि आप पूर्ण चौड़ाई और पूर्ण ऊँचाई के अलावा (या इसके बजाय) आधी-चौड़ाई और आधी-ऊँचाई को संग्रहीत करके एक छोटी लेकिन महत्वपूर्ण गति बढ़ा सकते हैं। दूसरी ओर, प्रदर्शन अड़चन के लिए 2 डी बाउंडिंग बॉक्स चौराहे के लिए यह दुर्लभ है।


9
यह स्पष्ट रूप से मानता है कि बक्से अक्ष-संरेखित हैं।
दस

1
Abs को विशेष रूप से धीमा नहीं होना चाहिए - सशर्त से कम नहीं, कम से कम, और एब्स के बिना इसे करने का एकमात्र तरीका (जो मुझे पता है) में अतिरिक्त सशर्त शामिल हैं।
ज़ोरबहुत

4
हाँ, यह धुरी-संरेखित बक्से मानती है। वर्णित संरचनाओं में रोटेशन को इंगित करने का कोई तरीका नहीं है, हालांकि, इसलिए मुझे लगा कि यह सुरक्षित है।
जोर्बहुत

3
एक्टिऑस्क्रिप्ट (मुख्य रूप से पूर्णांक कैल्क) में कैलेब्स को तेज करने के लिए यहां कुछ अच्छे सुझाव दिए गए हैं: lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math मैं यह पोस्ट कर रहा हूं, क्योंकि इसमें एक तेज़ शामिल है Math.abs () के लिए प्रतिस्थापन, जो वास्तव में Actionscript में चीजों को धीमा करने के लिए जाता है (प्रदर्शन महत्वपूर्ण सामग्री का बोलना)।
बंमज़ैक

2
आप याद कर रहे हैं कि उनके केंद्र में मूल है, बाएं किनारे पर नहीं। एक बॉक्स जो 0 से 10 तक चलता है, वास्तव में "x = 5" होगा, जबकि 8 से 12 तक चलने वाले बॉक्स में "x" 10 "होगा।" आप abs(5 - 10) * 2 < (10 + 4)=> के साथ अंत करते हैं 10 < 14। आपको टॉपलैफ्ट-कॉर्नर और आकार के साथ काम करने के लिए कुछ सरल ट्विकिंग करने की आवश्यकता होगी।
ZorbaTHut

37

यह एक्स और वाई अक्ष के साथ संरेखित दो आयतों के लिए काम करता है।
प्रत्येक आयत में गुण होते हैं:
"बाएं", इसके बाईं ओर का x समन्वय,
"शीर्ष", इसके शीर्ष पक्ष का y निर्देशांक,
"दाएं", इसके दाईं ओर का x समन्वय,
"नीचे", का y निर्देशांक इसके नीचे की तरफ,

function IntersectRect(r1:Rectangle, r2:Rectangle):Boolean {
    return !(r2.left > r1.right
        || r2.right < r1.left
        || r2.top > r1.bottom
        || r2.bottom < r1.top);
}

ध्यान दें कि यह एक समन्वय प्रणाली के लिए डिज़ाइन किया गया है जिसमें + y अक्ष नीचे इंगित करता है और + x अक्ष दाईं ओर (यानी विशिष्ट स्क्रीन / पिक्सेल निर्देशांक) के लिए निर्देशित होता है। इसे एक विशिष्ट कार्टेशियन प्रणाली के लिए अनुकूलित करने के लिए जिसमें + y को ऊपर की ओर निर्देशित किया जाता है, ऊर्ध्वाधर अक्षों के साथ तुलना को उल्टा किया जाएगा, जैसे:

return !(r2.left > r1.right
    || r2.right < r1.left
    || r2.top < r1.bottom
    || r2.bottom > r1.top);

विचार के लिए सभी संभव शर्तों जिस पर आयतों होगा कब्जा करने के लिए है नहीं ओवरलैप, और फिर जवाब नकारना देखने के लिए अगर वे कर रहे हैं ओवरलैप। कुल्हाड़ियों की दिशा के बावजूद, यह देखना आसान है कि दो आयतें ओवरलैप नहीं होंगी यदि:

  • r2 के बाएं किनारे को r1 के दाएं किनारे से अधिक दाईं ओर है

     ________     ________
    |        |   |        |
    |   r1   |   |   r2   |
    |        |   |        |
    |________|   |________|
    
  • या r2 के दाएं किनारे को r1 के बाएं किनारे से आगे छोड़ा गया है

     ________     ________
    |        |   |        |
    |   r2   |   |   r1   |
    |        |   |        |
    |________|   |________|
    
  • या r2 का ऊपरी किनारा r1 के निचले किनारे से नीचे है

     ________ 
    |        |
    |   r1   |
    |        |
    |________|
     ________ 
    |        |
    |   r2   |
    |        |
    |________|
    
  • या r2 के निचले किनारे को r1 के शीर्ष किनारे से ऊपर रखा गया है

     ________ 
    |        |
    |   r2   |
    |        |
    |________|
     ________ 
    |        |
    |   r1   |
    |        |
    |________|
    

मूल कार्य - और यह क्यों काम करता है - का एक वैकल्पिक विवरण यहां पाया जा सकता है: http://tekpool.wordpress.com/2006/10/11/rectangle-intersection-determine-if-two-given-rectangles-intersect प्रत्येक-दूसरे या नहीं /


1
आश्चर्यजनक रूप से सहज और एक बार फिर दिखाता है कि जब उत्तर खोजना बहुत कठिन है, तो एक विपरीत प्रश्न का उत्तर खोजने की कोशिश करने से आपको मदद मिल सकती है। धन्यवाद!
लोदीविज्क

1
आपको उल्लेख करना चाहिए कि y अक्ष नीचे इंगित करता है (एक छवि में)। अन्यथा असमानताएं r2.top> r1.bottom और r2.bottom <r1.top को उलटने की जरूरत है।
user1443778

@ user1443778 अच्छी पकड़! मैं आगे गया और एक समन्वय प्रणाली-अज्ञेयवादी तरीके से भी इस एल्गोरिथम के पीछे के तर्क को स्पष्ट किया।
पनकडूडल

11

यदि आप ऑब्जेक्ट-संरेखित बाउंडिंग बॉक्स चाहते हैं, तो मेटाटेट द्वारा सेपरेशन अक्ष प्रमेय पर इस ट्यूटोरियल का प्रयास करें : http://www.metanetsoftware.com/technique/tutorialA.html

SAT सबसे तेज़ समाधान नहीं है, लेकिन यह अपेक्षाकृत सरल है। आप एक एकल लाइन (या एक विमान अगर 3 डी) खोजने की कोशिश कर रहे हैं जो आपकी वस्तुओं को अलग कर देगा। यदि यह रेखा मौजूद है, तो यह आपके बक्से में से एक के किनारे पर पैरेलेल होने के लिए ग्वारेंटेड है, इसलिए आप सभी किनारों के परीक्षण के माध्यम से यह देखने के लिए पुनरावृति करते हैं कि क्या यह बक्से को अलग करता है।

यह केवल x / y अक्ष के लिए विवश होकर अक्ष-संरेखित बक्से के लिए भी काम करता है।


मैं रोटेशन के बारे में नहीं सोच रहा था, लेकिन धन्यवाद कि यह एक दिलचस्प कड़ी है।
इयान

5

DoBoxesIntersect ऊपर एक अच्छा जोड़ीदार समाधान है। हालाँकि, यदि आपके पास बहुत सारे बॉक्स हैं, तो आपके पास अभी भी एक O (N ^ 2) समस्या है, और आप पा सकते हैं कि आपको इसके ऊपर कुछ करने की आवश्यकता है जैसे कि काज को संदर्भित करता है। (3 डी टक्कर का पता लगाने वाले साहित्य में, यह एक व्यापक-चरण और संकीर्ण-चरण एल्गोरिथ्म दोनों के रूप में जाना जाता है। हम ओवरलैप्स के सभी संभावित जोड़े को खोजने के लिए वास्तव में तेजी से कुछ करेंगे, और फिर यह देखने के लिए अधिक महंगा होगा कि क्या हमारे संभव है। जोड़े वास्तविक जोड़े हैं।)

पहले जो व्यापक चरण का एल्गोरिथ्म मैंने उपयोग किया है वह "स्वीप-एंड-प्रून" है; 2 डी के लिए, आप प्रत्येक बॉक्स के आरंभ और अंत की दो क्रमबद्ध सूची बनाए रखेंगे। जब तक बॉक्स मूवमेंट फ्रेम से फ्रेम तक बॉक्स स्केल नहीं होता है, तब तक इन सूचियों के क्रम में बहुत बदलाव नहीं होता है, और इसलिए आप इसे बनाए रखने के लिए बुलबुले या सम्मिलन प्रकार का उपयोग कर सकते हैं। "रियल-टाइम रेंडरिंग" पुस्तक में आपके द्वारा किए जा सकने वाले अनुकूलन पर एक अच्छा राइटअप है, लेकिन यह एन बॉक्स, के के ओवरलैप और उत्कृष्ट वास्तविक दुनिया के साथ, व्यापक चरण में ओ (एन + के) समय तक उबलता है। प्रदर्शन यदि आप N ^ 2 बूलियनों को ट्रैक करने के लिए रख सकते हैं, तो किस बॉक्स के जोड़े फ्रेम-टू-फ्रेम से इंटरसेक्ट कर रहे हैं। आपके पास तब O (N + K ^ 2) समय है, जो कि << O (N ^ 2) है यदि आपके पास कई बॉक्स हैं लेकिन केवल कुछ ओवरलैप हैं।


5

एक बहुत ही सरल समस्या के लिए यहां गणित का बहुत कुछ है, मान लें कि हमारे पास एक आयत, शीर्ष, बाएं, नीचे, दाएं के लिए निर्धारित 4 बिंदु हैं ...

यह निर्धारित करने के मामले में कि क्या 2 रेक्ट्स टकराते हैं, हमें केवल यह देखने की आवश्यकता है कि सभी संभावित चरम सीमाएं टकराव को रोकेंगी, यदि इनमें से कोई भी पूरा नहीं हुआ है, तो 2 रेक्टर्स को टकरा जाना चाहिए, यदि आप सीमा टकराव को शामिल करना चाहते हैं, तो बस> और <को बदलें उपयुक्त के साथ> = और = <।

struct aRect{
  float top;
  float left;
  float bottom;
  float right;
};

bool rectCollision(rect a, rect b)
{
  return ! ( b.left > a.right || b.right < a.left || b.top < a.bottom || b.bottom > a.top);
}

मैं ईमानदारी से यकीन नहीं कर रहा हूँ कि यह शीर्ष-मतदान का जवाब क्यों नहीं है। यह सरल, सही और कुशल है।
3Dave

3

ZorbaTHut के उत्तर का वैकल्पिक संस्करण:

bool DoBoxesIntersect(Box a, Box b) {
     return (abs(a.x - b.x) < (a.width + b.width) / 2) &&
     (abs(a.y - b.y) < (a.height + b.height) / 2);
}

वास्तव में यह अंकगणित या तो ठीक काम करता है। आप <के दोनों ओर कोई भी अंकगणितीय ऑपरेशन कर सकते हैं और यह इसे परिवर्तित नहीं करता है (एक नकारात्मक माध्यम से गुणा करना, हालांकि, आपको कम से कम बदलना होगा)। उस उदाहरण में, बक्से आपस में नहीं टकराए। यदि बॉक्स A का केंद्र 1 पर है, तो यह -4 से 6 तक फैला है। बॉक्स b 10 में केंद्र है, और 7.5 से 12.5 तक फैला है, वहां कोई टकराव नहीं है ... अब, जिस विधि से वॉलकोलो पोस्ट किया गया है वह केवल सही नहीं है, लेकिन उन भाषाओं पर तेज़ी से चलेगा, जो लघु परिपालन को कार्यान्वित करती हैं, क्योंकि अधिकांश जाँचें किसी भी तरह झूठी होंगी, लघु परिशोधन के बाद में कटौती हो सकती है
Deleter

हां मुझे इसका अहसास तब हुआ जब मैं आज सुबह जगा। क्रिस ने मुझे अपने <> मिक्स अप बम्बूलेस किया।
आईएन

1
दो समस्याएं: पहला, विभाजन गुणन की तुलना में काफी धीमा हो जाता है। दूसरा, यदि शामिल किए गए मान पूर्णांक हैं, तो इसका परिणाम कुछ पूर्णांक ट्रंकेशन समस्याओं (अक्ष = 0, bx = 9, a.width = 9, b.width = 10: abs (0-9) <(9 + 10) में हो सकता है) / 2, 9 <19/2, 9 <9, फ़ंक्शन इस तथ्य के बावजूद गलत है कि बक्से निश्चित रूप से प्रतिच्छेद करते हैं।)
ZorbaTHut

2

आप जिस समस्या को हल करने का प्रयास करते हैं, उसके आधार पर आप उन्हें स्थानांतरित करते समय अपनी वस्तु पर नज़र रखने से बेहतर हो सकते हैं, अर्थात, सॉर्ट किए गए x प्रारंभ और समाप्ति स्थितियों की सूची और शुरुआत और अंत y पदों के लिए एक रखें। यदि आपको ओवरलैप चेक का बहुत कुछ करना है और इसलिए अनुकूलन करने की आवश्यकता है, तो आप इसे अपने लाभ के लिए उपयोग कर सकते हैं, जैसा कि आप तुरंत देख सकते हैं कि कौन आपके बाएं को बंद कर रहा है, हर कोई जो समाप्त हो रहा है, उसे छोड़ दिया जा सकता है हाथोंहाथ। टॉप, बॉटम और राईट के लिए एक ही आवेदन करें।
बहीखाता पद्धति का समय निश्चित रूप से खर्च होता है, इसलिए यह कुछ चलती वस्तुओं के साथ अधिक ओवरलैप चेक के साथ स्थिति के लिए अधिक अनुकूल है।
एक अन्य विकल्प स्थानिक हैशिंग है, जहां आप अनुमानित स्थिति के आधार पर वस्तुओं को बाल्टी करते हैं (आकार कई बाल्टी में डाल सकता है), लेकिन फिर से, केवल अगर बहुत सी वस्तुएं हैं, तो उनमें से कुछ अपेक्षाकृत बहीखाते की लागत के कारण प्रति चलन में चलती हैं।
मूल रूप से कुछ भी जो बचता है (n * n) / 2 (यदि आप वस्तु की जाँच करते हैं तो b के विरुद्ध आपको स्पष्ट रूप से b को जाँचना नहीं पड़ेगा), बाउंड चेक की जांच को अनुकूलित करने से अधिक मदद करता है। यदि बाउंडिंग बॉक्स चेक एक अड़चन है, तो मैं समस्या के वैकल्पिक समाधानों पर ध्यान देने की सलाह दूंगा।


2

केंद्रों के बीच की दूरी कोनों के बीच की दूरी के समान नहीं है (जब एक बॉक्स उदाहरण के लिए दूसरे के अंदर होता है), इसलिए INERERAL, यह समाधान सही है (मुझे लगता है)।

केंद्रों के बीच की दूरी (के लिए, कहते हैं, एक्स): abs(x1+1/2*w1 - x2+1/2*w2)या1/2 * abs(2*(x1-x2)+(w1-w2)

न्यूनतम दूरी है 1/2 w1 + 1/2 w2 or 1/2 (w1+w2)। पड़ाव रद्द ताकि ..

return 
ABS(2*(x1 - x2) + (w1-w2) ) < (w1+w2)) &&
ABS(2*(y1 - y2) + (h1-h2) ) < (h1+h2));

1
वहां "वापसी" कथन के साथ क्या है?
doppelgreener

1

यहाँ जावा में मेरा कार्यान्वयन एक दो-पूरक वास्तुकला है । यदि आप दो-पूरक पर नहीं हैं, तो इसके बजाय एक मानक Math.abs फ़ंक्शन कॉल का उपयोग करें:

boolean intersects(IntAxisAlignedBox left, IntAxisAlignedBox right) {
    return
        (
            lineDeltaFactor(left.min.x, left.max.x, right.min.x, right.max.x) |
            lineDeltaFactor(left.min.y, left.max.y, right.min.y, right.max.y) |
            lineDeltaFactor(left.min.z, left.max.z, right.min.z, right.max.z)
        ) == 0;
}

int lineDeltaFactor(int leftMin, int leftMax, int rightMin, int rightMax) {
    final int
            leftWidth = leftMax - leftMin,
            rightWidth = rightMax - rightMin,

            leftMid = leftMin + ((leftMax - leftMin) >> 1),
            rightMid = rightMin + ((rightMax - rightMin) >> 1);

    return (abs(leftMid - rightMid) << 1) / (leftWidth + rightWidth + 1);
}

int abs(int value) {
    final int mask = value >> (Integer.SIZE - 1);

    value ^= mask;
    value += mask & 1;
    return value;
}

एक आधा-सभ्य संकलक / एलएलवीएम इनलाइन मानते हुए महंगी स्टैक बाजीगरी और वी-टेबल लुक-अप से बचने के लिए इन कार्यों का विस्तार करता है। यह उन इनपुट मानों के लिए विफल हो जाएगा जो 32-बिट एक्सट्रीम (यानी Integer.MAX_VALUEऔर Integer.MIN_VALUE) के करीब हैं ।


0

सबसे तेज़ तरीका एकल वेक्टर रजिस्टर में सभी 4 मूल्यों को मिलाता है।

निम्नलिखित मानों के साथ एक वेक्टर में बक्से को स्टोर करें [ min.x, min.y, -max.x, -max.y ]। यदि आप इस तरह से बक्से को स्टोर करते हैं, तो चौराहे का परीक्षण केवल 3 सीपीयू निर्देश लेता है:

_mm_shuffle_ps दूसरे बॉक्स फ़्लिपिंग मिनट और अधिकतम हिस्सों को फिर से व्यवस्थित करने के लिए।

_mm_xor_ps_mm_set1_ps(-0.0f)दूसरे बॉक्स में सभी 4 मानों के संकेत फ्लिप करने के लिए जादुई संख्या के साथ ।

_mm_cmple_ps एक दूसरे के साथ सभी 4 मूल्यों की तुलना करने के लिए, दो रजिस्टरों की तुलना करें:

[ a.min.x, a.min.y, -a.max.x, -a.max.y ] < [ b.max.x, b.max.y, -b.min.x, -b.min.y ]

अंत में, यदि आवश्यक हो, तो _mm_movemask_psवेक्टर यूनिट से स्केलर रजिस्टर में परिणाम प्राप्त करने के लिए। मान 0 का अर्थ है बक्सों को काट दिया गया। या यदि आपके पास 2 से अधिक बक्से हैं, तो इसकी आवश्यकता नहीं है, वेक्टर रजिस्टरों में मूल्यों को छोड़ दें और कई बक्से से परिणामों को संयोजित करने के लिए बिटवाइज़ संचालन का उपयोग करें।

आपने भाषा और न ही प्लेटफ़ॉर्म निर्दिष्ट नहीं किया है, लेकिन SIMD के लिए समर्थन, या बहुत समान है, सभी प्लेटफ़ॉर्म और भाषाओं में उपलब्ध है। मोबाइल पर, ARM में बहुत समान सामान के साथ NEON SIMD है। .NET में System.Runtime.Intrinsics namespace, और इसी तरह वेक्टर128 है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.