मैं कैसे निर्धारित कर सकता हूं कि एक बहुभुज के भीतर 2 डी प्वाइंट है या नहीं?


497

मैं हिट-टेस्टिंग (जैसे ) में उपयोग के लिए, बहुभुज एल्गोरिथ्म के अंदर एक तेजी से 2 डी बिंदु बनाने की कोशिश कर रहा हूं Polygon.contains(p:Point)। प्रभावी तकनीकों के सुझावों की सराहना की जाएगी।


आप हमें दाएं या बाएं हाथ के सवाल पर अपनी धारणाओं के बारे में बताना भूल गए - जिसकी व्याख्या "अंदर" बनाम "बाहर" के रूप में भी की जा सकती है - आरटी
रिचर्ड टी

13
हां, मुझे एहसास है कि सवाल कई विवरणों को अनिर्दिष्ट छोड़ देता है, लेकिन इस बिंदु पर मुझे विभिन्न प्रकार की प्रतिक्रियाओं को देखने में रुचि है।
स्कॉट एवरडेन

4
एक 90 पक्षीय बहुभुज को एक एननेक्स्टैगन कहा जाता है और 10,000 पक्षीय बहुभुज को एक असंख्य कहा जाता है।

"सबसे सुरुचिपूर्ण" लक्ष्य से बाहर है, क्योंकि मुझे "सभी पर एक" काम खोजने में परेशानी हुई है-एल्गोरिथम। मुझे स्वयं इसका पता लगाना चाहिए: stackoverflow.com/questions/14818567/…
davidkonrad

जवाबों:


731

ग्राफिक्स के लिए, मैं नहीं बल्कि पूर्णांक पसंद करेंगे। कई सिस्टम यूआई पेंटिंग के लिए पूर्णांक का उपयोग करते हैं (पिक्सेल सभी के बाद इन्टस हैं), लेकिन उदाहरण के लिए macOS सब कुछ के लिए फ्लोट का उपयोग करता है। macOS केवल अंक जानता है और एक बिंदु एक पिक्सेल में अनुवाद कर सकता है, लेकिन मॉनिटर रिज़ॉल्यूशन के आधार पर, यह कुछ और में अनुवाद कर सकता है। रेटिना स्क्रीन पर आधा बिंदु (0.5 / 0.5) पिक्सेल होता है। फिर भी, मैंने कभी नहीं देखा कि macOS UI अन्य UI की तुलना में काफी धीमा है। सभी 3 डी एपीआई (ओपनजीएल या डायरेक्ट 3 डी) के बाद भी फ्लोट और आधुनिक ग्राफिक्स लाइब्रेरी के साथ काम करता है जो अक्सर GPU त्वरण का लाभ उठाते हैं।

अब आपने कहा कि गति आपकी मुख्य चिंता है, ठीक है, चलो गति के लिए चलते हैं। किसी भी परिष्कृत एल्गोरिदम को चलाने से पहले, पहले एक साधारण परीक्षण करें। अपने बहुभुज के चारों ओर एक अक्ष संरेखित बाउंडिंग बॉक्स बनाएं । यह बहुत आसान, तेज है और पहले से ही आपको बहुत सारी गणनाओं को सुरक्षित कर सकता है। वह कैसे काम करता है? बहुभुज के सभी बिंदुओं पर Iterate करें और X और Y के न्यूनतम / अधिकतम मान ज्ञात करें।

जैसे आपके पास अंक हैं (9/1), (4/3), (2/7), (8/2), (3/6)। इसका मतलब है कि Xmin 2 है, Xmax 9 है, Ymin 1 है और Ymax 7 है। आयत के बाहर दो किनारों (2/1) के साथ एक बिंदु और (9/7) बहुभुज के भीतर नहीं हो सकता है।

// p is your point, p.x is the x coord, p.y is the y coord
if (p.x < Xmin || p.x > Xmax || p.y < Ymin || p.y > Ymax) {
    // Definitely not within the polygon!
}

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

छेद के बिना बहुभुज

और यहाँ एक छेद के साथ है:

छेद के साथ बहुभुज

हरे रंग के बीच में एक छेद है!

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

यह दर्शाता है कि किरण बहुभुज के माध्यम से कैसे कटती है

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

आपके पास अभी भी ऊपर का बाउंडिंग बॉक्स है, याद है? बस बाउंडिंग बॉक्स के बाहर एक बिंदु चुनें और इसे अपनी किरण के लिए शुरुआती बिंदु के रूप में उपयोग करें। उदाहरण के लिए बिंदु (Xmin - e/p.y)बहुभुज के बाहर है।

लेकिन क्या है e? खैर, e(वास्तव में एप्सिलॉन) बाउंडिंग बॉक्स को कुछ पैडिंग देता है । जैसा कि मैंने कहा, यदि हम एक बहुभुज रेखा के बहुत करीब से शुरू करते हैं, तो रे ट्रेसिंग विफल हो जाती है। चूंकि बाउंडिंग बॉक्स बहुभुज के बराबर हो सकता है (यदि बहुभुज एक अक्ष संरेखित आयत है, तो बाउंडिंग बॉक्स बहुभुज के बराबर है!), हमें इसे सुरक्षित बनाने के लिए कुछ पैडिंग की आवश्यकता है, बस इतना ही। आपको कितना बड़ा चुनना चाहिए e? बहुत बड़ा नहीं। यह समन्वय प्रणाली के पैमाने पर निर्भर करता है जिसका उपयोग आप ड्राइंग के लिए करते हैं। यदि आपकी पिक्सेल चरण की चौड़ाई 1.0 है, तो बस 1.0 चुनें (फिर भी 0.1 ने भी काम किया होगा)

अब जब हमारे पास इसकी शुरुआत और अंत के निर्देशांकों के साथ किरण है, तो समस्या यह है कि " बहुभुज के भीतर बिंदु है " से पाली " रे बहुभुज पक्ष को कितनी बार काटती है "। इसलिए हम पहले की तरह बहुभुज बिंदुओं के साथ काम नहीं कर सकते, अब हमें वास्तविक पक्षों की आवश्यकता है। एक पक्ष को हमेशा दो बिंदुओं द्वारा परिभाषित किया जाता है।

side 1: (X1/Y1)-(X2/Y2)
side 2: (X2/Y2)-(X3/Y3)
side 3: (X3/Y3)-(X4/Y4)
:

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

// Test the ray against all sides
int intersections = 0;
for (side = 0; side < numberOfSides; side++) {
    // Test if current side intersects with ray.
    // If yes, intersections++;
}
if ((intersections & 1) == 1) {
    // Inside of polygon
} else {
    // Outside of polygon
}

अब तक तो ठीक है, लेकिन अगर दो वैक्टर चौराहे हैं तो आप कैसे परीक्षण करेंगे? यहाँ कुछ सी कोड (परीक्षण नहीं किया गया है), जो कि चाल करना चाहिए:

#define NO 0
#define YES 1
#define COLLINEAR 2

int areIntersecting(
    float v1x1, float v1y1, float v1x2, float v1y2,
    float v2x1, float v2y1, float v2x2, float v2y2
) {
    float d1, d2;
    float a1, a2, b1, b2, c1, c2;

    // Convert vector 1 to a line (line 1) of infinite length.
    // We want the line in linear equation standard form: A*x + B*y + C = 0
    // See: http://en.wikipedia.org/wiki/Linear_equation
    a1 = v1y2 - v1y1;
    b1 = v1x1 - v1x2;
    c1 = (v1x2 * v1y1) - (v1x1 * v1y2);

    // Every point (x,y), that solves the equation above, is on the line,
    // every point that does not solve it, is not. The equation will have a
    // positive result if it is on one side of the line and a negative one 
    // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector
    // 2 into the equation above.
    d1 = (a1 * v2x1) + (b1 * v2y1) + c1;
    d2 = (a1 * v2x2) + (b1 * v2y2) + c1;

    // If d1 and d2 both have the same sign, they are both on the same side
    // of our line 1 and in that case no intersection is possible. Careful, 
    // 0 is a special case, that's why we don't test ">=" and "<=", 
    // but "<" and ">".
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // The fact that vector 2 intersected the infinite line 1 above doesn't 
    // mean it also intersects the vector 1. Vector 1 is only a subset of that
    // infinite line 1, so it may have intersected that line before the vector
    // started or after it ended. To know for sure, we have to repeat the
    // the same test the other way round. We start by calculating the 
    // infinite line 2 in linear equation standard form.
    a2 = v2y2 - v2y1;
    b2 = v2x1 - v2x2;
    c2 = (v2x2 * v2y1) - (v2x1 * v2y2);

    // Calculate d1 and d2 again, this time using points of vector 1.
    d1 = (a2 * v1x1) + (b2 * v1y1) + c2;
    d2 = (a2 * v1x2) + (b2 * v1y2) + c2;

    // Again, if both have the same sign (and neither one is 0),
    // no intersection is possible.
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // If we get here, only two possibilities are left. Either the two
    // vectors intersect in exactly one point or they are collinear, which
    // means they intersect in any number of points from zero to infinite.
    if ((a1 * b2) - (a2 * b1) == 0.0f) return COLLINEAR;

    // If they are not collinear, they must intersect in exactly one point.
    return YES;
}

इनपुट मान वेक्टर 1 ( और ) और वेक्टर 2 ( और ) के दो समापन बिंदु हैं । तो आपके पास 2 वैक्टर, 4 अंक, 8 निर्देशांक हैं। और स्पष्ट हैं। चौराहों को बढ़ाता है, कुछ नहीं करता है।v1x1/v1y1v1x2/v1y2v2x1/v2y1v2x2/v2y2YESNOYESNO

COLLINEAR के बारे में क्या? इसका अर्थ है कि दोनों वैक्टर एक ही अनंत रेखा पर स्थित हैं, स्थिति और लंबाई के आधार पर, वे बिल्कुल भी प्रतिच्छेद नहीं करते हैं या वे एक अंतहीन संख्या में बिताते हैं। मुझे पूरी तरह से यकीन नहीं है कि इस मामले को कैसे संभालना है, मैं इसे चौराहे के रूप में नहीं गिनूंगा। खैर, यह मामला चलन में नहीं बल्कि वैसे भी है क्योंकि फ्लोटिंग पॉइंट राउंडिंग त्रुटियां हैं; बेहतर कोड शायद == 0.0fइसके लिए परीक्षण नहीं करेगा, बल्कि कुछ के लिए < epsilon, जहां एप्सिलॉन एक छोटी संख्या है।

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

अंतिम लेकिन कम से कम नहीं: यदि आप समस्या को हल करने के लिए 3 डी हार्डवेयर का उपयोग कर सकते हैं, तो एक दिलचस्प विकल्प है। बस GPU आप के लिए सभी काम करते हैं। एक पेंटिंग सतह बनाएं जो ऑफ स्क्रीन हो। इसे पूरी तरह से काले रंग से भरें। अब OpenGL या Direct3D को अपने बहुभुज (या यहां तक ​​कि अपने सभी बहुभुजों को पेंट करें) यदि आप परीक्षण करना चाहते हैं कि बिंदु उनमें से किसी के भीतर है, लेकिन आप जिसकी परवाह नहीं करते हैं) और बहुभुज को अलग से भरें रंग, जैसे सफेद। यह देखने के लिए कि क्या कोई बिंदु बहुभुज के भीतर है, इस बिंदु का रंग ड्राइंग सतह से प्राप्त करें। यह सिर्फ एक O (1) मेमोरी लाने वाला है।

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


26
+1 शानदार जवाब। इसे करने के लिए हार्डवेयर का उपयोग करने पर, मैंने अन्य स्थानों पर पढ़ा है कि यह धीमा हो सकता है क्योंकि आपको ग्राफिक्स कार्ड से डेटा वापस प्राप्त करना होगा। लेकिन मुझे सीपीयू को बहुत अधिक उतारने का सिद्धांत पसंद है। क्या किसी के पास इस बात का कोई अच्छा संदर्भ है कि यह ओपनजीएल में कैसे किया जा सकता है?
गैविन

3
+1 क्योंकि यह इतना सरल है! मुख्य समस्या यह है कि यदि आपका बहुभुज और परीक्षण बिंदु एक ग्रिड पर (अपूर्व नहीं) है तो आपको "डुप्लिकेटस" चौराहों से निपटना होगा, उदाहरण के लिए, सीधे एक बहुभुज बिंदु के माध्यम से! (एक के बजाय दो की समानता प्रदान करना)। इस अजीब क्षेत्र में हो जाता है: stackoverflow.com/questions/2255842/… । कंप्यूटर ग्राफिक्स इन विशेष मामलों से भरा है: सिद्धांत में सरल, व्यवहार में बालों वाला।
जारेड अपडेटिक

7
@RMorrisey: आपको ऐसा क्यों लगता है? मैं यह देखने में विफल रहता हूं कि यह एक अवतल बहुभुज के लिए कैसे विफल होगा। बहुभुज के अवतल होने पर किरण बहुभुज को कई बार छोड़ सकती है और पुन: दर्ज कर सकती है, लेकिन अंत में, हिट काउंटर विषम होगा यदि बिंदु भीतर है और भले ही वह बाहर है, अवतल बहुभुज के लिए भी।
मिकी

6
Softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm पर वर्णित 'तेज़ घुमावदार संख्या एल्गोरिथ्म' बहुत तेज़ काम करता है ...
SP

10
X और y निर्देशांक को अलग करने के लिए / का आपका उपयोग भ्रामक है, यह x को y द्वारा विभाजित के रूप में पढ़ता है। एक्स, वाई (यानी एक्स कॉमा वाई) का उपयोग करना अधिक स्पष्ट है, कुल मिलाकर, एक उपयोगी उत्तर है।
ऐश

583

मुझे लगता है कि निम्नलिखित कोड का सबसे अच्छा समाधान है ( यहां से लिया गया ):

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

तर्क

  • nvert : बहुभुज में कोने की संख्या। ऊपर वर्णित लेख में अंत में पहले शीर्ष को दोहराने के लिए चर्चा की गई है या नहीं।
  • वर्टेक्स, वर्टी : बहुभुज के कोने के x- और y-निर्देशांक युक्त एरे।
  • testx, testy : X- और परीक्षण बिंदु का y- समन्वय।

यह छोटा और कुशल दोनों है और उत्तल और अवतल बहुभुज दोनों के लिए काम करता है। जैसा कि पहले सुझाव दिया गया है, आपको पहले बाउंडिंग आयत की जांच करनी चाहिए और बहुभुज छेद का अलग से इलाज करना चाहिए।

इसके पीछे का विचार बहुत सरल है। लेखक इसका वर्णन इस प्रकार करता है:

मैं परीक्षण बिंदु से क्षैतिज रूप से (बढ़ती हुई x, निश्चित y) एक अर्ध-अनंत किरण चलाता हूं और गिनता हूं कि यह कितने किनारों को पार करता है। प्रत्येक क्रॉसिंग पर, किरण अंदर और बाहर के बीच स्विच करती है। इसे जॉर्डन वक्र प्रमेय कहा जाता है।

चर सी 0 से 1 और 1 से 0 तक स्विच कर रहा है हर बार क्षैतिज किरण किसी भी किनारे को पार करती है। तो मूल रूप से यह नज़र रखता है कि पार किए गए किनारों की संख्या सम या विषम है या नहीं। 0 का अर्थ सम है और 1 का अर्थ विषम है।


5
सवाल। वास्तव में क्या चर हैं जो मैं इसे पास करता हूं? वे क्या प्रतिनिधित्व करते हैं?
टेकनगोलगी

9
@ मिक यह जाँचता है कि verty[i]और verty[j]दोनों ओर हैं testy, इसलिए वे कभी बराबर नहीं हैं।
पीटर वुड

4
यह कोड मजबूत नहीं है, और मैं इसका उपयोग करने की अनुशंसा नहीं करूंगा। यहाँ कुछ विस्तृत विश्लेषण देने वाली एक कड़ी दी गई है: www-ma2.upc.es/geoc/Schirra-pointPolygon.pdf
मिकोला

13
इस दृष्टिकोण में वास्तव में सीमाएँ (किनारे-मामले) हैं: बहुभुज में बिंदु (15,20) की जाँच [(10,10), (10,20), (20,20), (20,10)] वापस आ जाएगी सच के बजाय झूठ। (10,20) या (20,15) के साथ भी। अन्य सभी मामलों के लिए, एल्गोरिथ्म पूरी तरह से ठीक काम करता है और किनारे-मामलों में गलत-नकारात्मक मेरे आवेदन के लिए ठीक है।
अलेक्जेंडर पच

10
@ अलेक्जेंडर, यह वास्तव में डिजाइन द्वारा है: शीर्ष और दाईं सीमाओं के विपरीत अर्थ में बाईं और नीचे की सीमाओं को संभालकर, दो अलग-अलग बहुभुज को एक किनारे साझा करना चाहिए, इस किनारे के किसी भी बिंदु को एक और केवल एक बहुभुज में स्थित होना चाहिए। .. उपयोगी संपत्ति।
wardw

69

यहाँ nirg द्वारा दिए गए उत्तर का C # संस्करण है , जो इस RPI प्रोफेसर से आता है । ध्यान दें कि उस RPI स्रोत से कोड का उपयोग करने के लिए एट्रिब्यूशन की आवश्यकता होती है।

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

public bool IsPointInPolygon( Point p, Point[] polygon )
{
    double minX = polygon[ 0 ].X;
    double maxX = polygon[ 0 ].X;
    double minY = polygon[ 0 ].Y;
    double maxY = polygon[ 0 ].Y;
    for ( int i = 1 ; i < polygon.Length ; i++ )
    {
        Point q = polygon[ i ];
        minX = Math.Min( q.X, minX );
        maxX = Math.Max( q.X, maxX );
        minY = Math.Min( q.Y, minY );
        maxY = Math.Max( q.Y, maxY );
    }

    if ( p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY )
    {
        return false;
    }

    // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
    bool inside = false;
    for ( int i = 0, j = polygon.Length - 1 ; i < polygon.Length ; j = i++ )
    {
        if ( ( polygon[ i ].Y > p.Y ) != ( polygon[ j ].Y > p.Y ) &&
             p.X < ( polygon[ j ].X - polygon[ i ].X ) * ( p.Y - polygon[ i ].Y ) / ( polygon[ j ].Y - polygon[ i ].Y ) + polygon[ i ].X )
        {
            inside = !inside;
        }
    }

    return inside;
}

5
महान काम करता है, धन्यवाद, मैंने जावास्क्रिप्ट में बदल दिया। stackoverflow.com/questions/217578/…
फिलिप लेन्ससेन

2
यह> ग्राफिक्सपैथ का उपयोग करने की तुलना में 1000 गुना तेज है। अदृश्य! बाउंडिंग बॉक्स चेक 70% धीमी गति से कार्य करता है।
जेम्स ब्राउन

ग्राफिक्सपैथ ही नहीं। अदृश्य () रास्ता धीमा है, लेकिन 0.01 एफ श्रेणी में बहुत छोटे बहुभुजों के साथ यह अच्छी तरह से काम नहीं करता है
एनडी पर निर्भर टीम से पैट्रिक

50

निर्ग के दृष्टिकोण पर आधारित एम। काट्ज द्वारा उत्तर का एक जावास्क्रिप्ट संस्करण है:

function pointIsInPoly(p, polygon) {
    var isInside = false;
    var minX = polygon[0].x, maxX = polygon[0].x;
    var minY = polygon[0].y, maxY = polygon[0].y;
    for (var n = 1; n < polygon.length; n++) {
        var q = polygon[n];
        minX = Math.min(q.x, minX);
        maxX = Math.max(q.x, maxX);
        minY = Math.min(q.y, minY);
        maxY = Math.max(q.y, maxY);
    }

    if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
        return false;
    }

    var i = 0, j = polygon.length - 1;
    for (i, j; i < polygon.length; j = i++) {
        if ( (polygon[i].y > p.y) != (polygon[j].y > p.y) &&
                p.x < (polygon[j].x - polygon[i].x) * (p.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x ) {
            isInside = !isInside;
        }
    }

    return isInside;
}

32

बिंदु पी और बहुभुज के प्रत्येक के बीच कोणों के उन्मुख योग की गणना करें। यदि कुल उन्मुख कोण 360 डिग्री है, तो बिंदु अंदर है। यदि कुल 0 है, तो बिंदु बाहर है।

मुझे यह तरीका बेहतर लगता है क्योंकि यह अधिक मजबूत है और संख्यात्मक परिशुद्धता पर कम निर्भर है।

चौराहों की संख्या की गणना करने वाले तरीके सीमित हैं क्योंकि आप चौराहों की संख्या की गणना के दौरान एक एपेक्स को 'हिट' कर सकते हैं।

संपादित करें: वैसे, यह विधि अवतल और उत्तल बहुभुज के साथ काम करती है।

संपादित करें: मैंने हाल ही में इस विषय पर एक संपूर्ण विकिपीडिया लेख पाया ।


1
नहीं, यह सच नहीं है। यह बहुभुज के उत्तलता की परवाह किए बिना काम करता है।
डेविड सेगंड 13

2
@ डारेनव: केवल एक एकड़ प्रति शीर्ष; दूसरी ओर, यह एल्गोरिदम SIMD में लागू करने के लिए सबसे आसान होना चाहिए क्योंकि इसमें बिल्कुल कोई शाखा नहीं है।
जैस्पर बेकर्स

1
@emilio, यदि बिंदु त्रिकोण से बहुत दूर है, तो मैं यह नहीं देखता कि बिंदु और त्रिभुज के दो एप द्वारा निर्मित कोण 90 डिग्री कैसे होगा।
डेविड नेगॉव्स

2
"बिंदु दूर है" मामलों को हल करने के लिए पहले बाउंडिंग बॉक्स चेक का उपयोग करें। ट्रिगर के लिए, आप पहले से तैयार तालिकाओं का उपयोग कर सकते हैं।
जोम

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

24

यह सवाल इतना दिलचस्प है। मेरे पास इस पोस्ट के अन्य उत्तरों से अलग एक और व्यावहारिक विचार है। लक्ष्य को अंदर या बाहर करना है, यह तय करने के लिए कोणों के योग का उपयोग करना है। बेहतर घुमावदार संख्या के रूप में जाना जाता है ।

X को लक्ष्य बिंदु होने दें। चलो सरणी [0, 1, .... n] क्षेत्र के सभी बिंदु हो। प्रत्येक सीमा बिंदु के साथ लक्ष्य बिंदु को एक रेखा के साथ कनेक्ट करें। यदि लक्ष्य बिंदु इस क्षेत्र के अंदर है। सभी कोणों का योग 360 डिग्री होगा। नहीं तो कोण 360 से कम होगा।

विचार की बुनियादी समझ पाने के लिए इस छवि का संदर्भ लें: यहाँ छवि विवरण दर्ज करें

मेरा एल्गोरिथम मानता है कि दक्षिणावर्त सकारात्मक दिशा है। यहाँ एक संभावित इनपुट है:

[[-122.402015, 48.225216], [-117.032049, 48.999931], [-116.919132, 45.995175], [-124.079107, 46.267259], [-124.717175, 48.377557], [-122.92315, 47.047963], [-122.402015, 48.225216]]

निम्नलिखित अजगर कोड है जो विचार को लागू करता है:

def isInside(self, border, target):
degree = 0
for i in range(len(border) - 1):
    a = border[i]
    b = border[i + 1]

    # calculate distance of vector
    A = getDistance(a[0], a[1], b[0], b[1]);
    B = getDistance(target[0], target[1], a[0], a[1])
    C = getDistance(target[0], target[1], b[0], b[1])

    # calculate direction of vector
    ta_x = a[0] - target[0]
    ta_y = a[1] - target[1]
    tb_x = b[0] - target[0]
    tb_y = b[1] - target[1]

    cross = tb_y * ta_x - tb_x * ta_y
    clockwise = cross < 0

    # calculate sum of angles
    if(clockwise):
        degree = degree + math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))
    else:
        degree = degree - math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))

if(abs(round(degree) - 360) <= 3):
    return True
return False

21

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

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

वैसे, यहां एरिक हेंस के लेख से ब्याज के लिए प्रदर्शन तालिकाओं में से एक है, यादृच्छिक बहुभुज पर परीक्षण।

                       number of edges per polygon
                         3       4      10      100    1000
MacMartin               2.9     3.2     5.9     50.6    485
Crossings               3.1     3.4     6.8     60.0    624
Triangle Fan+edge sort  1.1     1.8     6.5     77.6    787
Triangle Fan            1.2     2.1     7.3     85.4    865
Barycentric             2.1     3.8    13.8    160.7   1665
Angle Summation        56.2    70.4   153.6   1403.8  14693

Grid (100x100)          1.5     1.5     1.6      2.1      9.8
Grid (20x20)            1.7     1.7     1.9      5.7     42.2
Bins (100)              1.8     1.9     2.7     15.1    117
Bins (20)               2.1     2.2     3.7     26.3    278

11

निर्ग द्वारा उत्तर का स्विफ्ट संस्करण :

extension CGPoint {
    func isInsidePolygon(vertices: [CGPoint]) -> Bool {
        guard !vertices.isEmpty else { return false }
        var j = vertices.last!, c = false
        for i in vertices {
            let a = (i.y > y) != (j.y > y)
            let b = (x < (j.x - i.x) * (y - i.y) / (j.y - i.y) + i.x)
            if a && b { c = !c }
            j = i
        }
        return c
    }
}

यह बी की गणना में शून्य समस्या से एक संभावित विभाजन है। केवल "बी" और "सी" के साथ अगली पंक्ति की गणना करने की आवश्यकता है यदि "ए" के लिए गणना से पता चलता है कि इंटरसेक्टिंग की संभावना है। कोई संभावना नहीं अगर दोनों बिंदु ऊपर हैं, या दोनों बिंदु नीचे हैं - जिसे "ए" के लिए गणना द्वारा वर्णित किया गया है।
20

11

वास्तव में Nirg द्वारा पोस्ट किए गए समाधान की तरह और bobobobo द्वारा संपादित। मैंने इसे जावास्क्रिप्ट के अनुकूल बनाया और अपने उपयोग के लिए थोड़ा अधिक सुपाठ्य बनाया:

function insidePoly(poly, pointx, pointy) {
    var i, j;
    var inside = false;
    for (i = 0, j = poly.length - 1; i < poly.length; j = i++) {
        if(((poly[i].y > pointy) != (poly[j].y > pointy)) && (pointx < (poly[j].x-poly[i].x) * (pointy-poly[i].y) / (poly[j].y-poly[i].y) + poly[i].x) ) inside = !inside;
    }
    return inside;
}

8

मैं इस वापस जब मैं नीचे एक शोधकर्ता था पर कुछ काम किया माइकल स्टोनिब्रेकर आप जानते हैं, प्रोफेसर, जो के साथ आया था - Ingres , PostgreSQL , आदि

हमने महसूस किया कि सबसे तेज़ तरीका सबसे पहले एक बाउंडिंग बॉक्स करना था क्योंकि यह बहुत तेज़ है। यदि यह बाउंडिंग बॉक्स के बाहर है, तो यह बाहर है। अन्यथा, आप कड़ी मेहनत करते हैं ...

यदि आप एक महान एल्गोरिथ्म चाहते हैं, तो भू-कार्य के लिए ओपन सोर्स प्रोजेक्ट PostgreSQL स्रोत कोड देखें ...

मैं इंगित करना चाहता हूं, हमें कभी भी बाएं बनाम दाएं हाथ में कोई अंतर्दृष्टि नहीं मिली (कभी-कभी "बाहर" बनाम "अंदर" के रूप में भी व्यक्त)


अपडेट करें

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

इसके अलावा, मेरे काम ने "ऑन द लाइन" परीक्षणों के लिए अलग-अलग कार्यों का उपयोग किया।

... चूँकि किसी ने पूछा: हमें पता लगा कि बाउंडिंग बॉक्स परीक्षण सबसे अच्छा था, जब संख्याओं की संख्या कुछ संख्या से आगे बढ़ गई - यदि आवश्यक हो तो लंबी परीक्षा करने से पहले एक बहुत ही त्वरित परीक्षण करें ... बस एक बाउंडिंग बॉक्स का निर्माण किया जाता है सबसे बड़ा x, सबसे छोटा x, सबसे बड़ा y और सबसे छोटा y और उन्हें एक बॉक्स के चार बिंदु बनाने के लिए एक साथ रखा ...

उन लोगों के लिए एक और टिप जो अनुसरण करते हैं: हमने अपने सभी अधिक परिष्कृत और "लाइट-डिमिंग" कंप्यूटिंग को ग्रिड स्पेस में सभी को एक विमान पर सकारात्मक बिंदुओं में किया और फिर "वास्तविक" देशांतर / अक्षांश में वापस प्रोजेक्ट किया, इस प्रकार संभावित त्रुटियों से बचा चारों ओर रैपिंग जब एक देशांतर रेखा के 180 पार हो जाता है और जब ध्रुवीय क्षेत्रों को संभालता है। बढ़िया काम किया!


यदि मुझे बाउंडिंग बॉक्स नहीं है तो क्या होगा? :)
स्कॉट एवरंडन

8
आप आसानी से एक बाउंडिंग बॉक्स बना सकते हैं - यह सिर्फ चार बिंदु हैं जो सबसे बड़ा और सबसे कम x और सबसे बड़ा और सबसे कम y का उपयोग करते हैं। जल्द ही और अधिक।
टी पर रिचर्ड टी

"... जब एक देशांतर 180 की रेखा को पार कर जाता है और जब ध्रुवीय क्षेत्रों को संभालता है तो चारों ओर लपेटने की संभावित त्रुटियों से बचा जाता है।" क्या आप शायद अधिक विस्तार से इसका वर्णन कर सकते हैं? मुझे लगता है कि मैं अपने पॉलीगॉन 0 देशांतर को पार करने से बचने के लिए सब कुछ 'ऊपर' ले जाने का पता लगा सकता हूं, लेकिन पॉलीगोन को कैसे संभालना है, इस पर मैं स्पष्ट नहीं हूं, जिसमें पोल ​​भी शामिल हैं ...
पॉलीगनों tiritea

6

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

कई अनुकूलन की तरह, ये सामान्य मामलों के बजाय विशिष्ट पर आधारित होते हैं, और एकल उपयोग के बजाय amortized समय के आधार पर बेनीफिट उपज देते हैं।

इस क्षेत्र में काम करते हुए, मुझे जोसेफ ओ'रॉर्क्स की संगणना जियोमेट्री इन सी 'आईएसबीएन 0-521-44034-3 एक बड़ी मदद मिली।


4

तुच्छ समाधान बहुभुज को त्रिभुजों में विभाजित करना होगा और यहां बताए गए परीक्षणों को हिट करना होगा

यदि आपका बहुभुज CONVEX है तो एक बेहतर दृष्टिकोण हो सकता है। बहुभुज को अनंत रेखाओं के संग्रह के रूप में देखें। प्रत्येक पंक्ति अंतरिक्ष को दो में विभाजित करती है। हर बिंदु के लिए यह कहना आसान है अगर एक तरफ या दूसरी तरफ लाइन। यदि कोई बिंदु सभी रेखाओं के एक ही तरफ है तो वह बहुभुज के अंदर है।


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

1
इन curvigons पर कोई संदर्भ मिला?
शॉश

मुझे कर्विजनों के लिए एक रेफरी भी पसंद आएगा।
योएल में जोएल 20'09

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

4

मुझे लगता है कि यह पुराना है, लेकिन यहां एक कोको कास्टिंग एल्गोरिथ्म लागू किया गया है, अगर किसी को दिलचस्पी है। यकीन नहीं है कि यह चीजों को करने का सबसे कुशल तरीका है, लेकिन यह किसी की मदद कर सकता है।

- (BOOL)shape:(NSBezierPath *)path containsPoint:(NSPoint)point
{
    NSBezierPath *currentPath = [path bezierPathByFlatteningPath];
    BOOL result;
    float aggregateX = 0; //I use these to calculate the centroid of the shape
    float aggregateY = 0;
    NSPoint firstPoint[1];
    [currentPath elementAtIndex:0 associatedPoints:firstPoint];
    float olderX = firstPoint[0].x;
    float olderY = firstPoint[0].y;
    NSPoint interPoint;
    int noOfIntersections = 0;

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];
        [currentPath elementAtIndex:n associatedPoints:points];
        aggregateX += points[0].x;
        aggregateY += points[0].y;
    }

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];

        [currentPath elementAtIndex:n associatedPoints:points];
        //line equations in Ax + By = C form
        float _A_FOO = (aggregateY/[currentPath elementCount]) - point.y;  
        float _B_FOO = point.x - (aggregateX/[currentPath elementCount]);
        float _C_FOO = (_A_FOO * point.x) + (_B_FOO * point.y);

        float _A_BAR = olderY - points[0].y;
        float _B_BAR = points[0].x - olderX;
        float _C_BAR = (_A_BAR * olderX) + (_B_BAR * olderY);

        float det = (_A_FOO * _B_BAR) - (_A_BAR * _B_FOO);
        if (det != 0) {
            //intersection points with the edges
            float xIntersectionPoint = ((_B_BAR * _C_FOO) - (_B_FOO * _C_BAR)) / det;
            float yIntersectionPoint = ((_A_FOO * _C_BAR) - (_A_BAR * _C_FOO)) / det;
            interPoint = NSMakePoint(xIntersectionPoint, yIntersectionPoint);
            if (olderX <= points[0].x) {
                //doesn't matter in which direction the ray goes, so I send it right-ward.
                if ((interPoint.x >= olderX && interPoint.x <= points[0].x) && (interPoint.x > point.x)) {  
                    noOfIntersections++;
                }
            } else {
                if ((interPoint.x >= points[0].x && interPoint.x <= olderX) && (interPoint.x > point.x)) {
                     noOfIntersections++;
                } 
            }
        }
        olderX = points[0].x;
        olderY = points[0].y;
    }
    if (noOfIntersections % 2 == 0) {
        result = FALSE;
    } else {
        result = TRUE;
    }
    return result;
}

5
ध्यान दें कि यदि आप वास्तव में इसे कोको में कर रहे हैं, तो आप [NSBezierPath सम्‍मिलन:] विधि का उपयोग कर सकते हैं।
थॉमस डब्ल्यू

4

परीक्षण बिंदुओं के लिए नमूना विधि के साथ निर्ग के उत्तर का ओब्ज-सी संस्करण। निर्ग के जवाब ने मेरे लिए अच्छा काम किया।

- (BOOL)isPointInPolygon:(NSArray *)vertices point:(CGPoint)test {
    NSUInteger nvert = [vertices count];
    NSInteger i, j, c = 0;
    CGPoint verti, vertj;

    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        verti = [(NSValue *)[vertices objectAtIndex:i] CGPointValue];
        vertj = [(NSValue *)[vertices objectAtIndex:j] CGPointValue];
        if (( (verti.y > test.y) != (vertj.y > test.y) ) &&
        ( test.x < ( vertj.x - verti.x ) * ( test.y - verti.y ) / ( vertj.y - verti.y ) + verti.x) )
            c = !c;
    }

    return (c ? YES : NO);
}

- (void)testPoint {

    NSArray *polygonVertices = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(13.5, 41.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 56.5)],
        [NSValue valueWithCGPoint:CGPointMake(39.5, 69.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 84.5)],
        [NSValue valueWithCGPoint:CGPointMake(13.5, 100.0)],
        [NSValue valueWithCGPoint:CGPointMake(6.0, 70.5)],
        nil
    ];

    CGPoint tappedPoint = CGPointMake(23.0, 70.0);

    if ([self isPointInPolygon:polygonVertices point:tappedPoint]) {
        NSLog(@"YES");
    } else {
        NSLog(@"NO");
    }
}

नमूना बहुभुज


2
बेशक, ऑब्जेक्टिव-सी में, CGPathContainsPoint()आपका दोस्त है।
ज़ेव ईसेनबर्ग

@ZevEisenberg लेकिन यह बहुत आसान होगा! नोट के लिए धन्यवाद। मैं उस परियोजना को किसी बिंदु पर खोदूंगा, यह देखने के लिए कि मैंने कस्टम समाधान का उपयोग क्यों किया। मैं संभवतः के बारे में पता नहीं थाCGPathContainsPoint()
जॉन

4

एक समस्या की एक प्रेरक परिभाषा से अधिक सतर्क कुछ भी नहीं है। पूर्णता की खातिर यहां आपके पास एक संस्करण है, जो कि प्रोलॉग में है, जो कि रे कास्टिंग के पीछे के विचारों को भी स्पष्ट कर सकता है :

में सादगी एल्गोरिथ्म के अनुकरण पर आधारित है Http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

कुछ सहायक भविष्यवाणी करते हैं:

exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).

inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) +      X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).

get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).

2 अंक A और B दिए गए रेखा का समीकरण (लाइन (A, B)) है:

                    (YB-YA)
           Y - YA = ------- * (X - XA) 
                    (XB-YB) 

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

               (XB-XA)
           X < ------- * (Y - YA) + XA
               (YB-YA) 

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

is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] =  [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA)); 
                                                        is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).

in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon),  in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line),    in_y_range_at_poly(Coordinate,Line,Polygon), Lines).

traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).

% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).

3

निर्ग के उत्तर का C # संस्करण यहाँ है: मैं सिर्फ कोड साझा करूँगा। यह किसी को कुछ समय बचा सकता है।

public static bool IsPointInPolygon(IList<Point> polygon, Point testPoint) {
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++) {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y) {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X) {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }

यह ज्यादातर मामलों में काम करता है लेकिन यह गलत है और हमेशा सही तरीके से काम नहीं करता है! एम काट्ज़ से समाधान का उपयोग करें जो सही है
लुकास हैनसेक

3

जावा संस्करण:

public class Geocode {
    private float latitude;
    private float longitude;

    public Geocode() {
    }

    public Geocode(float latitude, float longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public float getLatitude() {
        return latitude;
    }

    public void setLatitude(float latitude) {
        this.latitude = latitude;
    }

    public float getLongitude() {
        return longitude;
    }

    public void setLongitude(float longitude) {
        this.longitude = longitude;
    }
}

public class GeoPolygon {
    private ArrayList<Geocode> points;

    public GeoPolygon() {
        this.points = new ArrayList<Geocode>();
    }

    public GeoPolygon(ArrayList<Geocode> points) {
        this.points = points;
    }

    public GeoPolygon add(Geocode geo) {
        points.add(geo);
        return this;
    }

    public boolean inside(Geocode geo) {
        int i, j;
        boolean c = false;
        for (i = 0, j = points.size() - 1; i < points.size(); j = i++) {
            if (((points.get(i).getLongitude() > geo.getLongitude()) != (points.get(j).getLongitude() > geo.getLongitude())) &&
                    (geo.getLatitude() < (points.get(j).getLatitude() - points.get(i).getLatitude()) * (geo.getLongitude() - points.get(i).getLongitude()) / (points.get(j).getLongitude() - points.get(i).getLongitude()) + points.get(i).getLatitude()))
                c = !c;
        }
        return c;
    }

}

2

.Net पोर्ट:

    static void Main(string[] args)
    {

        Console.Write("Hola");
        List<double> vertx = new List<double>();
        List<double> verty = new List<double>();

        int i, j, c = 0;

        vertx.Add(1);
        vertx.Add(2);
        vertx.Add(1);
        vertx.Add(4);
        vertx.Add(4);
        vertx.Add(1);

        verty.Add(1);
        verty.Add(2);
        verty.Add(4);
        verty.Add(4);
        verty.Add(1);
        verty.Add(1);

        int nvert = 6;  //Vértices del poligono

        double testx = 2;
        double testy = 5;


        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((verty[i] > testy) != (verty[j] > testy)) &&
             (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = 1;
        }
    }

2

VBA संस्करण:

नोट: याद रखें कि यदि आपका बहुभुज मानचित्र के भीतर का एक क्षेत्र है कि अक्षांश / देशांतर Y / X मान है जैसा कि X / Y (अक्षांश = Y, देशांतर = X) के विपरीत है, तो जो मैं समझता हूं, उसके पीछे से ऐतिहासिक निहितार्थ हैं देशांतर कोई माप नहीं था।

कक्षा मॉड्यूल: CPoint

Private pXValue As Double
Private pYValue As Double

'''''X Value Property'''''

Public Property Get X() As Double
    X = pXValue
End Property

Public Property Let X(Value As Double)
    pXValue = Value
End Property

'''''Y Value Property'''''

Public Property Get Y() As Double
    Y = pYValue
End Property

Public Property Let Y(Value As Double)
    pYValue = Value
End Property

मापांक:

Public Function isPointInPolygon(p As CPoint, polygon() As CPoint) As Boolean

    Dim i As Integer
    Dim j As Integer
    Dim q As Object
    Dim minX As Double
    Dim maxX As Double
    Dim minY As Double
    Dim maxY As Double
    minX = polygon(0).X
    maxX = polygon(0).X
    minY = polygon(0).Y
    maxY = polygon(0).Y

    For i = 1 To UBound(polygon)
        Set q = polygon(i)
        minX = vbMin(q.X, minX)
        maxX = vbMax(q.X, maxX)
        minY = vbMin(q.Y, minY)
        maxY = vbMax(q.Y, maxY)
    Next i

    If p.X < minX Or p.X > maxX Or p.Y < minY Or p.Y > maxY Then
        isPointInPolygon = False
        Exit Function
    End If


    ' SOURCE: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    isPointInPolygon = False
    i = 0
    j = UBound(polygon)

    Do While i < UBound(polygon) + 1
        If (polygon(i).Y > p.Y) Then
            If (polygon(j).Y < p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        ElseIf (polygon(i).Y < p.Y) Then
            If (polygon(j).Y > p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        End If
        j = i
        i = i + 1
    Loop   
End Function

Function vbMax(n1, n2) As Double
    vbMax = IIf(n1 > n2, n1, n2)
End Function

Function vbMin(n1, n2) As Double
    vbMin = IIf(n1 > n2, n2, n1)
End Function


Sub TestPointInPolygon()

    Dim i As Integer
    Dim InPolygon As Boolean

'   MARKER Object
    Dim p As CPoint
    Set p = New CPoint
    p.X = <ENTER X VALUE HERE>
    p.Y = <ENTER Y VALUE HERE>

'   POLYGON OBJECT
    Dim polygon() As CPoint
    ReDim polygon(<ENTER VALUE HERE>) 'Amount of vertices in polygon - 1
    For i = 0 To <ENTER VALUE HERE> 'Same value as above
       Set polygon(i) = New CPoint
       polygon(i).X = <ASSIGN X VALUE HERE> 'Source a list of values that can be looped through
       polgyon(i).Y = <ASSIGN Y VALUE HERE> 'Source a list of values that can be looped through
    Next i

    InPolygon = isPointInPolygon(p, polygon)
    MsgBox InPolygon

End Sub

2

मैंने निर्ग के c ++ कोड का पायथन कार्यान्वयन किया है :

इनपुट

  • बाउंडिंग_पॉइंट्स: नोड्स जो बहुभुज बनाते हैं।
  • bounding_box_positions: उम्मीदवार फ़िल्टर करने के लिए इंगित करता है। (बाउंडिंग बॉक्स से बनाए गए मेरे कार्यान्वयन में।

    (इनपुट प्रारूप में tuples की सूची इस प्रकार हैं: [(xcord, ycord), ...])

रिटर्न

  • सभी बिंदु जो बहुभुज के अंदर हैं।
def polygon_ray_casting(self, bounding_points, bounding_box_positions):
    # Arrays containing the x- and y-coordinates of the polygon's vertices.
    vertx = [point[0] for point in bounding_points]
    verty = [point[1] for point in bounding_points]
    # Number of vertices in the polygon
    nvert = len(bounding_points)
    # Points that are inside
    points_inside = []

    # For every candidate position within the bounding box
    for idx, pos in enumerate(bounding_box_positions):
        testx, testy = (pos[0], pos[1])
        c = 0
        for i in range(0, nvert):
            j = i - 1 if i != 0 else nvert - 1
            if( ((verty[i] > testy ) != (verty[j] > testy))   and
                    (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) ):
                c += 1
        # If odd, that means that we are inside the polygon
        if c % 2 == 1: 
            points_inside.append(pos)


    return points_inside

फिर, यहाँ से विचार लिया जाता है


2

आश्चर्यचकित कोई भी इसे पहले नहीं लाया था, लेकिन एक डेटाबेस की आवश्यकता वाले व्यावहारिक लोगों के लिए: MongoDB में एक सहित जियो प्रश्नों के लिए उत्कृष्ट समर्थन है।

आप क्या देख रहे हैं:

db.neighborhoods.findOne ({ज्योमेट्री: {$ जियोइंटरसेक्ट्स: {$ ज्योमेट्री: {टाइप: "पॉइंट", निर्देशांक: ["देशांतर", "अक्षांश"]}}}})

Neighborhoodsसंग्रह है कि मानक GeoJson प्रारूप में एक या एक से अधिक बहुभुज संग्रहीत करता है। यदि क्वेरी शून्य हो जाती है तो इसे इंटरसेक्ट नहीं किया जाता है, अन्यथा यह है।

यहाँ बहुत अच्छी तरह से प्रलेखित: https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/

330 अनियमित बहुभुज ग्रिड में वर्गीकृत 6,000 से अधिक बिंदुओं के लिए प्रदर्शन एक मिनट से कम नहीं था, जिसमें कोई अनुकूलन नहीं था और उनके संबंधित बहुभुज के साथ दस्तावेजों को अपडेट करने का समय भी शामिल था।


1

सी में बहुभुज परीक्षण में एक बिंदु का उपयोग करता है जो किरण-कास्टिंग का उपयोग नहीं करता है। और यह अतिव्यापी क्षेत्रों (स्वयं के चौराहों) के लिए काम कर सकता है, use_holesतर्क देख सकता है।

/* math lib (defined below) */
static float dot_v2v2(const float a[2], const float b[2]);
static float angle_signed_v2v2(const float v1[2], const float v2[2]);
static void copy_v2_v2(float r[2], const float a[2]);

/* intersection function */
bool isect_point_poly_v2(const float pt[2], const float verts[][2], const unsigned int nr,
                         const bool use_holes)
{
    /* we do the angle rule, define that all added angles should be about zero or (2 * PI) */
    float angletot = 0.0;
    float fp1[2], fp2[2];
    unsigned int i;
    const float *p1, *p2;

    p1 = verts[nr - 1];

    /* first vector */
    fp1[0] = p1[0] - pt[0];
    fp1[1] = p1[1] - pt[1];

    for (i = 0; i < nr; i++) {
        p2 = verts[i];

        /* second vector */
        fp2[0] = p2[0] - pt[0];
        fp2[1] = p2[1] - pt[1];

        /* dot and angle and cross */
        angletot += angle_signed_v2v2(fp1, fp2);

        /* circulate */
        copy_v2_v2(fp1, fp2);
        p1 = p2;
    }

    angletot = fabsf(angletot);
    if (use_holes) {
        const float nested = floorf((angletot / (float)(M_PI * 2.0)) + 0.00001f);
        angletot -= nested * (float)(M_PI * 2.0);
        return (angletot > 4.0f) != ((int)nested % 2);
    }
    else {
        return (angletot > 4.0f);
    }
}

/* math lib */

static float dot_v2v2(const float a[2], const float b[2])
{
    return a[0] * b[0] + a[1] * b[1];
}

static float angle_signed_v2v2(const float v1[2], const float v2[2])
{
    const float perp_dot = (v1[1] * v2[0]) - (v1[0] * v2[1]);
    return atan2f(perp_dot, dot_v2v2(v1, v2));
}

static void copy_v2_v2(float r[2], const float a[2])
{
    r[0] = a[0];
    r[1] = a[1];
}

नोट: यह कम इष्टतम तरीकों में से एक है क्योंकि इसमें बहुत से कॉल शामिल हैं atan2f, लेकिन यह इस धागे को पढ़ने वाले डेवलपर्स के लिए दिलचस्पी का हो सकता है (मेरे परीक्षणों में इसकी ~ 23x धीमी तो लाइन चौराहा विधि का उपयोग करके)।


0

बहुभुज पर हिट का पता लगाने के लिए हमें दो चीजों का परीक्षण करने की आवश्यकता है:

  1. यदि प्वाइंट बहुभुज क्षेत्र के अंदर है। (रे-कास्टिंग एल्गोरिथम द्वारा पूरा किया जा सकता है)
  2. यदि प्वाइंट बहुभुज सीमा पर है (उसी एल्गोरिथम द्वारा पूरा किया जा सकता है जो पॉलीलाइन (लाइन) पर प्वाइंट डिटेक्शन के लिए उपयोग किया जाता है)।

0

रे कास्टिंग एल्गोरिथ्म में निम्नलिखित विशेष मामलों से निपटने के लिए :

  1. बहुभुज की ओर से किरण एक को ओवरलैप करती है।
  2. बिंदु बहुभुज के अंदर है और किरण बहुभुज के एक शीर्ष से होकर गुजरती है।
  3. बिंदु बहुभुज के बाहर है और किरण केवल बहुभुज के कोण को छूती है।

यह निर्धारित करने की जाँच करें कि क्या एक बिंदु एक जटिल बहुभुज के अंदर है । लेख उन्हें हल करने का एक आसान तरीका प्रदान करता है ताकि उपरोक्त मामलों के लिए कोई विशेष उपचार की आवश्यकता न हो।


0

आप यह जांच कर सकते हैं कि क्या आपके बहुभुज के कोने से वांछित बिंदु को जोड़कर बनाया गया क्षेत्र बहुभुज के क्षेत्र से मेल खाता है।

या आप चेक कर सकते हैं कि आपके बिंदु से आंतरिक कोण का योग आपके चेक प्वाइंट बिंदु पर दो लगातार बहुभुज की प्रत्येक जोड़ी से 360 तक है, लेकिन मुझे लगता है कि पहला विकल्प जल्दी है क्योंकि इसमें विभाजन नहीं हैं और न ही गणना त्रिकोणमितीय कार्यों के विलोम।

मुझे नहीं पता कि क्या होता है यदि आपके बहुभुज के अंदर एक छेद होता है लेकिन यह मुझे लगता है कि मुख्य विचार इस स्थिति के अनुकूल हो सकता है

आप गणित समुदाय में प्रश्न पोस्ट कर सकते हैं। मुझे यकीन है कि उनके पास ऐसा करने के एक मिलियन तरीके हैं


0

यदि आप एक जावा-स्क्रिप्ट लाइब्रेरी की तलाश कर रहे हैं, तो पॉलीगॉन क्लास के लिए जावास्क्रिप्ट गूगल मैप्स v3 एक्सटेंशन है ताकि यह पता लगाया जा सके कि उसके भीतर एक बिंदु रहता है या नहीं।

var polygon = new google.maps.Polygon([], "#000000", 1, 1, "#336699", 0.3);
var isWithinPolygon = polygon.containsLatLng(40, -90);

गूगल एक्स्टेंशन गिथब



0

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

सरल बहुभुजों के लिए सबसे अच्छा एल्गोरिथ्म रे कास्टिंग (क्रॉसिंग नंबर) एल्गोरिदम है। जटिल बहुभुजों के लिए, यह एल्गोरिथ्म उन बिंदुओं का पता नहीं लगाता है जो अतिव्यापी क्षेत्रों के अंदर हैं। तो जटिल बहुभुजों के लिए आपको विंडिंग नंबर एल्गोरिदम का उपयोग करना होगा।

यहाँ दोनों एल्गोरिदम के सी कार्यान्वयन के साथ एक उत्कृष्ट लेख है। मैंने उनकी कोशिश की और वे अच्छे से काम करें।

http://geomalgorithms.com/a03-_inclusion.html


0

निर्ग द्वारा समाधान के स्केल संस्करण (आयत पूर्व-चेक बाउंडिंग बाउंड को अलग से किया जाता है):

def inside(p: Point, polygon: Array[Point], bounds: Bounds): Boolean = {

  val length = polygon.length

  @tailrec
  def oddIntersections(i: Int, j: Int, tracker: Boolean): Boolean = {
    if (i == length)
      tracker
    else {
      val intersects = (polygon(i).y > p.y) != (polygon(j).y > p.y) && p.x < (polygon(j).x - polygon(i).x) * (p.y - polygon(i).y) / (polygon(j).y - polygon(i).y) + polygon(i).x
      oddIntersections(i + 1, i, if (intersects) !tracker else tracker)
    }
  }

  oddIntersections(0, length - 1, tracker = false)
}

0

यहाँ @nirg उत्तर का गोलंग संस्करण है (C@ कोड द्वारा @@ m-katz द्वारा प्रेरित)

func isPointInPolygon(polygon []point, testp point) bool {
    minX := polygon[0].X
    maxX := polygon[0].X
    minY := polygon[0].Y
    maxY := polygon[0].Y

    for _, p := range polygon {
        minX = min(p.X, minX)
        maxX = max(p.X, maxX)
        minY = min(p.Y, minY)
        maxY = max(p.Y, maxY)
    }

    if testp.X < minX || testp.X > maxX || testp.Y < minY || testp.Y > maxY {
        return false
    }

    inside := false
    j := len(polygon) - 1
    for i := 0; i < len(polygon); i++ {
        if (polygon[i].Y > testp.Y) != (polygon[j].Y > testp.Y) && testp.X < (polygon[j].X-polygon[i].X)*(testp.Y-polygon[i].Y)/(polygon[j].Y-polygon[i].Y)+polygon[i].X {
            inside = !inside
        }
        j = i
    }

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