मैं हिट-टेस्टिंग (जैसे ) में उपयोग के लिए, बहुभुज एल्गोरिथ्म के अंदर एक तेजी से 2 डी बिंदु बनाने की कोशिश कर रहा हूं Polygon.contains(p:Point)
। प्रभावी तकनीकों के सुझावों की सराहना की जाएगी।
मैं हिट-टेस्टिंग (जैसे ) में उपयोग के लिए, बहुभुज एल्गोरिथ्म के अंदर एक तेजी से 2 डी बिंदु बनाने की कोशिश कर रहा हूं Polygon.contains(p:Point)
। प्रभावी तकनीकों के सुझावों की सराहना की जाएगी।
जवाबों:
ग्राफिक्स के लिए, मैं नहीं बल्कि पूर्णांक पसंद करेंगे। कई सिस्टम यूआई पेंटिंग के लिए पूर्णांक का उपयोग करते हैं (पिक्सेल सभी के बाद इन्टस हैं), लेकिन उदाहरण के लिए 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/v1y1
v1x2/v1y2
v2x1/v2y1
v2x2/v2y2
YES
NO
YES
NO
COLLINEAR के बारे में क्या? इसका अर्थ है कि दोनों वैक्टर एक ही अनंत रेखा पर स्थित हैं, स्थिति और लंबाई के आधार पर, वे बिल्कुल भी प्रतिच्छेद नहीं करते हैं या वे एक अंतहीन संख्या में बिताते हैं। मुझे पूरी तरह से यकीन नहीं है कि इस मामले को कैसे संभालना है, मैं इसे चौराहे के रूप में नहीं गिनूंगा। खैर, यह मामला चलन में नहीं बल्कि वैसे भी है क्योंकि फ्लोटिंग पॉइंट राउंडिंग त्रुटियां हैं; बेहतर कोड शायद == 0.0f
इसके लिए परीक्षण नहीं करेगा, बल्कि कुछ के लिए < epsilon
, जहां एप्सिलॉन एक छोटी संख्या है।
यदि आपको बड़ी संख्या में बिंदुओं का परीक्षण करने की आवश्यकता है, तो आप निश्चित रूप से बहुभुज पक्षों के रैखिक समीकरण मानक रूपों को स्मृति में रखकर पूरी चीज को थोड़ा तेज कर सकते हैं, इसलिए आपको हर बार इनकी पुनर्गणना नहीं करनी होगी। यह आपको मेमोरी में तीन फ्लोटिंग पॉइंट वैल्यू को स्टोर करने के बदले में प्रत्येक परीक्षण पर दो फ्लोटिंग पॉइंट गुणा और तीन फ़्लोटिंग पॉइंट सबट्रैक्शन बचाएगा। यह एक विशिष्ट मेमोरी बनाम कम्प्यूटेशन टाइम ट्रेड ऑफ है।
अंतिम लेकिन कम से कम नहीं: यदि आप समस्या को हल करने के लिए 3 डी हार्डवेयर का उपयोग कर सकते हैं, तो एक दिलचस्प विकल्प है। बस GPU आप के लिए सभी काम करते हैं। एक पेंटिंग सतह बनाएं जो ऑफ स्क्रीन हो। इसे पूरी तरह से काले रंग से भरें। अब OpenGL या Direct3D को अपने बहुभुज (या यहां तक कि अपने सभी बहुभुजों को पेंट करें) यदि आप परीक्षण करना चाहते हैं कि बिंदु उनमें से किसी के भीतर है, लेकिन आप जिसकी परवाह नहीं करते हैं) और बहुभुज को अलग से भरें रंग, जैसे सफेद। यह देखने के लिए कि क्या कोई बिंदु बहुभुज के भीतर है, इस बिंदु का रंग ड्राइंग सतह से प्राप्त करें। यह सिर्फ एक O (1) मेमोरी लाने वाला है।
निश्चित रूप से यह विधि केवल प्रयोग करने योग्य है यदि आपकी ड्राइंग सतह को विशाल नहीं होना है। यदि यह GPU मेमोरी में फिट नहीं हो सकता है, तो यह विधि CPU पर करने की तुलना में धीमी है। यदि यह विशाल होना होगा और आपका GPU आधुनिक शेड्स का समर्थन करता है, तो आप अभी भी GPU को shader के रूप में ऊपर दिखाए गए किरण कास्टिंग को लागू करके उपयोग कर सकते हैं, जो कि बिल्कुल संभव है। बहुभुज या परीक्षण के लिए बड़ी संख्या में अंक के लिए, यह भुगतान करेगा, विचार करें कि कुछ GPU समानांतर में 64 से 256 बिंदुओं का परीक्षण करने में सक्षम होंगे। ध्यान दें कि सीपीयू से जीपीयू और बैक में डेटा ट्रांसफर करना हमेशा महंगा होता है, इसलिए सिंपल पॉलीगोन के एक जोड़े के खिलाफ कुछ बिंदुओं का परीक्षण करने के लिए, जहां या तो पॉइंट्स या पॉलीगोन डायनेमिक होते हैं और अक्सर बदलते रहेंगे, एक जीपीयू दृष्टिकोण शायद ही कभी भुगतान करेगा। बंद।
मुझे लगता है कि निम्नलिखित कोड का सबसे अच्छा समाधान है ( यहां से लिया गया ):
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;
}
यह छोटा और कुशल दोनों है और उत्तल और अवतल बहुभुज दोनों के लिए काम करता है। जैसा कि पहले सुझाव दिया गया है, आपको पहले बाउंडिंग आयत की जांच करनी चाहिए और बहुभुज छेद का अलग से इलाज करना चाहिए।
इसके पीछे का विचार बहुत सरल है। लेखक इसका वर्णन इस प्रकार करता है:
मैं परीक्षण बिंदु से क्षैतिज रूप से (बढ़ती हुई x, निश्चित y) एक अर्ध-अनंत किरण चलाता हूं और गिनता हूं कि यह कितने किनारों को पार करता है। प्रत्येक क्रॉसिंग पर, किरण अंदर और बाहर के बीच स्विच करती है। इसे जॉर्डन वक्र प्रमेय कहा जाता है।
चर सी 0 से 1 और 1 से 0 तक स्विच कर रहा है हर बार क्षैतिज किरण किसी भी किनारे को पार करती है। तो मूल रूप से यह नज़र रखता है कि पार किए गए किनारों की संख्या सम या विषम है या नहीं। 0 का अर्थ सम है और 1 का अर्थ विषम है।
verty[i]
और verty[j]
दोनों ओर हैं testy
, इसलिए वे कभी बराबर नहीं हैं।
यहाँ 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;
}
निर्ग के दृष्टिकोण पर आधारित एम। काट्ज द्वारा उत्तर का एक जावास्क्रिप्ट संस्करण है:
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;
}
बिंदु पी और बहुभुज के प्रत्येक के बीच कोणों के उन्मुख योग की गणना करें। यदि कुल उन्मुख कोण 360 डिग्री है, तो बिंदु अंदर है। यदि कुल 0 है, तो बिंदु बाहर है।
मुझे यह तरीका बेहतर लगता है क्योंकि यह अधिक मजबूत है और संख्यात्मक परिशुद्धता पर कम निर्भर है।
चौराहों की संख्या की गणना करने वाले तरीके सीमित हैं क्योंकि आप चौराहों की संख्या की गणना के दौरान एक एपेक्स को 'हिट' कर सकते हैं।
संपादित करें: वैसे, यह विधि अवतल और उत्तल बहुभुज के साथ काम करती है।
संपादित करें: मैंने हाल ही में इस विषय पर एक संपूर्ण विकिपीडिया लेख पाया ।
यह सवाल इतना दिलचस्प है। मेरे पास इस पोस्ट के अन्य उत्तरों से अलग एक और व्यावहारिक विचार है। लक्ष्य को अंदर या बाहर करना है, यह तय करने के लिए कोणों के योग का उपयोग करना है। बेहतर घुमावदार संख्या के रूप में जाना जाता है ।
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
एरिक हैंस लेख 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
निर्ग द्वारा उत्तर का स्विफ्ट संस्करण :
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
}
}
वास्तव में 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;
}
मैं इस वापस जब मैं नीचे एक शोधकर्ता था पर कुछ काम किया माइकल स्टोनिब्रेकर आप जानते हैं, प्रोफेसर, जो के साथ आया था - Ingres , PostgreSQL , आदि
हमने महसूस किया कि सबसे तेज़ तरीका सबसे पहले एक बाउंडिंग बॉक्स करना था क्योंकि यह बहुत तेज़ है। यदि यह बाउंडिंग बॉक्स के बाहर है, तो यह बाहर है। अन्यथा, आप कड़ी मेहनत करते हैं ...
यदि आप एक महान एल्गोरिथ्म चाहते हैं, तो भू-कार्य के लिए ओपन सोर्स प्रोजेक्ट PostgreSQL स्रोत कोड देखें ...
मैं इंगित करना चाहता हूं, हमें कभी भी बाएं बनाम दाएं हाथ में कोई अंतर्दृष्टि नहीं मिली (कभी-कभी "बाहर" बनाम "अंदर" के रूप में भी व्यक्त)
अपडेट करें
BKB के लिंक ने उचित एल्गोरिदम की एक अच्छी संख्या प्रदान की। मैं पृथ्वी विज्ञान की समस्याओं पर काम कर रहा था और इसलिए एक ऐसे समाधान की आवश्यकता थी जो अक्षांश / देशांतर में काम करता हो, और इसमें विलक्षणता की अजीब समस्या है - क्या यह छोटे क्षेत्र या बड़े क्षेत्र के अंदर का क्षेत्र है? इसका उत्तर यह है कि लंबवत मामलों की "दिशा" - यह या तो बाएं हाथ से या दाएं हाथ से है और इस तरह आप किसी भी दिए गए बहुभुज के रूप में "अंदर" क्षेत्र को इंगित कर सकते हैं। जैसे, मेरे काम ने उस पेज पर तीन एन्यूमरेटेड समाधान का उपयोग किया।
इसके अलावा, मेरे काम ने "ऑन द लाइन" परीक्षणों के लिए अलग-अलग कार्यों का उपयोग किया।
... चूँकि किसी ने पूछा: हमें पता लगा कि बाउंडिंग बॉक्स परीक्षण सबसे अच्छा था, जब संख्याओं की संख्या कुछ संख्या से आगे बढ़ गई - यदि आवश्यक हो तो लंबी परीक्षा करने से पहले एक बहुत ही त्वरित परीक्षण करें ... बस एक बाउंडिंग बॉक्स का निर्माण किया जाता है सबसे बड़ा x, सबसे छोटा x, सबसे बड़ा y और सबसे छोटा y और उन्हें एक बॉक्स के चार बिंदु बनाने के लिए एक साथ रखा ...
उन लोगों के लिए एक और टिप जो अनुसरण करते हैं: हमने अपने सभी अधिक परिष्कृत और "लाइट-डिमिंग" कंप्यूटिंग को ग्रिड स्पेस में सभी को एक विमान पर सकारात्मक बिंदुओं में किया और फिर "वास्तविक" देशांतर / अक्षांश में वापस प्रोजेक्ट किया, इस प्रकार संभावित त्रुटियों से बचा चारों ओर रैपिंग जब एक देशांतर रेखा के 180 पार हो जाता है और जब ध्रुवीय क्षेत्रों को संभालता है। बढ़िया काम किया!
डेविड सेगोंड का उत्तर मानक सामान्य उत्तर से बहुत अधिक है, और रिचर्ड टी का सबसे आम अनुकूलन है, हालांकि कुछ अन्य हैं। अन्य मजबूत अनुकूलन कम सामान्य समाधानों पर आधारित हैं। उदाहरण के लिए यदि आप बहुत सारे बिंदुओं के साथ एक ही बहुभुज की जांच करने जा रहे हैं, तो बहुभुज को त्रिभुजित करने से चीजों को बहुत तेजी से गति मिल सकती है क्योंकि बहुत तेज TIN खोज एल्गोरिदम की एक संख्या है। एक और अगर पॉलीगॉन और पॉइंट्स कम रिज़ॉल्यूशन में एक सीमित प्लेन पर हैं, तो स्क्रीन डिस्प्ले का कहना है, आप दिए गए रंग में मेमोरी मैप्ड डिस्प्ले बफर पर बहुभुज को पेंट कर सकते हैं, और दिए गए पिक्सेल के रंग को देखने के लिए देख सकते हैं कि क्या यह झूठ है बहुभुज में।
कई अनुकूलन की तरह, ये सामान्य मामलों के बजाय विशिष्ट पर आधारित होते हैं, और एकल उपयोग के बजाय amortized समय के आधार पर बेनीफिट उपज देते हैं।
इस क्षेत्र में काम करते हुए, मुझे जोसेफ ओ'रॉर्क्स की संगणना जियोमेट्री इन सी 'आईएसबीएन 0-521-44034-3 एक बड़ी मदद मिली।
तुच्छ समाधान बहुभुज को त्रिभुजों में विभाजित करना होगा और यहां बताए गए परीक्षणों को हिट करना होगा
यदि आपका बहुभुज CONVEX है तो एक बेहतर दृष्टिकोण हो सकता है। बहुभुज को अनंत रेखाओं के संग्रह के रूप में देखें। प्रत्येक पंक्ति अंतरिक्ष को दो में विभाजित करती है। हर बिंदु के लिए यह कहना आसान है अगर एक तरफ या दूसरी तरफ लाइन। यदि कोई बिंदु सभी रेखाओं के एक ही तरफ है तो वह बहुभुज के अंदर है।
मुझे लगता है कि यह पुराना है, लेकिन यहां एक कोको कास्टिंग एल्गोरिथ्म लागू किया गया है, अगर किसी को दिलचस्पी है। यकीन नहीं है कि यह चीजों को करने का सबसे कुशल तरीका है, लेकिन यह किसी की मदद कर सकता है।
- (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;
}
परीक्षण बिंदुओं के लिए नमूना विधि के साथ निर्ग के उत्तर का ओब्ज-सी संस्करण। निर्ग के जवाब ने मेरे लिए अच्छा काम किया।
- (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");
}
}
CGPathContainsPoint()
आपका दोस्त है।
CGPathContainsPoint()
एक समस्या की एक प्रेरक परिभाषा से अधिक सतर्क कुछ भी नहीं है। पूर्णता की खातिर यहां आपके पास एक संस्करण है, जो कि प्रोलॉग में है, जो कि रे कास्टिंग के पीछे के विचारों को भी स्पष्ट कर सकता है :
में सादगी एल्गोरिथ्म के अनुकरण पर आधारित है 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).
निर्ग के उत्तर का 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;
}
जावा संस्करण:
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;
}
}
.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;
}
}
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
मैंने निर्ग के 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
आश्चर्यचकित कोई भी इसे पहले नहीं लाया था, लेकिन एक डेटाबेस की आवश्यकता वाले व्यावहारिक लोगों के लिए: MongoDB में एक सहित जियो प्रश्नों के लिए उत्कृष्ट समर्थन है।
आप क्या देख रहे हैं:
db.neighborhoods.findOne ({ज्योमेट्री: {$ जियोइंटरसेक्ट्स: {$ ज्योमेट्री: {टाइप: "पॉइंट", निर्देशांक: ["देशांतर", "अक्षांश"]}}}})
Neighborhoods
संग्रह है कि मानक GeoJson प्रारूप में एक या एक से अधिक बहुभुज संग्रहीत करता है। यदि क्वेरी शून्य हो जाती है तो इसे इंटरसेक्ट नहीं किया जाता है, अन्यथा यह है।
यहाँ बहुत अच्छी तरह से प्रलेखित: https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/
330 अनियमित बहुभुज ग्रिड में वर्गीकृत 6,000 से अधिक बिंदुओं के लिए प्रदर्शन एक मिनट से कम नहीं था, जिसमें कोई अनुकूलन नहीं था और उनके संबंधित बहुभुज के साथ दस्तावेजों को अपडेट करने का समय भी शामिल था।
सी में बहुभुज परीक्षण में एक बिंदु का उपयोग करता है जो किरण-कास्टिंग का उपयोग नहीं करता है। और यह अतिव्यापी क्षेत्रों (स्वयं के चौराहों) के लिए काम कर सकता है, 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 धीमी तो लाइन चौराहा विधि का उपयोग करके)।
बहुभुज पर हिट का पता लगाने के लिए हमें दो चीजों का परीक्षण करने की आवश्यकता है:
रे कास्टिंग एल्गोरिथ्म में निम्नलिखित विशेष मामलों से निपटने के लिए :
यह निर्धारित करने की जाँच करें कि क्या एक बिंदु एक जटिल बहुभुज के अंदर है । लेख उन्हें हल करने का एक आसान तरीका प्रदान करता है ताकि उपरोक्त मामलों के लिए कोई विशेष उपचार की आवश्यकता न हो।
आप यह जांच कर सकते हैं कि क्या आपके बहुभुज के कोने से वांछित बिंदु को जोड़कर बनाया गया क्षेत्र बहुभुज के क्षेत्र से मेल खाता है।
या आप चेक कर सकते हैं कि आपके बिंदु से आंतरिक कोण का योग आपके चेक प्वाइंट बिंदु पर दो लगातार बहुभुज की प्रत्येक जोड़ी से 360 तक है, लेकिन मुझे लगता है कि पहला विकल्प जल्दी है क्योंकि इसमें विभाजन नहीं हैं और न ही गणना त्रिकोणमितीय कार्यों के विलोम।
मुझे नहीं पता कि क्या होता है यदि आपके बहुभुज के अंदर एक छेद होता है लेकिन यह मुझे लगता है कि मुख्य विचार इस स्थिति के अनुकूल हो सकता है
आप गणित समुदाय में प्रश्न पोस्ट कर सकते हैं। मुझे यकीन है कि उनके पास ऐसा करने के एक मिलियन तरीके हैं
यदि आप एक जावा-स्क्रिप्ट लाइब्रेरी की तलाश कर रहे हैं, तो पॉलीगॉन क्लास के लिए जावास्क्रिप्ट गूगल मैप्स v3 एक्सटेंशन है ताकि यह पता लगाया जा सके कि उसके भीतर एक बिंदु रहता है या नहीं।
var polygon = new google.maps.Polygon([], "#000000", 1, 1, "#336699", 0.3);
var isWithinPolygon = polygon.containsLatLng(40, -90);
उपयोग करते समय क्यूटी(Qt 4.3+), कोई भी QPolygon के फ़ंक्शन में शामिल हो सकता है
उत्तर इस बात पर निर्भर करता है कि आपके पास सरल या जटिल बहुभुज हैं या नहीं। साधारण बहुभुजों में कोई भी खंड खंड चौराहा नहीं होना चाहिए। इसलिए उनके पास छेद हो सकते हैं लेकिन लाइनें एक दूसरे को पार नहीं कर सकती हैं। जटिल क्षेत्रों में लाइन चौराहे हो सकते हैं - इसलिए उनके पास अतिव्यापी क्षेत्र हो सकते हैं, या एक ही बिंदु से एक-दूसरे को छूने वाले क्षेत्र हो सकते हैं।
सरल बहुभुजों के लिए सबसे अच्छा एल्गोरिथ्म रे कास्टिंग (क्रॉसिंग नंबर) एल्गोरिदम है। जटिल बहुभुजों के लिए, यह एल्गोरिथ्म उन बिंदुओं का पता नहीं लगाता है जो अतिव्यापी क्षेत्रों के अंदर हैं। तो जटिल बहुभुजों के लिए आपको विंडिंग नंबर एल्गोरिदम का उपयोग करना होगा।
यहाँ दोनों एल्गोरिदम के सी कार्यान्वयन के साथ एक उत्कृष्ट लेख है। मैंने उनकी कोशिश की और वे अच्छे से काम करें।
निर्ग द्वारा समाधान के स्केल संस्करण (आयत पूर्व-चेक बाउंडिंग बाउंड को अलग से किया जाता है):
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)
}
यहाँ @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
}