मान लें कि प्रत्येक बॉक्स ऑब्जेक्ट में गुण x, y, चौड़ाई, ऊंचाई है और उनकी उत्पत्ति उनके केंद्र में है, और यह कि न तो ऑब्जेक्ट और न ही बाउंडिंग बॉक्स घूमते हैं।
मान लें कि प्रत्येक बॉक्स ऑब्जेक्ट में गुण x, y, चौड़ाई, ऊंचाई है और उनकी उत्पत्ति उनके केंद्र में है, और यह कि न तो ऑब्जेक्ट और न ही बाउंडिंग बॉक्स घूमते हैं।
जवाबों:
(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 डी बाउंडिंग बॉक्स चौराहे के लिए यह दुर्लभ है।
abs(5 - 10) * 2 < (10 + 4)
=> के साथ अंत करते हैं 10 < 14
। आपको टॉपलैफ्ट-कॉर्नर और आकार के साथ काम करने के लिए कुछ सरल ट्विकिंग करने की आवश्यकता होगी।
यह एक्स और वाई अक्ष के साथ संरेखित दो आयतों के लिए काम करता है।
प्रत्येक आयत में गुण होते हैं:
"बाएं", इसके बाईं ओर का 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 प्रत्येक-दूसरे या नहीं /
यदि आप ऑब्जेक्ट-संरेखित बाउंडिंग बॉक्स चाहते हैं, तो मेटाटेट द्वारा सेपरेशन अक्ष प्रमेय पर इस ट्यूटोरियल का प्रयास करें : http://www.metanetsoftware.com/technique/tutorialA.html
SAT सबसे तेज़ समाधान नहीं है, लेकिन यह अपेक्षाकृत सरल है। आप एक एकल लाइन (या एक विमान अगर 3 डी) खोजने की कोशिश कर रहे हैं जो आपकी वस्तुओं को अलग कर देगा। यदि यह रेखा मौजूद है, तो यह आपके बक्से में से एक के किनारे पर पैरेलेल होने के लिए ग्वारेंटेड है, इसलिए आप सभी किनारों के परीक्षण के माध्यम से यह देखने के लिए पुनरावृति करते हैं कि क्या यह बक्से को अलग करता है।
यह केवल x / y अक्ष के लिए विवश होकर अक्ष-संरेखित बक्से के लिए भी काम करता है।
DoBoxesIntersect ऊपर एक अच्छा जोड़ीदार समाधान है। हालाँकि, यदि आपके पास बहुत सारे बॉक्स हैं, तो आपके पास अभी भी एक O (N ^ 2) समस्या है, और आप पा सकते हैं कि आपको इसके ऊपर कुछ करने की आवश्यकता है जैसे कि काज को संदर्भित करता है। (3 डी टक्कर का पता लगाने वाले साहित्य में, यह एक व्यापक-चरण और संकीर्ण-चरण एल्गोरिथ्म दोनों के रूप में जाना जाता है। हम ओवरलैप्स के सभी संभावित जोड़े को खोजने के लिए वास्तव में तेजी से कुछ करेंगे, और फिर यह देखने के लिए अधिक महंगा होगा कि क्या हमारे संभव है। जोड़े वास्तविक जोड़े हैं।)
पहले जो व्यापक चरण का एल्गोरिथ्म मैंने उपयोग किया है वह "स्वीप-एंड-प्रून" है; 2 डी के लिए, आप प्रत्येक बॉक्स के आरंभ और अंत की दो क्रमबद्ध सूची बनाए रखेंगे। जब तक बॉक्स मूवमेंट फ्रेम से फ्रेम तक बॉक्स स्केल नहीं होता है, तब तक इन सूचियों के क्रम में बहुत बदलाव नहीं होता है, और इसलिए आप इसे बनाए रखने के लिए बुलबुले या सम्मिलन प्रकार का उपयोग कर सकते हैं। "रियल-टाइम रेंडरिंग" पुस्तक में आपके द्वारा किए जा सकने वाले अनुकूलन पर एक अच्छा राइटअप है, लेकिन यह एन बॉक्स, के के ओवरलैप और उत्कृष्ट वास्तविक दुनिया के साथ, व्यापक चरण में ओ (एन + के) समय तक उबलता है। प्रदर्शन यदि आप N ^ 2 बूलियनों को ट्रैक करने के लिए रख सकते हैं, तो किस बॉक्स के जोड़े फ्रेम-टू-फ्रेम से इंटरसेक्ट कर रहे हैं। आपके पास तब O (N + K ^ 2) समय है, जो कि << O (N ^ 2) है यदि आपके पास कई बॉक्स हैं लेकिन केवल कुछ ओवरलैप हैं।
एक बहुत ही सरल समस्या के लिए यहां गणित का बहुत कुछ है, मान लें कि हमारे पास एक आयत, शीर्ष, बाएं, नीचे, दाएं के लिए निर्धारित 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);
}
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);
}
आप जिस समस्या को हल करने का प्रयास करते हैं, उसके आधार पर आप उन्हें स्थानांतरित करते समय अपनी वस्तु पर नज़र रखने से बेहतर हो सकते हैं, अर्थात, सॉर्ट किए गए x प्रारंभ और समाप्ति स्थितियों की सूची और शुरुआत और अंत y पदों के लिए एक रखें। यदि आपको ओवरलैप चेक का बहुत कुछ करना है और इसलिए अनुकूलन करने की आवश्यकता है, तो आप इसे अपने लाभ के लिए उपयोग कर सकते हैं, जैसा कि आप तुरंत देख सकते हैं कि कौन आपके बाएं को बंद कर रहा है, हर कोई जो समाप्त हो रहा है, उसे छोड़ दिया जा सकता है हाथोंहाथ। टॉप, बॉटम और राईट के लिए एक ही आवेदन करें।
बहीखाता पद्धति का समय निश्चित रूप से खर्च होता है, इसलिए यह कुछ चलती वस्तुओं के साथ अधिक ओवरलैप चेक के साथ स्थिति के लिए अधिक अनुकूल है।
एक अन्य विकल्प स्थानिक हैशिंग है, जहां आप अनुमानित स्थिति के आधार पर वस्तुओं को बाल्टी करते हैं (आकार कई बाल्टी में डाल सकता है), लेकिन फिर से, केवल अगर बहुत सी वस्तुएं हैं, तो उनमें से कुछ अपेक्षाकृत बहीखाते की लागत के कारण प्रति चलन में चलती हैं।
मूल रूप से कुछ भी जो बचता है (n * n) / 2 (यदि आप वस्तु की जाँच करते हैं तो b के विरुद्ध आपको स्पष्ट रूप से b को जाँचना नहीं पड़ेगा), बाउंड चेक की जांच को अनुकूलित करने से अधिक मदद करता है। यदि बाउंडिंग बॉक्स चेक एक अड़चन है, तो मैं समस्या के वैकल्पिक समाधानों पर ध्यान देने की सलाह दूंगा।
केंद्रों के बीच की दूरी कोनों के बीच की दूरी के समान नहीं है (जब एक बॉक्स उदाहरण के लिए दूसरे के अंदर होता है), इसलिए 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));
यहाँ जावा में मेरा कार्यान्वयन एक दो-पूरक वास्तुकला है । यदि आप दो-पूरक पर नहीं हैं, तो इसके बजाय एक मानक 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
) के करीब हैं ।
सबसे तेज़ तरीका एकल वेक्टर रजिस्टर में सभी 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 है।