यदि कोई बिंदु एक घुमाए गए आयत के अंदर है, तो मैं कुशलतापूर्वक कैसे जांच करूं?


11

अनुकूलन के लिए भाग, सीखने के उद्देश्यों के लिए भाग, मैं यह पूछने की हिम्मत करूंगा: मैं सबसे कुशलता से जांच कर सकता हूं कि 2 Pडी घुमाया गया आयत के अंदर XYZWसी # या सी ++ का उपयोग कर रहा है या नहीं?

वर्तमान में, मैं जो कुछ भी कर रहा हूं, वह " टाइम इन ट्रायंगल " एल्गोरिदम का उपयोग कर रहा है, जो कि रियल टाइम कोलिजन डिटेक्शन नामक पुस्तक में पाया गया है , और इसे दो बार चलाने (आयत बनाने वाले दो त्रिकोणों के लिए, XYZ और XZW कहते हैं):

bool PointInTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
 // Compute vectors        
 Vector2 v0 = C - A;
 Vector2 v1 = B - A;
 Vector2 v2 = P - A;

 // Compute dot products
 float dot00 = Vector2.Dot(v0, v0);
 float dot01 = Vector2.Dot(v0, v1);
 float dot02 = Vector2.Dot(v0, v2);
 float dot11 = Vector2.Dot(v1, v1);
 float dot12 = Vector2.Dot(v1, v2);

 // Compute barycentric coordinates
 float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
 float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
 float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

 // Check if point is in triangle
 if(u >= 0 && v >= 0 && (u + v) < 1)
    { return true; } else { return false; }
}


bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
 if(PointInTriangle(X,Y,Z,P)) return true;
 if(PointInTriangle(X,Z,W,P)) return true;
 return false;
}

हालांकि, मुझे लगता है कि एक क्लीनर और तेज़ तरीका हो सकता है। विशेष रूप से, गणित के संचालन की संख्या को कम करने के लिए।


क्या आपके पास कई बिंदु हैं, या क्या आपके पास कई आयतें हैं? इससे पहले कि आप इस तरह के एक छोटे से काम को अनुकूलित करने का प्रयास करें, यह पहला सवाल है।
सैम होसेवर

अच्छी बात। मेरे पास बहुत अधिक अंक होंगे, लेकिन जांचने के लिए आयतों का और भी अधिक।
लुई 15

एक घुमाए गए आयत को एक बिंदु की दूरी खोजने के बारे में संबंधित प्रश्न । यह उस का एक पतित मामला है (केवल दूरी 0 के लिए जाँचने पर)। बेशक, ऐसे अनुकूलन होंगे जो यहां लागू होते हैं जो वहां नहीं हैं।
एको

क्या आपने आयत के संदर्भ फ्रेम में बिंदु को घुमाने पर विचार किया है?
रिचर्ड टिंगल

@RichardTingle वास्तव में मैं शुरुआत में नहीं था। बाद में मैंने किया, क्योंकि मुझे लगता है कि नीचे दिए गए उत्तरों में से एक से संबंधित है। लेकिन सिर्फ यह स्पष्ट करने के लिए: आयतों के संदर्भ फ्रेम में बिंदु को घुमाने के बाद आप जो सुझाव दे रहे हैं, उसके बाद अधिकतम ।x, min.x, आदि के बीच तार्किक तुलना द्वारा सिर्फ शामिल किए जाने की जांच करनी चाहिए?
लुई 15

जवाबों:


2

अंतिम स्थिति को बदलने के लिए एक आसान और सरल अनुकूलन होगा PointInTriangle:

bool PointInRectangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) {
  ...
  if(u >= 0 && v >= 0 && u <= 1 && v <= 1)
      { return true; } else { return false; }
  }
}

कोड बहुत PointInRectangleपहले से ही था, (u + v) < 1यह देखने के लिए कि क्या यह आयत के "दूसरे" त्रिकोण में नहीं है , हालत यह थी।

वैकल्पिक रूप से, आप isLeftचार बार (पृष्ठ पर पहला कोड उदाहरण भी, बहुत समझाया गया है) परीक्षण कर सकते हैं, और यह देख सकते हैं कि वे सभी एक ही संकेत के साथ परिणाम देते हैं (जो कि इस बात पर निर्भर करता है कि अंक दक्षिणावर्त या वामावर्त क्रम में दिए गए थे) अंदर होने की बात। यह किसी अन्य उत्तल बहुभुज के लिए भी काम करता है।

float isLeft( Point P0, Point P1, Point P2 )
{
    return ( (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y) );
}
bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
    return (isLeft(X, Y, P) > 0 && isLeft(Y, Z, P) > 0 && isLeft(Z, W, P) > 0 && isLeft(W, X, p) > 0);
}

उत्तम। मुझे नहीं पता कि क्या मैं आपके सुझाव को पसंद करता हूं, जो कि वास्तव में तेजी से और मेरी तुलना में बहुत अधिक सुरुचिपूर्ण है, या यदि मुझे अधिक पसंद है, तो आपने देखा है कि मेरा पॉइंटइन कोड आसानी से पॉइंटइनक्राइक बन सकता है! धन्यवाद
12:15 पर लुई 15

isLeftविधि के लिए +1 । इसके लिए ट्रिगर फ़ंक्शंस की आवश्यकता नहीं है (जैसा कि Vector2.Dotहोता है), जो चीज़ों को बहुत गति देता है।
एको

Btw, कोड को घुमाया नहीं जा सकता था (परीक्षण नहीं किया; इस कंप्यूटर में कैसे नहीं है), सीधे मुख्य समारोह के भीतर isLeft को शामिल करके, और "&&" ऑपरेटरों द्वारा "" का प्रतिस्थापन करके। उलटा तर्क गर्त? public static bool PointInRectangle(Vector2 P, Vector2 X, Vector2 Y, Vector2 Z, Vector2 W) { return !(( (Y.x - X.x) * (P.y - X.y) - (P.x - X.x) * (Y.y - X.y) ) < 0 || ( (Z.x - Y.x) * (P.y - Y.y) - (P.x - Y.x) * (Z.y - Y.y) ) < 0 || ( (W.x - Z.x) * (P.y - Z.y) - (P.x - Z.x) * (W.y - Z.y) ) < 0 || ( (X.x - W.x) * (P.y - W.y) - (P.x - W.x) * (X.y - W.y) ) < 0 ); }
लुई 15

1
@ लुई 15 मुझे नहीं लगता कि आपको इसकी आवश्यकता है - दोनों && और || एक नकारात्मक / सकारात्मक पाया गया था (या एक और कारण था?) आगे बयानों को क्रियान्वित करना बंद कर देंगे। इनलाइनर के isLeftरूप में घोषणा करना कंपाइलर आपके लिए कुछ ऐसा ही करेगा (और शायद बेहतर तब आप कर सकते थे, क्योंकि कंपाइलर लिखने वाले इंजीनियर सीपीयू को सबसे अच्छी तरह से जानते थे, जो भी विकल्प सबसे तेज है) आपके कोड को उसी या बेहतर प्रभाव के साथ अधिक पठनीय बनाता है।
वंड्रा

8

संपादित करें: एल्गोरिदम को बेहतर बनाने के लिए सुझाए गए नकारात्मक गोलाकार बाउंड चेक की दक्षता के बारे में ओपी टिप्पणी संदेहपूर्ण रही है कि क्या जांच करता है कि एक मनमाना 2 डी बिंदु एक घुमाया और / या चलती आयत के भीतर है। अपने 2D गेम इंजन (OpenGL / C ++) पर थोड़ा सा चक्कर लगाते हुए, मैं OPs वर्तमान बिंदु-इन-आयत-चेक एल्गोरिदम (और रूपांतरों) के खिलाफ अपने एल्गोरिथ्म का प्रदर्शन बेंचमार्क प्रदान करके अपने उत्तर को पूरक करता हूं।

मैंने मूल रूप से एल्गोरिथ्म को जगह में छोड़ने का सुझाव दिया (जैसा कि यह लगभग इष्टतम है), लेकिन मात्र खेल तर्क के माध्यम से सरल करें: (1) मूल आयत के चारों ओर एक पूर्व-संसाधित सर्कल का उपयोग करके; (2) एक दूरी की जाँच करें और यदि बिंदु दिए गए सर्कल के भीतर है; (3) ओपी या अन्य किसी भी सरल एल्गोरिथ्म का उपयोग करें (मैं एक अन्य उत्तर में प्रदान की गई आइलेट एल्गोरिथ्म की सलाह देता हूं)। मेरे सुझाव के पीछे तर्क यह है कि एक बिंदु के घेरे के भीतर की जाँच यह है कि घुमाए गए आयत या किसी अन्य बहुभुज की सीमा जाँच की तुलना में काफी अधिक कुशल है।

बेंचमार्क टेस्ट के लिए मेरा प्रारंभिक परिदृश्य एक विवश स्थान में बड़ी संख्या में दिखने और गायब होने वाले डॉट्स (जिनकी स्थिति प्रत्येक गेम-लूप में बदल जाती है) को चलाने के लिए है, जो लगभग 20 घूर्णन / मूविंग वर्गों से भरा होगा। मैंने चित्रण प्रयोजनों के लिए एक वीडियो ( यूट्यूब लिंक ) प्रकाशित किया है। मापदंडों पर ध्यान दें: बेतरतीब ढंग से दिखने वाले डॉट्स, संख्या या आयतों की संख्या। मैं निम्नलिखित मानकों के साथ बेंचमार्क करूंगा:

बंद : ओपी द्वारा प्रदान किए गए सीधे सरल एल्गोरिथ्म बिना सर्कल बाउंड्री नेगेटिव चेक के

चालू : पहले बहिष्करण जाँच के रूप में आयतों के आसपास प्रति-संसाधित (सीमा) हलकों का उपयोग करना

ON + Stack : स्टैक पर लूप के भीतर रन-टाइम पर सर्कल की सीमाएँ बनाना

ON + स्क्वायर डिस्टेंस : अधिक महंगी स्क्वायर रूट एल्गोरिथ्म (Pieter Geerkens) लेने से बचने के लिए एक और अनुकूलन के रूप में स्क्वायर डिस्टेंस का उपयोग करना।

यहाँ विभिन्न एल्गोरिदम के विभिन्न प्रदर्शनों का एक सारांश है जो लूप के माध्यम से पुनरावृति में लगने वाले समय को दर्शाता है।

यहाँ छवि विवरण दर्ज करें

एक्स-अक्ष अधिक डॉट्स जोड़कर बढ़ी हुई जटिलता दिखाता है (और इस तरह लूप को धीमा कर रहा है)। (उदाहरण के लिए, 1000 आयत बिंदुओं पर एक परिसीमित स्थान में 20 आयतों के साथ 1000 बार, लूप पुनरावृत्ति करता है और एल्गोरिथ्म को 20000 बार कॉल करता है।) y- अक्ष एक उच्च रिज़ॉल्यूशन का उपयोग करके पूरे लूप को पूरा करने में लगने वाले समय (ms) को दिखाता है। प्रदर्शन टाइमर। 20 से अधिक एमएस एक सभ्य खेल के लिए समस्याग्रस्त होगा क्योंकि यह उच्च एफपीएस का लाभ नहीं उठाएगा ताकि एक चिकनी एनीमेशन को प्रक्षेपित किया जा सके और खेल कई बार 'बीहड़' दिखाई दे सकता है।

परिणाम 1 : लूप के भीतर एक तेज नकारात्मक जांच के साथ एक पूर्व-संसाधित परिपत्र बाध्य एल्गोरिदम नियमित एल्गोरिदम की तुलना में 1900% द्वारा प्रदर्शन में सुधार करता है (चेक के बिना मूल लूप समय का 5%)। परिणाम एक लूप के भीतर पुनरावृत्तियों की संख्या के लिए लगभग आनुपातिक रखता है, इस प्रकार यह कोई फर्क नहीं पड़ता कि हम 10 या 10000 यादृच्छिक रूप से प्रदर्शित अंक की जांच करते हैं। इस प्रकार, इस चित्रण में प्रदर्शन हानि महसूस किए बिना वस्तुओं की संख्या सुरक्षित रूप से 10k तक बढ़ सकती है।

परिणाम 2 : यह पिछली टिप्पणी द्वारा सुझाया गया है कि एल्गोरिथ्म तेज हो सकता है, लेकिन स्मृति गहन। हालाँकि, ध्यान दें कि पूर्व-संसाधित सर्कल आकार के लिए एक फ़्लोटिंग को केवल 4 बाइट्स लेता है। यह तब तक कोई वास्तविक मुद्दा नहीं होना चाहिए जब तक कि ओपी एक साथ 100000+ ऑब्जेक्ट को चलाने की योजना नहीं बनाता है। एक वैकल्पिक और स्मृति कुशल दृष्टिकोण लूप के भीतर स्टैक पर सर्कल के अधिकतम आकार की गणना करना है और इसे हर पुनरावृत्ति के साथ दायरे से बाहर जाने देना है और इस प्रकार गति के कुछ अज्ञात मूल्य के लिए व्यावहारिक रूप से कोई स्मृति उपयोग नहीं है। वास्तव में, परिणाम से पता चलता है कि यह दृष्टिकोण वास्तव में एक पूर्व-संसाधित सर्कल आकार का उपयोग करने की तुलना में धीमा है, लेकिन यह अभी भी लगभग 1150% (मूल प्रसंस्करण समय का 8%) के काफी प्रदर्शन में सुधार दिखाता है।

परिणाम 3 : मैं आगे वास्तविक दूरी के बजाय चुकता दूरी का उपयोग करके परिणाम 1 एल्गोरिथ्म में सुधार करता हूं और इस प्रकार एक कम्प्यूटेशनल रूप से महंगा वर्गमूल संचालन ले रहा हूं। यह केवल sligthtly प्रदर्शन को बढ़ाता है (2400%)। (नोट: मैं भी इसी तरह के लेकिन थोड़ा खराब परिणाम के साथ वर्गमूल सन्निकटन के लिए पूर्व-संसाधित सरणियों के लिए हैश टेबल की कोशिश करता हूं)

परिणाम 4 : मैं आगे आयतों को हिलाने / टकराने की जाँच करता हूँ; हालाँकि, यह मूल परिणामों (जैसी कि अपेक्षित है) को बदलता नहीं है क्योंकि तार्किक जाँच अनिवार्य रूप से समान है।

परिणाम 5 : मैं आयतों की संख्या को अलग-अलग करता हूं और पाता हूं कि एल्गोरिथ्म और भी अधिक कुशल हो जाता है, कम भीड़ वाला स्थान भर जाता है (डेमो में नहीं दिखाया गया है)। परिणाम भी कुछ हद तक अपेक्षित है, क्योंकि किसी बिंदु के लिए एक सर्कल और ऑब्जेक्ट की सीमाओं के बीच छोटे स्थान के भीतर संभावना कम हो जाती है। दूसरे चरम पर, मैं एक ही सीमित छोटे स्थान के भीतर आयतों की संख्या को भी 100 तक बढ़ाने की कोशिश करता हूं और उन्हें लूप (पापी (पुनरावृत्ति)) के भीतर रन टाइम पर डायनामिक रूप से भिन्न करता हूं। यह अभी भी 570% (या मूल लूप समय के 15%) के प्रदर्शन में वृद्धि के साथ बहुत अच्छा प्रदर्शन करता है।

परिणाम 6 : मैं यहाँ पर सुझाए गए वैकल्पिक एल्गोरिदम का परीक्षण करता हूँ और प्रदर्शन में बहुत मामूली लेकिन महत्वपूर्ण अंतर नहीं पाता (2%)। दिलचस्प और अधिक सरल IsLeft एल्गोरिथ्म 17% (मूल गणना समय का 85%) द्वारा प्रदर्शन को बढ़ावा देने के साथ बहुत अच्छा प्रदर्शन करता है, लेकिन एक त्वरित नकारात्मक चेक एल्गोरिथ्म की दक्षता के पास कहीं नहीं है।

मेरा कहना है कि पहले लीन डिजाइन और गेम लॉजिक पर विचार करें, खासकर जब सीमाओं और टकराव की घटनाओं से निपटें। ओपीएस वर्तमान एल्गोरिथ्म पहले से ही काफी कुशल है और एक और अनुकूलन अंतर्निहित अवधारणा को अनुकूलित करने के रूप में महत्वपूर्ण नहीं है। इसके अलावा, खेल के दायरे और उद्देश्य को संप्रेषित करना अच्छा है, क्योंकि एल्गोरिथ्म की दक्षता गंभीर रूप से उन पर निर्भर करती है।

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

Rec Points  Iter    OFF     ON     ON_Stack     ON_SqrDist  Ileft Algorithm (Wondra)
            (ms)    (ms)    (ms)    (ms)        (ms)        (ms)
20  10      200     0.29    0.02    0.04        0.02        0.17
20  100     2000    2.23    0.10    0.20        0.09        1.69
20  1000    20000   24.48   1.25    1.99        1.05        16.95
20  10000   200000  243.85  12.54   19.61       10.85       160.58

यद्यपि मुझे असामान्य दृष्टिकोण पसंद था और दा विंची संदर्भ से प्यार था, मुझे नहीं लगता कि हलकों से निपटना, अकेले त्रिज्या करना, यह कुशल होगा। इसके अलावा, यह समाधान तभी उचित है जब सभी आयतें पहले से तय हों और पहले से ज्ञात हों
लुई 15

आयत की स्थिति को ठीक करने की आवश्यकता नहीं है। रिश्तेदार निर्देशांक का उपयोग करें। इसे भी ऐसे समझिए। वह दायरा एक ही रहता है, चाहे कोई भी चक्कर क्यों न हो।
मेजे

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

तेजी से सकारात्मक / नकारात्मक परीक्षण के लिए दिलचस्प एल्गोरिथ्म! प्रीप्रोसेस्ड बाउंडिंग सर्कल (और चौड़ाई) को बचाने के लिए समस्या अतिरिक्त मेमोरी हो सकती है, यह अच्छा हेयुरिस्टिक हो सकता है लेकिन यह भी ध्यान दें कि इसका सीमित उपयोग है - ज्यादातर ऐसे मामलों के लिए जहां मेमोरी ज्यादा मायने नहीं रखती है (बड़ी वस्तुओं पर स्थिर आकार के आयत = स्प्रिट गेम ऑब्जेक्ट) और प्रीप्रोसेस करने का समय है।
वंड्रा

संपादित + जोड़ा बेंचमार्क परीक्षण।
माज़ी

2

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


हम्म, सुझाव के लिए धन्यवाद, लेकिन घूर्णन और उलटा घुमाव प्राप्त करना उतना कुशल नहीं लगता है। वास्तव में यह मेरे समाधान के रूप में शायद ही कुशल होगा
बजे

आप ध्यान दें कि एक 3D बिंदु बुद्धि को घुमाना एक मैट्रिक्स 6 गुणन और 3 परिवर्धन, और एक फ़ंक्शन कॉल है। @ वॉन्ड्रा का समाधान सबसे अच्छा समकक्ष है, लेकिन इरादे में बहुत कम स्पष्ट; और DRY के उल्लंघन के माध्यम से रखरखाव की त्रुटियों के लिए अतिसंवेदनशील
पीटर गेकरेंस

@ पीटर जेकरेन्स ने दावा किया, मेरा कोई भी समाधान DRY का उल्लंघन कैसे करता है (और क्या DRY महत्वपूर्ण प्रोग्रामिंग सिद्धांतों में से एक है? अब तक इसके बारे में कभी नहीं सुना है)? और सबसे महत्वपूर्ण बात, उन समाधानों में क्या त्रुटियां हैं? हमेशा सीखने के लिए तैयार।
वंद्रा

@wondra: DRY = खुद को न दोहराएं। आपका कोड स्निपेट हर जगह वेक्टर गुणन द्वारा मैट्रिक्स के विवरण को कोडित करने का सुझाव देता है जो कि मानक मैट्रिक्स-एप्लिकेशन-टू-वेक्टर विधि को लागू करने के बजाय कोड में कार्यक्षमता प्रकट होती है।
पीटर गेकरेंस

@PieterGeerkens के पाठ्यक्रम में इसका केवल एक ही भाग सुझाया गया है - 1) आपके पास स्पष्ट रूप से मैट्रिक्स नहीं है (प्रत्येक प्रश्न के लिए नए मैट्रिक्स को आवंटित करना कठिन प्रदर्शन को प्रभावित करेगा) 2) मैं केवल गुणन के विशिष्ट मामले का उपयोग करता हूं, इस मामले के लिए अनुकूलित जेनेरिक के ब्लोट को छोड़ देता है। एक। यह निम्न-स्तरीय संचालन है और अप्रत्याशित व्यवहार को रोकने के लिए इसे संक्षिप्त रहना चाहिए।
वंड्रा

1

मेरे पास इसे बेंचमार्क करने का समय नहीं था, लेकिन मेरा सुझाव परिवर्तन मैट्रिक्स को संग्रहीत करना होगा जो आयत को x- और y- श्रेणी में अक्ष संरेखित वर्ग में बदलकर 0 से 1 तक कर देगा। दूसरे शब्दों में मैट्रिक्स को संग्रहीत करें आयत के एक कोने को (0,0) में बदल देता है और विपरीत को (1,1) में बदल देता है।

यह निश्चित रूप से अधिक महंगा होगा यदि आयत को बहुत अधिक स्थानांतरित किया जाता है और टकराव की जाँच बहुत कम ही की जाती है, लेकिन यदि आयत के अद्यतनों की तुलना में बहुत अधिक जाँच होती है तो यह कम से कम दो त्रिभुजों के विरुद्ध परीक्षण के मूल दृष्टिकोण से अधिक तेज़ होगा। छह डॉट उत्पादों के रूप में एक मैट्रिक्स गुणा के साथ प्रतिस्थापित किया जाएगा।

लेकिन हमेशा की तरह इस एल्गोरिथ्म की गति उस तरह के चेक पर निर्भर करती है जिस पर आप प्रदर्शन करने की उम्मीद करते हैं। यदि अधिकांश बिंदु सरल दूरी की जाँच करने वाले आयत के समीप नहीं हैं (उदाहरण के लिए (point.x - firstCorner.x)> aLargeDistance) के परिणामस्वरूप बड़ी गति हो सकती है, जबकि यह लगभग सभी चीजों को धीमा भी कर सकती है। अंक आयत के अंदर हैं।

संपादित करें: यह मेरा आयत-वर्ग जैसा दिखेगा:

class Rectangle
{
public:
    Matrix3x3 _transform;

    Rectangle()
    {}

    void setCorners(Vector2 p_a, Vector2 p_b, Vector2 p_c)
    {
        // create a matrix from the two edges of the rectangle
        Vector2 edgeX = p_b - p_a;
        Vector2 edgeY = p_c - p_a;

        // and then create the inverse of that matrix because we want to 
        // transform points from world coordinates into "rectangle coordinates".
        float scaling = 1/(edgeX._x*edgeY._y - edgeY._x*edgeX._y);

        _transform._columns[0]._x = scaling * edgeY._y;
        _transform._columns[0]._y = - scaling * edgeX._y;
        _transform._columns[1]._x = - scaling * edgeY._x;
        _transform._columns[1]._y = scaling * edgeX._x;

        // the third column is the translation, which also has to be transformed into "rectangle space"
        _transform._columns[2]._x = -p_a._x * _transform._columns[0]._x - p_a._y * _transform._columns[1]._x;
        _transform._columns[2]._y = -p_a._x * _transform._columns[0]._y - p_a._y * _transform._columns[1]._y;
    }

    bool isInside(Vector2 p_point)
    {
        Vector2 test = _transform.transform(p_point);
        return  (test._x>=0)
                && (test._x<=1)
                && (test._y>=0)
                && (test._y<=1);
    }
};

यह मेरे बेंचमार्क की पूरी सूची है:

#include <cstdlib>
#include <math.h>
#include <iostream>

#include <sys/time.h>

using namespace std;

class Vector2
{
public:
    float _x;
    float _y;

    Vector2()
    :_x(0)
    ,_y(0)
    {}

    Vector2(float p_x, float p_y)
        : _x (p_x)
        , _y (p_y)
        {}

    Vector2 operator-(const Vector2& p_other) const
    {
        return Vector2(_x-p_other._x, _y-p_other._y);
    }

    Vector2 operator+(const Vector2& p_other) const
    {
        return Vector2(_x+p_other._x, _y+p_other._y);
    }

    Vector2 operator*(float p_factor) const
    {
        return Vector2(_x*p_factor, _y*p_factor);
    }

    static float Dot(Vector2 p_a, Vector2 p_b)
    {
        return (p_a._x*p_b._x + p_a._y*p_b._y);
    }
};

bool PointInTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
 // Compute vectors        
 Vector2 v0 = C - A;
 Vector2 v1 = B - A;
 Vector2 v2 = P - A;

 // Compute dot products
 float dot00 = Vector2::Dot(v0, v0);
 float dot01 = Vector2::Dot(v0, v1);
 float dot02 = Vector2::Dot(v0, v2);
 float dot11 = Vector2::Dot(v1, v1);
 float dot12 = Vector2::Dot(v1, v2);

 // Compute barycentric coordinates
 float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
 float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
 float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

 // Check if point is in triangle
 if(u >= 0 && v >= 0 && (u + v) < 1)
    { return true; } else { return false; }
}


bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
 if(PointInTriangle(X,Y,Z,P)) return true;
 if(PointInTriangle(X,Z,W,P)) return true;
 return false;
}

class Matrix3x3
{
public:
    Vector2 _columns[3];

    Vector2 transform(Vector2 p_in)
    {
        return _columns[0] * p_in._x + _columns[1] * p_in._y + _columns[2];
    }
};

class Rectangle
{
public:
    Matrix3x3 _transform;

    Rectangle()
    {}

    void setCorners(Vector2 p_a, Vector2 p_b, Vector2 p_c)
    {
        // create a matrix from the two edges of the rectangle
        Vector2 edgeX = p_b - p_a;
        Vector2 edgeY = p_c - p_a;

        // and then create the inverse of that matrix because we want to 
        // transform points from world coordinates into "rectangle coordinates".
        float scaling = 1/(edgeX._x*edgeY._y - edgeY._x*edgeX._y);

        _transform._columns[0]._x = scaling * edgeY._y;
        _transform._columns[0]._y = - scaling * edgeX._y;
        _transform._columns[1]._x = - scaling * edgeY._x;
        _transform._columns[1]._y = scaling * edgeX._x;

        // the third column is the translation, which also has to be transformed into "rectangle space"
        _transform._columns[2]._x = -p_a._x * _transform._columns[0]._x - p_a._y * _transform._columns[1]._x;
        _transform._columns[2]._y = -p_a._x * _transform._columns[0]._y - p_a._y * _transform._columns[1]._y;
    }

    bool isInside(Vector2 p_point)
    {
        Vector2 test = _transform.transform(p_point);
        return  (test._x>=0)
                && (test._x<=1)
                && (test._y>=0)
                && (test._y<=1);
    }
};

void runTest(float& outA, float& outB)
{
    Rectangle r;
    r.setCorners(Vector2(0,0.5), Vector2(0.5,1), Vector2(0.5,0));

    int numTests = 10000;

    Vector2 points[numTests];

    Vector2 cornerA[numTests];
    Vector2 cornerB[numTests];
    Vector2 cornerC[numTests];
    Vector2 cornerD[numTests];

    bool results[numTests];
    bool resultsB[numTests];

    for (int i=0; i<numTests; ++i)
    {
        points[i]._x = rand() / ((float)RAND_MAX);
        points[i]._y = rand() / ((float)RAND_MAX);

        cornerA[i]._x = rand() / ((float)RAND_MAX);
        cornerA[i]._y = rand() / ((float)RAND_MAX);

        Vector2 edgeA;
        edgeA._x = rand() / ((float)RAND_MAX);
        edgeA._y = rand() / ((float)RAND_MAX);

        Vector2 edgeB;
        edgeB._x = rand() / ((float)RAND_MAX);
        edgeB._y = rand() / ((float)RAND_MAX);

        cornerB[i] = cornerA[i] + edgeA;
        cornerC[i] = cornerA[i] + edgeB;
        cornerD[i] = cornerA[i] + edgeA + edgeB;
    }

    struct timeval start, end;

    gettimeofday(&start, NULL);
    for (int i=0; i<numTests; ++i)
    {
        r.setCorners(cornerA[i], cornerB[i], cornerC[i]);
        results[i] = r.isInside(points[i]);
    }
    gettimeofday(&end, NULL);
    float elapsed = (end.tv_sec - start.tv_sec)*1000;
    elapsed += (end.tv_usec - start.tv_usec)*0.001;
    outA += elapsed;

    gettimeofday(&start, NULL);
    for (int i=0; i<numTests; ++i)
    {
        resultsB[i] = PointInRectangle(cornerA[i], cornerB[i], cornerC[i], cornerD[i], points[i]);
    }
    gettimeofday(&end, NULL);
    elapsed = (end.tv_sec - start.tv_sec)*1000;
    elapsed += (end.tv_usec - start.tv_usec)*0.001;
    outB += elapsed;
}

/*
 * 
 */
int main(int argc, char** argv) 
{
    float a = 0;
    float b = 0;

    for (int i=0; i<5000; i++)
    {
        runTest(a, b);
    }

    std::cout << "Result: " << a << " / " << b << std::endl;

    return 0;
}

कोड निश्चित रूप से सुंदर नहीं है, लेकिन मुझे तुरंत कोई प्रमुख बग दिखाई नहीं देता है। उस कोड के साथ मुझे ऐसे परिणाम मिलते हैं जो संकेत देते हैं कि मेरा समाधान लगभग दोगुना है यदि आयत को प्रत्येक चेक के बीच ले जाया जाए। यदि यह नहीं चलता है तो मेरा कोड पांच गुना अधिक तेजी से लगता है।

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


दिलचस्प है, लेकिन मत भूलो कि आपको डॉट्स पर भी मैट्रिक्स रोटेशन को लागू करने की आवश्यकता है। मेरे गेमगाइन में मैट्रिक्स रॉट ऑपरेशन है और बाद में आपके एल्गोरिदम को बेंचमार्क कर सकता है। आपकी अंतिम टिप्पणी के संबंध में। फिर आप एक 'इनर सर्कल' को भी परिभाषित कर सकते हैं और एक डबल नेगेटिव जाँच कर सकते हैं यदि डॉट इनर सर्कल के बाहर और बाहरी सर्किट के अंदर स्थित है जैसा कि ऊपर वर्णित है।
माज़ी

हां, यदि आप त्रिकोण के बीच में अधिकांश बिंदुओं के करीब होने की उम्मीद करते हैं, तो इससे मदद मिलेगी। मैं एक आयताकार दौड़ ट्रैक जैसी स्थिति की कल्पना कर रहा था, जहां आप उदाहरण के लिए एक बाहरी आयत का उपयोग करके एक आयताकार पथ को परिभाषित करते हैं कि चरित्र को अंदर रहना पड़ता है और एक छोटे आंतरिक आयत को जिसमें से बाहर रहना पड़ता है। उस स्थिति में हर चेक आयत की सीमा के करीब होगा और उन सर्कल की जाँच शायद प्रदर्शन को और खराब कर देगी। दी, यह एक निर्मित उदाहरण है, लेकिन मैं कहूंगा कि यह कुछ ऐसा है जो वास्तव में हो सकता है।
लार्स कोकमोहोर

जैसी चीजें हो सकती हैं, हां। मुझे आश्चर्य है कि एल्गोरिथ्म के खिलाफ मोड़ने के लिए मिठाई स्थान क्या है। अंत में यह आपके उद्देश्य से उब जाता है। यदि आपके पास समय है तो आप अपना कोड पोस्ट कर सकते हैं, ओपीएस पोस्ट का उपयोग कर सकते हैं और मैं आपके एल्गोरिथ्म को बेंचमार्क कर सकता हूं? चलो देखते हैं कि क्या आपका अंतर्ज्ञान सही है। मैं IsLeft Algorithm के खिलाफ आपके विचार के प्रदर्शन पर उत्सुक हूं।
मेजे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.