यह कैसे निर्धारित करें कि बहुभुज बिंदुओं की सूची दक्षिणावर्त क्रम में है?


259

बिंदुओं की एक सूची होने के बाद, मुझे यह कैसे पता चलेगा कि वे घड़ी की दिशा में हैं?

उदाहरण के लिए:

point[0] = (5,0)
point[1] = (6,4)
point[2] = (4,5)
point[3] = (1,5)
point[4] = (1,0)

कहेंगे कि यह कुछ लोगों के लिए दक्षिणावर्त विरोधी (या काउंटर-क्लॉकवाइज) है।


4
कृपया ध्यान दें: स्वीकृत उत्तर, और इसके बाद के कई उत्तरों के लिए बहुत सारे अतिरिक्त और गुणा की आवश्यकता होती है (वे क्षेत्र गणना पर आधारित होते हैं जो नकारात्मक या सकारात्मक को समाप्त करते हैं; उदाहरण के लिए "शॉलेस फॉर्मूला")। उनमें से किसी एक को लागू करने से पहले, lhf के उत्तर पर विचार करें , जो सरल / तेज है - विकी पर आधारित - सरल बहुभुज का उन्मुखीकरण
टूलमेकर 20

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

जवाबों:


416

सुझाए गए कुछ तरीके गैर-उत्तल बहुभुज के मामले में विफल होंगे, जैसे कि अर्धचंद्राकार। यहां एक सरल एक है जो गैर-उत्तल बहुभुज के साथ काम करेगा (यह एक आत्म-प्रतिच्छेदन बहुभुज के साथ भी काम करेगा, जैसे कि एक आंकड़ा-आठ, यह बताता है कि क्या यह ज्यादातर दक्षिणावर्त है)।

किनारों पर योग, (x 2 - x 1 ) (y 2 + y 1 )। यदि परिणाम सकारात्मक है, तो वक्र दक्षिणावर्त है, यदि यह नकारात्मक है तो वक्र प्रति-दक्षिणावर्त है। (परिणाम +/- सम्मेलन के साथ संलग्न क्षेत्र से दोगुना है।)

point[0] = (5,0)   edge[0]: (6-5)(4+0) =   4
point[1] = (6,4)   edge[1]: (4-6)(5+4) = -18
point[2] = (4,5)   edge[2]: (1-4)(5+5) = -30
point[3] = (1,5)   edge[3]: (1-1)(0+5) =   0
point[4] = (1,0)   edge[4]: (5-1)(0+0) =   0
                                         ---
                                         -44  counter-clockwise

28
यह एक साधारण मामले में लागू पथरी है। (मेरे पास ग्राफिक्स पोस्ट करने का कौशल नहीं है।) एक लाइन सेगमेंट के तहत क्षेत्र इसकी औसत ऊंचाई (y2 + y1) / 2 बार इसकी क्षैतिज लंबाई (x2-X1) के बराबर है। X में साइन कन्वेंशन को नोटिस करें। इसे कुछ त्रिकोणों के साथ आज़माएँ और आप जल्द ही देखेंगे कि यह कैसे काम करता है।
बीटा

72
एक मामूली चेतावनी: यह उत्तर एक सामान्य कार्टेशियन समन्वय प्रणाली मानता है। यह कारण ध्यान देने योग्य है कि HTML5 कैनवास जैसे कुछ सामान्य संदर्भ, उल्टे Y- अक्ष का उपयोग करते हैं। फिर नियम को फ़्लिप करना होगा: यदि क्षेत्र नकारात्मक है , तो वक्र दक्षिणावर्त है।
लार्स

8
@ Mr.Qbs: तो मेरा तरीका काम करता है, लेकिन यदि आप एक महत्वपूर्ण हिस्सा छोड़ते हैं , तो यह काम नहीं करता है। यह खबर नहीं है।
बीटा

11
@ Mr.Qbs: आपको हमेशा अंतिम बिंदु को पहले एक से जोड़ना होगा। यदि आपके पास N अंक 0 से N-1 तक है, तो आपको गणना करनी होगी: Sum( (x[(i+1) mod N] - x[i]) * (y[i] + y[(i+1) mod N]) )i = 0 से N-1 के लिए। Ie, सूचकांक Modulo N ( N ≡ 0) सूत्र को बंद पॉलीगोन के लिए ही काम करना चाहिए । बहुभुजों में कोई काल्पनिक किनारा नहीं है।
ओलिवियर जैकोट-डेसकॉम्ब्स

4
यह ब्लॉग .element84.com/polygon-winding.html सरल अंग्रेजी में बताता है कि यह समाधान क्यों काम करता है।
डेविड जोरिछा

49

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

तो, बहुभुज के प्रत्येक शीर्ष (बिंदु) के लिए, दो निकटवर्ती किनारों के क्रॉस-उत्पाद परिमाण की गणना करें:

Using your data:
point[0] = (5, 0)
point[1] = (6, 4)
point[2] = (4, 5)
point[3] = (1, 5)
point[4] = (1, 0)

तो लगातार किनारों को लेबल
edgeAसे खंड है point0करने के लिए point1और
edgeBके बीच point1करने के लिए point2
...
edgeEके बीच है point4और point0

तब वर्टेक्स ए ( point0) के बीच है
edgeE[से point4करने के लिए point0]
edgeA[से point0`point1 'के लिए

ये दो किनारे स्वयं वैक्टर हैं, जिनके x और y निर्देशांक उनके प्रारंभ और अंत बिंदुओं के निर्देशांक घटाकर निर्धारित किए जा सकते हैं:

edgeE= point0- point4= (1, 0) - (5, 0)= (-4, 0) और
edgeA= point1- point0= (6, 4) - (1, 0)= (5, 4) और

और इन दो समीपस्थ किनारों के पार उत्पाद निम्नलिखित मैट्रिक्स, जो अक्ष समन्वय प्रतीकों तीन का प्रतिनिधित्व करने के नीचे दो वैक्टर के निर्देशांक रख कर निर्माण किया है की निर्धारक का उपयोग कर गणना की जाती है ( i, j, और k)। तीसरा (शून्य) -प्रवेशित समन्वय है क्योंकि क्रॉस उत्पाद अवधारणा 3-डी निर्माण है, और इसलिए हम क्रॉस-उत्पाद को लागू करने के लिए इन 2-डी वैक्टर को 3-डी में विस्तारित करते हैं:

 i    j    k 
-4    0    0
 1    4    0    

यह देखते हुए कि सभी क्रॉस-उत्पाद दो वैक्टर के विमान के लिए एक वेक्टर लंबवत पैदा करते हैं, जिसे गुणा किया जाता है, ऊपर दिए गए मैट्रिक्स के निर्धारक में केवल k(या z- अक्ष) घटक होता है। या z- अक्ष घटक
के परिमाण की गणना करने का सूत्र = है k
a1*b2 - a2*b1 = -4* 4 - 0* 1-16

इस मान का परिमाण ( -16), 2 मूल वैक्टरों के बीच के कोण का माप है, जो 2 वैक्टरों के परिमाण के गुणन द्वारा गुणा किया जाता है।
दरअसल, इसके मूल्य का एक और सूत्र है
A X B (Cross Product) = |A| * |B| * sin(AB)

तो, कोण के एक माप को वापस पाने के लिए आपको इस मूल्य को विभाजित करने की आवश्यकता है, ( -16), दो वैक्टर के परिमाण के उत्पाद द्वारा।

|A| * |B| = 4 * Sqrt(17) =16.4924...

तो पाप का उपाय (AB) = -16 / 16.4924=-.97014...

यह एक उपाय है कि क्या शीर्ष खंड के बाद, बाईं या दाईं ओर मुड़ा हुआ है, और कितना। चाप-साइन लेने की कोई आवश्यकता नहीं है। हम इसके बारे में परवाह करेंगे इसकी परिमाण है, और निश्चित रूप से इसका संकेत (सकारात्मक या नकारात्मक) है!

बंद रास्ते के आसपास अन्य 4 बिंदुओं में से प्रत्येक के लिए ऐसा करें, और प्रत्येक शीर्ष पर इस गणना से मान जोड़ें।

यदि अंतिम राशि सकारात्मक है, तो आप दक्षिणावर्त, नकारात्मक, वामावर्त गए।


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

1
मेरा समाधान प्रत्येक शीर्ष पर किनारे के कोणों में परिवर्तन की साइन की राशि को मापता है। यह सकारात्मक होगा जब घड़ी की दिशा में नकारात्मक और नकारात्मक जब काउंटर दक्षिणावर्त traversing।
चार्ल्स ब्रेटाना

2
यह इस दृष्टिकोण के साथ लगता है कि आपको आर्क्सिन लेने की आवश्यकता है, जब तक कि आप उत्तलता नहीं लेते हैं (जिस स्थिति में आपको केवल एक शीर्ष की जांच करने की आवश्यकता है)
एजेंटप

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

1
@CharlesBretana - जबकि मैंने ल्यूक का टेस्ट नहीं चलाया है, मेरा मानना ​​है कि वह सही है। यह एक अरेखीय पैमाने [बिना आर्क्सिन बनाम आर्क्सिन के] के साथ संयुक्त योग की प्रकृति है । गौर कीजिए कि आपने क्या सुझाव दिया था, कि आपने सही तरीके से खारिज कर दिया। उन्होंने सुझाव दिया कि आप "बस गिनती" करते हैं, और आपने बताया कि मुट्ठी भर बड़े मूल्य बड़ी संख्या में छोटे मूल्यों से आगे निकल सकते हैं। अब प्रत्येक मान बनाम नहीं के अर्कसीन पर विचार करें। क्या यह अभी भी मामला नहीं है कि आर्क्सिन लेने में विफल रहने से प्रत्येक मूल्य को गलत वजन मिलता है, इसलिए एक ही दोष है (हालांकि बहुत कम है)?
टूलमेकर 20

47

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

हस्ताक्षरित क्षेत्र की गणना करें: A = 1/2 * (x 1 * y 2 - x 2 * y 1 + x 2 * y 3 - x 3 * y 2 + ... + x n * y 1 - x 1 * y n )

या छद्म कोड में:

signedArea = 0
for each point in points:
    x1 = point[0]
    y1 = point[1]
    if point is last point
        x2 = firstPoint[0]
        y2 = firstPoint[1]
    else
        x2 = nextPoint[0]
        y2 = nextPoint[1]
    end if

    signedArea += (x1 * y2 - x2 * y1)
end for
return signedArea / 2

ध्यान दें कि यदि आप केवल ऑर्डर की जाँच कर रहे हैं, तो आपको 2 से विभाजित करने की आवश्यकता नहीं है।

सूत्र: http://mathworld.wolfram.com/PolygonArea.html


क्या आपके हस्ताक्षरित क्षेत्र सूत्र में कोई टाइपो ऊपर था? यह "xn * y1 - X1 * yn" के साथ समाप्त होता है; जब मेरा मानना ​​है कि यह "x_n y_ {n + 1} - y_n x_ {n-1}" होना चाहिए (LaTeX में, कम से कम)। दूसरी ओर, यह दस साल हो गए हैं जब मैंने कोई रेखीय बीजगणित कक्षाएं ली हैं।
माइकल एरिक ओबेरलिन

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

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

2
@EricFortier - FWIW, संभवतः एक बड़े सरणी का आकार बदलने के बजाय, प्रत्येक पुनरावृत्ति के लिए एक कुशल विकल्प है कि वह previousPointअगले पुनरावृत्ति के रूप में अपनी बात को बचा सके । लूप शुरू करने से पहले, previousPointसरणी के अंतिम बिंदु पर सेट करें। व्यापार बंद अतिरिक्त स्थानीय चर प्रति है, लेकिन कम सरणी पहुंच। और सबसे महत्वपूर्ण बात, इनपुट सरणी को स्पर्श करना नहीं है।
टूलमेकरसैट

2
@MichaelEricOberlin - बहुभुज को बंद करने के लिए आवश्यक है , अंतिम बिंदु से पहले बिंदु तक लाइन खंड को शामिल करके। (एक सही गणना समान होगी, कोई फर्क नहीं पड़ता कि कौन सा बिंदु बंद बहुभुज शुरू करता है।)
टूलमेकर

36

सबसे छोटी y (और यदि संबंध हैं तो सबसे बड़ी x) के साथ शीर्ष ज्ञात करें। बता दें कि वर्टेक्स हो सकता है Aऔर सूची में पिछला शीर्ष हो सकता है Bऔर सूची में अगला शीर्ष हो सकता है C। अब गणना संकेत के पार उत्पाद की ABऔरAC


संदर्भ:


7
यह en.wikipedia.org/wiki/Curve_orientation में भी बताया गया है । मुद्दा यह है कि पाया बिंदु उत्तल पतवार पर होना चाहिए, और पूरे बहुभुज के उन्मुखीकरण को निर्धारित करने के लिए उत्तल पतवार (और इसके निकटवर्ती पड़ोसियों) पर एक बिंदु पर स्थानीय रूप से देखने के लिए केवल आवश्यक है।
एम काट्ज

1
हैरान और जागृत यह अधिक upvotes प्राप्त नहीं हुआ है। सरल बहुभुजों के लिए ( जो कुछ क्षेत्रों में सबसे बहुभुज है ), इस उत्तर से एक O(1)हल निकलता है। अन्य सभी उत्तर बहुभुज बिंदुओं की संख्या के O(n)लिए समाधान nनिकालते हैं। और भी गहरी अनुकूलन के लिए, विकिपीडिया के शानदार वक्र अभिविन्यास लेख के व्यावहारिक विचार उपधारा देखें।
सेसिल करी

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


1
@CecilCurry मुझे लगता है कि आपकी दूसरी टिप्पणी बताती है कि इसने अधिक वृद्धि क्यों नहीं प्राप्त की। यह उन सीमाओं के किसी भी उल्लेख के बिना, कुछ परिदृश्यों में गलत उत्तर देता है।
लार्ष

23

इस उत्तर पर आधारित एल्गोरिथ्म का एक सरल C # कार्यान्वयन यहां दिया गया है

मान लेते हैं कि हमारे पास ए Vector प्रकार है Xऔर Yप्रकार के गुण हैं double

public bool IsClockwise(IList<Vector> vertices)
{
    double sum = 0.0;
    for (int i = 0; i < vertices.Count; i++) {
        Vector v1 = vertices[i];
        Vector v2 = vertices[(i + 1) % vertices.Count];
        sum += (v2.X - v1.X) * (v2.Y + v1.Y);
    }
    return sum > 0.0;
}

% मॉडुलो या शेष संचालक मोडुलो ऑपरेशन करता है जो ( विकिपीडिया के अनुसार ) एक के बाद एक संख्या के विभाजन के बाद शेष पाता है।


6

किसी एक कोने पर शुरू करें, और प्रत्येक पक्ष द्वारा सममित कोण की गणना करें।

पहला और अंतिम शून्य होगा (इसलिए उन को छोड़ें); बाकी के लिए, कोण की साइन को सामान्यीकरण के क्रॉस उत्पाद द्वारा यूनिट की लंबाई (बिंदु [n] -पंट [0]) और (बिंदु [n-1] -point [0]) को दिया जाएगा।

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


यह देखते हुए कि मूल रूप से क्रॉस उत्पाद मूल रूप से कोण के साइन को एक सकारात्मक स्केलिंग कारक के रूप में कैसे उबालता है, यह संभवतः क्रॉस उत्पाद करने के लिए बेहतर है। यह तेज़ और कम जटिल होगा।
ReaperUnreal

4

इसके लायक क्या है, मैंने इस मिक्सिन का इस्तेमाल Google मैप्स एपीआई v3 ऐप के वाइंडिंग ऑर्डर की गणना के लिए किया।

कोड बहुभुज क्षेत्रों के साइड इफेक्ट का लाभ उठाता है: वर्टेक्स का एक दक्षिणावर्त वाइंडिंग ऑर्डर एक पॉजिटिव एरिया देता है, जबकि उसी वर्टेक्स का काउंटर-क्लॉकवाइज वाइंडिंग ऑर्डर उसी एरिया को नेगेटिव वैल्यू बनाता है। कोड Google मैप्स ज्यामिति पुस्तकालय में एक निजी एपीआई का भी उपयोग करता है। मैंने इसका उपयोग करने में सहज महसूस किया - अपने जोखिम पर उपयोग करें।

नमूना उपयोग:

var myPolygon = new google.maps.Polygon({/*options*/});
var isCW = myPolygon.isPathClockwise();

यूनिट परीक्षणों के साथ पूर्ण उदाहरण @ http://jsfiddle.net/stevejansen/bq2ec/

/** Mixin to extend the behavior of the Google Maps JS API Polygon type
 *  to determine if a polygon path has clockwise of counter-clockwise winding order.
 *  
 *  Tested against v3.14 of the GMaps API.
 *
 *  @author  stevejansen_github@icloud.com
 *
 *  @license http://opensource.org/licenses/MIT
 *
 *  @version 1.0
 *
 *  @mixin
 *  
 *  @param {(number|Array|google.maps.MVCArray)} [path] - an optional polygon path; defaults to the first path of the polygon
 *  @returns {boolean} true if the path is clockwise; false if the path is counter-clockwise
 */
(function() {
  var category = 'google.maps.Polygon.isPathClockwise';
     // check that the GMaps API was already loaded
  if (null == google || null == google.maps || null == google.maps.Polygon) {
    console.error(category, 'Google Maps API not found');
    return;
  }
  if (typeof(google.maps.geometry.spherical.computeArea) !== 'function') {
    console.error(category, 'Google Maps geometry library not found');
    return;
  }

  if (typeof(google.maps.geometry.spherical.computeSignedArea) !== 'function') {
    console.error(category, 'Google Maps geometry library private function computeSignedArea() is missing; this may break this mixin');
  }

  function isPathClockwise(path) {
    var self = this,
        isCounterClockwise;

    if (null === path)
      throw new Error('Path is optional, but cannot be null');

    // default to the first path
    if (arguments.length === 0)
        path = self.getPath();

    // support for passing an index number to a path
    if (typeof(path) === 'number')
        path = self.getPaths().getAt(path);

    if (!path instanceof Array && !path instanceof google.maps.MVCArray)
      throw new Error('Path must be an Array or MVCArray');

    // negative polygon areas have counter-clockwise paths
    isCounterClockwise = (google.maps.geometry.spherical.computeSignedArea(path) < 0);

    return (!isCounterClockwise);
  }

  if (typeof(google.maps.Polygon.prototype.isPathClockwise) !== 'function') {
    google.maps.Polygon.prototype.isPathClockwise = isPathClockwise;
  }
})();

यह कोशिश करने पर मुझे इसका उल्टा परिणाम मिलता है, दक्षिणावर्त क्रम में खींचा गया एक बहुभुज ऋणात्मक क्षेत्र उत्पन्न करता है, जबकि एक खींचा गया काउंटर दक्षिणावर्त धनात्मक उत्पन्न करता है। किसी भी मामले में, यह स्निपेट अभी भी सुपर उपयोगी है 5yrs पर, धन्यवाद।
कैमरन रॉबर्ट्स

@CameronRoberts मानदंड (विशेष रूप से जियोसन के लिए IETF देखें) 'राइट-हैंड नियम' का पालन करना है। मुझे लगता है कि Google से शिकायत है। उस स्थिति में बाहरी रिंग को वामावर्त (पॉजिटिव पॉजिटिव एरिया) होना चाहिए, और इनर रिंग (छेद) क्लॉकवाइज (मुख्य क्षेत्र से हटाए जाने वाले नकारात्मक क्षेत्र) को घुमावदार कर रहे हैं।
एग्ज़ एल'ओम

4

जावास्क्रिप्ट में शॉन के उत्तर का कार्यान्वयन :

function calcArea(poly) {
    if(!poly || poly.length < 3) return null;
    let end = poly.length - 1;
    let sum = poly[end][0]*poly[0][1] - poly[0][0]*poly[end][1];
    for(let i=0; i<end; ++i) {
        const n=i+1;
        sum += poly[i][0]*poly[n][1] - poly[n][0]*poly[i][1];
    }
    return sum;
}

function isClockwise(poly) {
    return calcArea(poly) > 0;
}

let poly = [[352,168],[305,208],[312,256],[366,287],[434,248],[416,186]];

console.log(isClockwise(poly));

let poly2 = [[618,186],[650,170],[701,179],[716,207],[708,247],[666,259],[637,246],[615,219]];

console.log(isClockwise(poly2));

बहुत यकीन है कि यह सही है। लगता है यह काम कर रहा है :-)

यदि आप सोच रहे हैं तो ये बहुभुज इस तरह दिखते हैं:


3

यह OpenLayers 2 के लिए कार्यान्वित फ़ंक्शन है । क्लॉकवाइज बहुभुज होने की स्थिति area < 0, इस संदर्भ द्वारा पुष्टि की गई है ।

function IsClockwise(feature)
{
    if(feature.geometry == null)
        return -1;

    var vertices = feature.geometry.getVertices();
    var area = 0;

    for (var i = 0; i < (vertices.length); i++) {
        j = (i + 1) % vertices.length;

        area += vertices[i].x * vertices[j].y;
        area -= vertices[j].x * vertices[i].y;
        // console.log(area);
    }

    return (area < 0);
}

Openlayers javascript आधारित मैप मैनेजमेंट लाइब्रेरी है जैसे googlemaps और इसे openlayers 2 में लिखा और इस्तेमाल किया जाता है
MSS

क्या आप थोड़ा सा समझा सकते हैं कि आपका कोड क्या करता है, और आप ऐसा क्यों कर रहे हैं?
nbro

@ यह कोड lhf उत्तर को लागू करता है । सीधे तौर पर पैरामीटर के रूप में कोने होने से गैर ओपनलेयर भाग को शुद्ध जावास्क्रिप्ट फ़ंक्शन में रखना आसान है । यह अच्छी तरह से काम करता है, और मल्टीपोलियन के मामले में अनुकूलित किया जा सकता है ।
एलीज एल'ओम

2

यदि आप Matlab का उपयोग करते हैं, तो फ़ंक्शन ispolycwसही हो जाता है यदि बहुभुज कोने दक्षिणावर्त क्रम में होते हैं।


1

जैसा कि इस विकिपीडिया लेख में बताया गया है वक्र अभिविन्यास , दिए गए 3 अंक p, qऔर rविमान पर (यानी x और y निर्देशांक के साथ), आप निम्नलिखित निर्धारक के संकेत की गणना कर सकते हैं

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

यदि निर्धारक नकारात्मक (यानी Orient(p, q, r) < 0) है, तो बहुभुज उन्मुख दक्षिणावर्त (सीडब्ल्यू) है। यदि निर्धारक सकारात्मक (यानी Orient(p, q, r) > 0) है, तो बहुभुज उन्मुख वामावर्त (CCW) है। निर्धारक शून्य (यानी है Orient(p, q, r) == 0) अंक अगर p, qऔर rकर रहे हैं समरेख

उपरोक्त सूत्र में, हम लोगों को निर्देशांक के सामने प्रस्तुत करते हैं p, q और rक्योंकि हम सजातीय निर्देशांक का उपयोग कर रहे हैं ।


@tibetty क्या आप बता सकते हैं कि बहुभुज अवतल होने पर यह विधि कई स्थितियों में काम क्यों नहीं करेगी?
नबंर

1
कृपया अपनी पोस्ट में विकी आइटम संदर्भ की अंतिम तालिका देखें। मेरे लिए एक गलत उदाहरण देना आसान है लेकिन इसे साबित करना कठिन है।
tibetty

1
कृपया अपनी पोस्ट में विकी आइटम संदर्भ की अंतिम तालिका देखें। मेरे लिए एक गलत उदाहरण देना आसान है लेकिन इसे साबित करना कठिन है।
टिबेट्टी

1
@tibetty सही है। आप बहुभुज के साथ किसी भी तीन अंक नहीं ले सकते हैं; आप उस बहुभुज के उत्तल या अवतल क्षेत्र में हो सकते हैं। विकी को ध्यान से पढ़ते हुए, एक को पोलीगोन को घेरने वाले उत्तल पतवार के साथ तीन बिंदु लेने चाहिए । "व्यावहारिक विचारों" से: "एक उपयुक्त शीर्ष को खोजने के लिए बहुभुज के उत्तल पतवार का निर्माण करने की आवश्यकता नहीं होती है। एक सामान्य विकल्प सबसे छोटे एक्स-समन्वय के साथ बहुभुज का शीर्ष है। यदि उनमें से कई हैं, तो एक। सबसे छोटे Y- समन्वय के साथ उठाया जाता है। यह बहुभुज के उत्तल पतवार के [क] शीर्ष होने की गारंटी है। "
टूलमेकरसेव

1
इसलिए lhf का पूर्व उत्तर , जो समान है, और एक ही विकि लेख को संदर्भित करता है, लेकिन इस तरह के बिंदु को निर्दिष्ट करता है। [स्पष्ट रूप से यह कोई फर्क नहीं पड़ता कि कोई सबसे छोटा या सबसे बड़ा, x या y लेता है, जब तक कोई बीच में आने से बचता है; प्रभावी ढंग से एक बहुभुज के चारों ओर बाउंडिंग बॉक्स के एक किनारे से काम कर रहा है, एक अवतल क्षेत्र में गारंटी देने के लिए।]
टूलमेकरसैट

0

मुझे लगता है कि कुछ बिंदुओं के लिए दक्षिणावर्त दिए जाने के लिए सभी किनारों को सकारात्मक होना चाहिए न कि किनारों का योग। यदि एक किनारे नकारात्मक है तो कम से कम 3 अंक काउंटर-क्लॉकवाइज दिए जाते हैं।


सच है, लेकिन आप बहुभुज के घुमावदार आदेश (घड़ी या घड़ी की दिशा में) की अवधारणा को गलत समझते हैं। एक पूरी तरह से उत्तल बहुभुज में, सभी बिंदुओं पर कोण दक्षिणावर्त होगा या सभी काउंटर-क्लॉकवाइज़ [जैसा कि आपके वाक्य में होगा]। अवतल क्षेत्र (नों) वाले बहुभुज में, "गुफाएं" विपरीत दिशा में होंगी, लेकिन बहुभुज के रूप में अभी भी एक अच्छी तरह से परिभाषित इंटीरियर है, और तदनुसार घड़ी या काउंटर-दक्षिणावर्त माना जाता है। देखें en.wikipedia.org/wiki/...
ToolmakerSteve

0

मेरा C # / LINQ समाधान @charlesbretana के क्रॉस उत्पाद सलाह पर आधारित है, जो नीचे है। आप घुमावदार के लिए एक संदर्भ सामान्य निर्दिष्ट कर सकते हैं। यह तब तक काम करता है जब तक वक्र ज्यादातर अप वेक्टर द्वारा परिभाषित विमान में होता है।

using System.Collections.Generic;
using System.Linq;
using System.Numerics;

namespace SolidworksAddinFramework.Geometry
{
    public static class PlanePolygon
    {
        /// <summary>
        /// Assumes that polygon is closed, ie first and last points are the same
        /// </summary>
       public static bool Orientation
           (this IEnumerable<Vector3> polygon, Vector3 up)
        {
            var sum = polygon
                .Buffer(2, 1) // from Interactive Extensions Nuget Pkg
                .Where(b => b.Count == 2)
                .Aggregate
                  ( Vector3.Zero
                  , (p, b) => p + Vector3.Cross(b[0], b[1])
                                  /b[0].Length()/b[1].Length());

            return Vector3.Dot(up, sum) > 0;

        } 

    }
}

एक यूनिट टेस्ट के साथ

namespace SolidworksAddinFramework.Spec.Geometry
{
    public class PlanePolygonSpec
    {
        [Fact]
        public void OrientationShouldWork()
        {

            var points = Sequences.LinSpace(0, Math.PI*2, 100)
                .Select(t => new Vector3((float) Math.Cos(t), (float) Math.Sin(t), 0))
                .ToList();

            points.Orientation(Vector3.UnitZ).Should().BeTrue();
            points.Reverse();
            points.Orientation(Vector3.UnitZ).Should().BeFalse();



        } 
    }
}

0

अन्य उत्तरों में स्पष्टीकरण का उपयोग करके यह मेरा समाधान है:

def segments(poly):
    """A sequence of (x,y) numeric coordinates pairs """
    return zip(poly, poly[1:] + [poly[0]])

def check_clockwise(poly):
    clockwise = False
    if (sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0:
        clockwise = not clockwise
    return clockwise

poly = [(2,2),(6,2),(6,6),(2,6)]
check_clockwise(poly)
False

poly = [(2, 6), (6, 6), (6, 2), (2, 2)]
check_clockwise(poly)
True

1
क्या आप निर्दिष्ट कर सकते हैं कि अन्य उत्तर वास्तव में किस पर आधारित है?
नबंर

0

एक बहुत computationally सरल तरीका, आप पहले से ही बहुभुज के अंदर एक बिंदु जानते हैं :

  1. उस क्रम में मूल बहुभुज, बिंदुओं और उनके निर्देशांक में से कोई भी रेखा खंड चुनें।

  2. एक ज्ञात "अंदर" बिंदु जोड़ें, और एक त्रिकोण बनाएं।

  3. सीडब्ल्यू या सीसीडब्ल्यू की गणना उन तीन बिंदुओं के साथ करें


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

यह काम करता है भले ही बहुभुज अवतल हो। बिंदु को उस अवतल बहुभुज के अंदर होना चाहिए। हालाँकि मैं जटिल बहुभुज के बारे में निश्चित नहीं हूँ (परीक्षण नहीं किया।)
वेंकट गोला

"यह तब भी काम करता है जब बहुभुज अवतल हो।" - प्रतिपक्ष: पाली (0,0), (1,1), (0,2), (2,2), (2,0)। लाइन खंड (1,1), (0, 2)। यदि आप त्रिभुज बनाने के लिए (1,1), (0,2), (1,2) के भीतर एक आंतरिक बिंदु चुनते हैं -> (1,1), (0,2), (0.5,1.5), तो आप प्राप्त करते हैं यदि आप (0,0), (1,1), (1,0)> (1,1), (0,2), (0.5,0.5) के भीतर एक आंतरिक बिंदु चुनते हैं, तो विपरीत वाइंडिंग। वे दोनों मूल बहुभुज के आंतरिक हैं, फिर भी विपरीत घुमाव हैं। इसलिए, उनमें से एक गलत जवाब देता है।
टूलमेकरसेव

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

0

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

हमेशा की तरह, एक सकारात्मक संख्या एक सीडब्ल्यू अभिविन्यास का प्रतिनिधित्व करती है, जबकि एक नकारात्मक संख्या CCW।


0

यहां ऊपर दिए गए उत्तरों के आधार पर तेजी से 3.0 समाधान दिया गया है:

    for (i, point) in allPoints.enumerated() {
        let nextPoint = i == allPoints.count - 1 ? allPoints[0] : allPoints[i+1]
        signedArea += (point.x * nextPoint.y - nextPoint.x * point.y)
    }

    let clockwise  = signedArea < 0

0

इसके लिए एक और समाधान;

const isClockwise = (vertices=[]) => {
    const len = vertices.length;
    const sum = vertices.map(({x, y}, index) => {
        let nextIndex = index + 1;
        if (nextIndex === len) nextIndex = 0;

        return {
            x1: x,
            x2: vertices[nextIndex].x,
            y1: x,
            y2: vertices[nextIndex].x
        }
    }).map(({ x1, x2, y1, y2}) => ((x2 - x1) * (y1 + y2))).reduce((a, b) => a + b);

    if (sum > -1) return true;
    if (sum < 0) return false;
}

इस तरह एक सरणी के रूप में सभी कोने ले लो;

const vertices = [{x: 5, y: 0}, {x: 6, y: 4}, {x: 4, y: 5}, {x: 1, y: 5}, {x: 1, y: 0}];
isClockwise(vertices);

0

दिशा को निर्धारित करने के लिए R के लिए समाधान और घड़ी की दिशा में उल्टा होने पर (यह ओविन वस्तुओं के लिए आवश्यक पाया गया):

coords <- cbind(x = c(5,6,4,1,1),y = c(0,4,5,5,0))
a <- numeric()
for (i in 1:dim(coords)[1]){
  #print(i)
  q <- i + 1
  if (i == (dim(coords)[1])) q <- 1
  out <- ((coords[q,1]) - (coords[i,1])) * ((coords[q,2]) + (coords[i,2]))
  a[q] <- out
  rm(q,out)
} #end i loop

rm(i)

a <- sum(a) #-ve is anti-clockwise

b <- cbind(x = rev(coords[,1]), y = rev(coords[,2]))

if (a>0) coords <- b #reverses coords if polygon not traced in anti-clockwise direction

0

हालांकि ये उत्तर सही हैं, वे आवश्यकता से अधिक गणितीय रूप से गहन हैं। मान लीजिए कि मानचित्र निर्देशांक है, जहां मानचित्र पर सबसे उत्तर बिंदु उच्चतम बिंदु है। सबसे उत्तर बिंदु खोजें, और यदि 2 बिंदु टाई करते हैं, तो यह सबसे उत्तर है तो सबसे अधिक पूर्व (यह वह बिंदु है जिसे lhf अपने उत्तर में उपयोग करता है)। आपकी बातों में,

बिंदु [0] = (५,०)

बिंदु [१] = (६,४)

बिंदु [२] = (४,५)

बिंदु [३] = (१,५)

बिंदु [४] = (१००)

यदि हम मानते हैं कि P2 सबसे उत्तर में है तो पूर्व बिंदु या तो पिछला या अगला बिंदु दक्षिणावर्त, CW या CCW निर्धारित करता है। चूंकि उत्तर दिशा में सबसे उत्तर बिंदु है, यदि पी 1 (पिछले) से पी 2 चलता है तो दिशा सीडब्ल्यू है। इस मामले में, यह पश्चिम की ओर बढ़ता है, इसलिए दिशा CCW है जैसा कि स्वीकृत उत्तर कहता है। यदि पिछले बिंदु में कोई क्षैतिज गति नहीं है, तो वही प्रणाली अगले बिंदु, P3 पर लागू होती है। यदि पी 3 पी 2 के पश्चिम में है, यह है, तो आंदोलन सीसीडब्ल्यू है। यदि पी 2 से पी 3 आंदोलन पूर्व है, तो यह इस मामले में पश्चिम है, आंदोलन सीडब्ल्यू है। मान लें कि आपके डेटा में nte, P2 सबसे उत्तर की ओर है तो पूर्व बिंदु है और prv आपके डेटा में पिछला बिंदु, P1 है, और nxt अगला बिंदु है, आपके डेटा में P3 है, और [0] क्षैतिज या पूर्व / है पश्चिम जहां पश्चिम पूर्व से कम है, और [1] ऊर्ध्वाधर है।

if (nte[0] >= prv[0] && nxt[0] >= nte[0]) return(CW);
if (nte[0] <= prv[0] && nxt[0] <= nte[0]) return(CCW);
// Okay, it's not easy-peasy, so now, do the math
if (nte[0] * nxt[1] - nte[1] * nxt[0] - prv[0] * (nxt[1] - crt[1]) + prv[1] * (nxt[0] - nte[0]) >= 0) return(CCW); // For quadrant 3 return(CW)
return(CW) // For quadrant 3 return (CCW)

IMHO, यह lhf के उत्तर में दिखाए गए मौलिक गणित से चिपके रहना सुरक्षित होगा - उसका उल्लेख करने के लिए धन्यवाद। इसे क्वाडंटेंट्स में कम करने की चुनौती यह है कि इसकी उचित मात्रा में यह साबित करने के लिए कि आपका फॉर्मूला सभी मामलों में सही है। क्या आपने "अधिक पश्चिम" की सही गणना की? एक अवतल बहुभुज में जहां दोनों [1] और [3] [2] के "पश्चिम और दक्षिण" हैं? क्या आपने उस स्थिति में [1] और [3] की अलग-अलग लंबाई को सही ढंग से संभाला है? मुझे कोई पता नहीं है, जबकि अगर मैं सीधे उस कोण (या उसके निर्धारक) की गणना करता हूं, तो मैं अच्छी तरह से ज्ञात सूत्रों का उपयोग कर रहा हूं।
टूलमेकरसेव

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

यह मेरी चिंता का समाधान नहीं है। वह सूत्र क्या है? क्या यह अभिविन्यास निर्धारक है जैसा कि lhf के उत्तर से विकी लिंक में दिया गया है? अगर ऐसा है, तो कहिए। बताएं कि आप जो कर रहे हैं, वह त्वरित जांच कर रहा है जो मानक गणित से बचने के लिए, अधिकांश मामलों को संभालता है। अगर ऐसा है, तो आपका जवाब अब मेरे लिए मायने रखता है। (माइनर निट: पढ़ने में आसान हो सकता है अगर आप का इस्तेमाल किया .xऔर .yएक struct के बजाय [0]और [1]मैं नहीं जानता था कि क्या अपने कोड कह रहे थे, पहली बार मैं इस पर नजर।।)
ToolmakerSteve

चूँकि मुझे आपके दृष्टिकोण पर विश्वास नहीं था, इसलिए मैंने lhf के दृष्टिकोण को लागू किया ; उसके लिंक से सूत्र। धीरे हिस्सा है खोजने उचित शिखर - हे (एन) खोज। एक बार पाए जाने के बाद, निर्धारक एक O (1) ऑपरेशन है, जिसमें 5 जोड़ के साथ 6 गुणकों का उपयोग किया जाता है। वह अंतिम भाग वह है जिसे आपने अनुकूलित किया है; लेकिन आपने अतिरिक्त इफ़-टेस्ट जोड़कर ऐसा किया है। मैं व्यक्तिगत रूप से एक गैर-मानक दृष्टिकोण लेने का औचित्य नहीं कर सकता - प्रत्येक चरण को सही करने के लिए सत्यापित करने की आवश्यकता होगी - लेकिन क्वाडरंट के एक दिलचस्प विश्लेषण के लिए धन्यवाद!
टूलमेकर

0

Lhf के उत्तर को लागू करने के लिए C # कोड :

// https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
public static WindingOrder DetermineWindingOrder(IList<Vector2> vertices)
{
    int nVerts = vertices.Count;
    // If vertices duplicates first as last to represent closed polygon,
    // skip last.
    Vector2 lastV = vertices[nVerts - 1];
    if (lastV.Equals(vertices[0]))
        nVerts -= 1;
    int iMinVertex = FindCornerVertex(vertices);
    // Orientation matrix:
    //     [ 1  xa  ya ]
    // O = | 1  xb  yb |
    //     [ 1  xc  yc ]
    Vector2 a = vertices[WrapAt(iMinVertex - 1, nVerts)];
    Vector2 b = vertices[iMinVertex];
    Vector2 c = vertices[WrapAt(iMinVertex + 1, nVerts)];
    // determinant(O) = (xb*yc + xa*yb + ya*xc) - (ya*xb + yb*xc + xa*yc)
    double detOrient = (b.X * c.Y + a.X * b.Y + a.Y * c.X) - (a.Y * b.X + b.Y * c.X + a.X * c.Y);

    // TBD: check for "==0", in which case is not defined?
    // Can that happen?  Do we need to check other vertices / eliminate duplicate vertices?
    WindingOrder result = detOrient > 0
            ? WindingOrder.Clockwise
            : WindingOrder.CounterClockwise;
    return result;
}

public enum WindingOrder
{
    Clockwise,
    CounterClockwise
}

// Find vertex along one edge of bounding box.
// In this case, we find smallest y; in case of tie also smallest x.
private static int FindCornerVertex(IList<Vector2> vertices)
{
    int iMinVertex = -1;
    float minY = float.MaxValue;
    float minXAtMinY = float.MaxValue;
    for (int i = 0; i < vertices.Count; i++)
    {
        Vector2 vert = vertices[i];
        float y = vert.Y;
        if (y > minY)
            continue;
        if (y == minY)
            if (vert.X >= minXAtMinY)
                continue;

        // Minimum so far.
        iMinVertex = i;
        minY = y;
        minXAtMinY = vert.X;
    }

    return iMinVertex;
}

// Return value in (0..n-1).
// Works for i in (-n..+infinity).
// If need to allow more negative values, need more complex formula.
private static int WrapAt(int i, int n)
{
    // "+n": Moves (-n..) up to (0..).
    return (i + n) % n;
}

1
यह डाउन-पॉजिटिव Y निर्देशांक के लिए प्रतीत होता है। मानक निर्देशांक के लिए CW / CCW को पलटें।
वार्विक एलीसन

0

इस उत्तर के आधार पर एक साधारण पायथन 3 कार्यान्वयन है (जो बदले में, स्वीकृत उत्तर में प्रस्तावित समाधान पर आधारित है )

def is_clockwise(points):
    # points is your list (or array) of 2d points.
    assert len(points) > 0
    s = 0.0
    for p1, p2 in zip(points, points[1:] + [points[0]]):
        s += (p2[0] - p1[0]) * (p2[1] + p1[1])
    return s > 0.0

-4

इन बिंदुओं के द्रव्यमान का केंद्र ज्ञात कीजिए।

मान लीजिए कि इस बिंदु से आपके बिंदुओं तक लाइनें हैं।

लाइन 0 लाइन 1 के लिए दो लाइनों के बीच का कोण ज्ञात करें

यह लाइन 1 और लाइन 2 के लिए करते हैं

...

...

अगर यह कोण वामावर्त की तुलना में नीरस रूप से बढ़ रहा है,

अन्यथा यदि नीरस रूप से घटता है तो यह दक्षिणावर्त है

और (यह नीरस नहीं है)

आप निर्णय नहीं कर सकते, इसलिए यह बुद्धिमान नहीं है


"द्रव्यमान के केंद्र" से मुझे लगता है कि आपका मतलब "सेंट्रोइड" है?
विक्की चिजवानी

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