จะตรวจสอบได้อย่างไรว่ารายการจุดรูปหลายเหลี่ยมเรียงตามเข็มนาฬิกาหรือไม่


259

มีรายการคะแนนฉันจะค้นหาว่าพวกเขาอยู่ในลำดับตามเข็มนาฬิกาได้อย่างไร

ตัวอย่างเช่น:

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

จะบอกว่ามันเป็นทวนเข็มนาฬิกา (หรือทวนเข็มนาฬิกาสำหรับบางคน)


4
โปรดทราบ: คำตอบที่ได้รับการยอมรับและคำตอบมากมายหลังจากนั้นจำเป็นต้องมีการเพิ่มเติมและการคูณหลายครั้ง (คำตอบนั้นขึ้นอยู่กับการคำนวณพื้นที่ที่สิ้นสุดเชิงลบหรือบวกเช่น "สูตรเชือกผูกรองเท้า") ก่อนที่จะดำเนินการอย่างใดอย่างหนึ่งของผู้ที่พิจารณาคำตอบ LHF ของซึ่งเป็นที่เรียบง่าย / รวดเร็ว - ขึ้นอยู่กับวิกิพีเดีย - การวางแนวของรูปหลายเหลี่ยมที่เรียบง่าย
ToolmakerSteve

ฉันคิดถึงมันเสมอในแง่ของผลคูณของเวกเตอร์สองตัวที่อยู่ติดกัน ถ้าฉันเดินไปรอบ ๆ ขอบเขตของรูปหลายเหลี่ยมหัวของฉันก็จะชี้ออกมาจากระนาบ ฉันข้ามเวกเตอร์ออกจากเครื่องบินไปเป็นเวกเตอร์ทิศทางการเดินของฉันเพื่อรับทิศทางที่สามในระบบพิกัดของฉัน ถ้าเวกเตอร์นั้นชี้ว่าภายในอยู่ทางซ้ายของฉันมันจะทวนเข็มนาฬิกา หากการตกแต่งภายในอยู่ทางขวามือของฉัน
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 กลับด้าน จากนั้นกฎจะต้องพลิก: หากพื้นที่เป็นลบเส้นโค้งจะเป็นตามเข็มนาฬิกา
LarsH

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 คือต้องใช้ดัชนี Modulo N ( N ≡ 0) สูตรนี้ใช้ได้กับรูปหลายเหลี่ยมปิดเท่านั้น รูปหลายเหลี่ยมไม่มีขอบจินตภาพ
Olivier Jacot-Descombes

4
blog.element84.com/polygon-winding.htmlนี้อธิบายเป็นภาษาอังกฤษอย่างง่ายว่าทำไมโซลูชันนี้จึงใช้งานได้
David Zorychta

49

สินค้าข้ามวัดระดับของการตั้งฉาก-Ness สองเวกเตอร์ ลองจินตนาการว่าขอบรูปหลายเหลี่ยมแต่ละอันของคุณเป็นเวกเตอร์ในระนาบ xy ของพื้นที่ xyz แบบสามมิติ (3-D) จากนั้นครอสโปรดัคของขอบต่อเนื่องสองอันนั้นคือเวกเตอร์ในทิศทาง z, (บวก z- ทิศทางถ้าส่วนที่สองเป็นตามเข็มนาฬิกา, ลบด้วยทิศ z ถ้ามันทวนเข็มนาฬิกา) ขนาดของเวกเตอร์นี้เป็นสัดส่วนกับไซน์ของมุมระหว่างขอบดั้งเดิมสองค่าดังนั้นมันจะไปถึงค่าสูงสุดเมื่อมันตั้งฉากและเรียวออกไปจะหายไปเมื่อขอบมี collinear (ขนาน)

ดังนั้นสำหรับแต่ละจุดยอด (จุด) ของรูปหลายเหลี่ยมให้คำนวณขนาดของผลิตภัณฑ์ข้ามของขอบที่อยู่ติดกันสองอัน:

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อยู่ระหว่างและ point4point0

Vertex A ( point0) อยู่ระหว่าง
edgeE[จาก point4ถึงpoint0]
edgeA[จาก point0ถึง `จุด 1 '

ขอบทั้งสองนี้เป็นเวกเตอร์ซึ่งพิกัด x และ y สามารถกำหนดได้โดยการลบพิกัดของจุดเริ่มต้นและจุดสิ้นสุด:

edgeE= point0- point4= (1, 0) - (5, 0)= (-4, 0) และ
edgeA= point1- point0= (6, 4) - (1, 0)= (5, 4) และ

และสินค้าข้ามของทั้งสองขอบที่อยู่ติดกันจะคำนวณโดยใช้ปัจจัยของเมทริกซ์ต่อไปนี้ซึ่งถูกสร้างโดยการใส่พิกัดของสองเวกเตอร์ด้านล่างสัญลักษณ์ที่เป็นตัวแทนของสามประสานงานแกน ( i, jและk) พิกัดที่สาม (ศูนย์) ที่มีค่าอยู่ที่นั่นเพราะแนวคิดผลิตภัณฑ์ Cross เป็นโครงสร้างสามมิติดังนั้นเราจึงขยายเวกเตอร์ 2 มิติเหล่านี้เป็น 3 มิติเพื่อใช้ผลิตภัณฑ์ข้าม:

 i    j    k 
-4    0    0
 1    4    0    

เนื่องจาก cross-product ทั้งหมดสร้างเวกเตอร์ตั้งฉากกับระนาบของเวกเตอร์สองตัวที่ถูกคูณตัวกำหนดของเมทริกซ์ด้านบนมีเพียงkส่วนประกอบ, (หรือแกน z)
สูตรการคำนวณขนาดของkส่วนประกอบหรือแกน z คือ
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
ที่จริงแล้วโซลูชันนี้เป็นโซลูชันที่แตกต่างจากโซลูชันที่ยอมรับ ไม่ว่าพวกเขาจะเท่ากันหรือไม่เป็นคำถามที่ฉันกำลังตรวจสอบ แต่ฉันสงสัยว่าพวกเขาไม่ใช่ ... คำตอบที่ได้รับการยอมรับจะคำนวณพื้นที่ของรูปหลายเหลี่ยมโดยคำนึงถึงความแตกต่างระหว่างพื้นที่ใต้ขอบด้านบนของรูปหลายเหลี่ยมและพื้นที่ใต้ ขอบด้านล่างของรูปหลายเหลี่ยม อันหนึ่งจะเป็นค่าลบ (อันที่คุณเดินทางจากซ้ายไปขวา) และอีกอันจะเป็นลบ เมื่อเคลื่อนที่ตามเข็มนาฬิกาขอบบนจะเคลื่อนที่จากซ้ายไปขวาและใหญ่ขึ้นดังนั้นผลรวมจึงเป็นบวก
Charles Bretana

1
โซลูชันของฉันวัดผลรวมของไซน์ของการเปลี่ยนแปลงในมุมขอบของแต่ละจุดยอด สิ่งนี้จะเป็นผลบวกเมื่อเคลื่อนที่ตามเข็มนาฬิกาและลบเมื่อเคลื่อนที่ทวนเข็มนาฬิกา
Charles Bretana

2
ดูเหมือนว่าด้วยวิธีนี้คุณจะต้องใช้เวลา arcsin เว้นแต่คุณสมมตินูน (ในกรณีที่คุณจะต้องตรวจสอบจุดสุดยอด)
agentp

2
คุณจำเป็นต้องใช้อาร์ซิน ลองใช้รูปหลายเหลี่ยมแบบไม่นูนและคุณจะพบว่าการทดสอบจะล้มเหลวสำหรับรูปหลายเหลี่ยมถ้าคุณไม่ใช้อาร์คซิน
ลุคฮัทชิสัน

1
@CharlesBretana - ในขณะที่ฉันยังไม่ได้ทำการทดสอบของลุคฉันเชื่อว่าเขาถูกต้อง นั่นคือธรรมชาติของการสรุปรวมกับระดับที่ไม่เชิงเส้น [โดยไม่ต้องอาร์ซีซินกับอาร์ซีซิน] พิจารณาสิ่งที่มาร์แชลแนะนำว่าคุณปฏิเสธอย่างถูกต้อง เขาแนะนำว่าคุณ "แค่นับ" และคุณชี้ให้เห็นว่าหยิบของค่าขนาดใหญ่จำนวนหนึ่งอาจมีค่ามากกว่าค่าเล็ก ๆ จำนวนมาก ทีนี้ลองพิจารณา arcsin ของแต่ละค่าเทียบกับไม่ใช่ มันยังคงเป็นกรณีที่ไม่สามารถใช้อาร์ซิซินให้น้ำหนักที่ไม่ถูกต้องกับแต่ละค่าดังนั้นจึงมีข้อบกพร่องเดียวกัน (แม้ว่าจะน้อยกว่ามาก)?
ToolmakerSteve

47

ฉันเดาว่านี่เป็นคำถามที่ค่อนข้างเก่า แต่ฉันจะทิ้งโซลูชันอื่นต่อไปเพราะมันตรงไปตรงมาและไม่เข้มข้นทางคณิตศาสตร์ - มันใช้พีชคณิตพื้นฐาน คำนวณพื้นที่ที่ลงนามของรูปหลายเหลี่ยม ถ้ามันเป็นลบคะแนนจะเรียงตามเข็มนาฬิกาถ้ามันเป็นบวกมันจะทวนเข็มนาฬิกา (ซึ่งคล้ายกับโซลูชันของ Beta มาก)

คำนวณพื้นที่ที่ลงนาม: 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 อย่างน้อย) ในอีกทางหนึ่งเป็นเวลาสิบปีแล้วที่ฉันเรียนวิชาพีชคณิตเชิงเส้น
Michael Eric Oberlin

Nope หากคุณตรวจสอบแหล่งที่มาคุณจะเห็นว่าสูตรทำการอ้างอิงจุดแรกอีกครั้งในเทอมสุดท้าย (y1 และ x1) (ขออภัยฉันไม่คุ้นเคยกับ LaTeX มากนัก แต่ฉันได้จัดรูปแบบตัวห้อยเพื่อให้สามารถอ่านได้ง่ายขึ้น)
Sean the Bean

ฉันใช้โซลูชันนี้และทำงานได้อย่างสมบูรณ์แบบสำหรับการใช้งานของฉัน โปรดทราบว่าหากคุณสามารถวางแผนล่วงหน้าและสำรองและเพิ่มเวกเตอร์สองตัวในอาร์เรย์ของคุณคุณสามารถกำจัดการเปรียบเทียบ (หรือ%) โดยการเพิ่มเวกเตอร์แรกที่ส่วนท้ายของอาร์เรย์ ด้วยวิธีนี้คุณจะวนซ้ำองค์ประกอบทั้งหมดยกเว้นองค์ประกอบสุดท้าย (ความยาว -2 แทนความยาว -1)
Eric Fortier

2
@EricFortier - FWIW แทนที่จะปรับขนาดอาร์เรย์ที่มีขนาดใหญ่อาจเป็นทางเลือกที่มีประสิทธิภาพสำหรับแต่ละการวนซ้ำเพื่อบันทึกจุดpreviousPointสำหรับการวนซ้ำถัดไป ก่อนเริ่มวนรอบให้ตั้งค่าpreviousPointเป็นจุดสุดท้ายของอาร์เรย์ การแลกเปลี่ยนเป็นการคัดลอกตัวแปรพิเศษในท้องถิ่น แต่มีการเข้าถึงอาเรย์น้อยกว่า และที่สำคัญไม่จำเป็นต้องสัมผัสแถวลำดับอินพุต
ToolmakerSteve

2
@MichaelEricOberlin - จำเป็นต้องปิดรูปหลายเหลี่ยมโดยการรวมส่วนของเส้นจากจุดสุดท้ายถึงจุดแรก (การคำนวณที่ถูกต้องจะเหมือนกันไม่ว่าจุดเริ่มต้นของรูปหลายเหลี่ยมที่ปิด)
ToolmakerSteve

36

ค้นหาจุดสุดยอดด้วย y ที่เล็กที่สุด (และ x ที่ใหญ่ที่สุดหากมีความสัมพันธ์) ให้จุดสุดยอดเป็นAและจุดสุดยอดก่อนหน้านี้ในรายการเป็นและจุดสุดยอดต่อไปในรายการเป็นB Cตอนนี้คำนวณเครื่องหมายของผลิตภัณฑ์ข้ามและABAC


อ้างอิง:


7
นอกจากนี้ยังมีการอธิบายในen.wikipedia.org/wiki/Curve_orientation ประเด็นก็คือจุดที่พบนั้นจะต้องอยู่บนเปลือกนูนและจำเป็นต้องดูเฉพาะจุดเดียวบนเปลือกนูน (และเพื่อนบ้านใกล้เคียง) เพื่อกำหนดทิศทางของรูปหลายเหลี่ยมทั้งหมด
M Katz

1
ตกตะลึงและกลัวว่าสิ่งนี้จะไม่ได้รับ upvotes เพิ่มเติม สำหรับรูปหลายเหลี่ยมอย่างง่าย ( ซึ่งเป็นรูปหลายเหลี่ยมส่วนใหญ่ในบางสาขา ) คำตอบนี้ให้O(1)วิธีการแก้ปัญหา คำตอบอื่น ๆ ให้ผลO(n)เฉลยสำหรับnจำนวนจุดรูปหลายเหลี่ยม สำหรับการเพิ่มประสิทธิภาพที่ลึกซึ้งยิ่งขึ้นโปรดดูหัวข้อการพิจารณาในทางปฏิบัติของบทความปฐมนิเทศ Curve ที่ยอดเยี่ยมของ Wikipedia
เซซิลแกงกะหรี่

8
การทำให้กระจ่าง:การแก้ปัญหานี้ก็O(1)ต่อเมื่อ (A)รูปหลายเหลี่ยมนี้นูน (ในกรณีที่จุดสุดยอดใด ๆ อยู่บนเปลือกนูนและพอเพียง)หรือ (B)คุณรู้จุดยอดที่มีพิกัด Y น้อยที่สุดแล้ว หากไม่ใช่กรณีนี้ (เช่นรูปหลายเหลี่ยมนี้ไม่นูนและคุณไม่รู้อะไรเลย)O(n)จำเป็นต้องค้นหา เนื่องจากไม่จำเป็นต้องมีการรวมตัว แต่ก็ยังเร็วกว่าโซลูชันอื่น ๆ สำหรับรูปหลายเหลี่ยมอย่างง่าย
เซซิลแกงกะหรี่


1
@CecilCurry ฉันคิดว่าความคิดเห็นที่ 2 ของคุณอธิบายว่าทำไมสิ่งนี้ถึงไม่ได้รับการโหวตเพิ่มอีก มันให้คำตอบที่ผิดในบางสถานการณ์โดยไม่มีการกล่าวถึงข้อ จำกัด เหล่านั้น
LarsH

23

นี่คือการใช้ C # อย่างง่ายของอัลกอริทึมตามคำตอบนี้

สมมติว่าเรามีVectorประเภทที่มีXและคุณสมบัติของประเภทYdouble

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;
}

%เป็นตัวดำเนินการโมดูโลหรือส่วนที่เหลือที่ดำเนินการโมดูโลซึ่ง ( ตาม Wikipedia ) พบส่วนที่เหลือหลังจากการหารหมายเลขหนึ่งด้วยอีกหมายเลขหนึ่ง


6

เริ่มที่หนึ่งในจุดยอดและคำนวณมุมที่ถูกหารด้วยแต่ละด้าน

ครั้งแรกและครั้งสุดท้ายจะเป็นศูนย์ (ดังนั้นข้ามเหล่านั้น); สำหรับส่วนที่เหลือไซน์ของมุมจะถูกกำหนดโดยผลคูณไขว้ของการทำมาตรฐานให้เป็นความยาวหน่วยของ (จุด [n] - จุด [0]) และ (จุด [n-1] - จุด [0]

หากผลรวมของค่าเป็นบวกรูปหลายเหลี่ยมของคุณจะถูกวาดในลักษณะทวนเข็มนาฬิกา


เมื่อมองว่าผลิตภัณฑ์กากบาทโดยทั่วไปเดือดลงไปถึงค่าสเกลบวกเป็นอย่างไรคูณไซน์ของมุมก็น่าจะดีกว่าที่จะทำผลิตภัณฑ์ครอส มันจะเร็วขึ้นและซับซ้อนน้อยลง
ReaperUnreal

4

สำหรับสิ่งที่คุ้มค่าฉันใช้ mixin นี้เพื่อคำนวณลำดับการไขลานสำหรับแอป Google Maps API v3

รหัสใช้ประโยชน์จากผลข้างเคียงของพื้นที่รูปหลายเหลี่ยม: ลำดับการหมุนตามเข็มนาฬิกาของจุดยอดให้ผลเป็นพื้นที่บวกในขณะที่ลำดับการหมุนทวนเข็มนาฬิกาของจุดสุดยอดเดียวกันสร้างพื้นที่เดียวกันเป็นค่าลบ รหัสนี้ยังใช้ API ส่วนตัวในห้องสมุดเรขาคณิตของ Google Maps ฉันรู้สึกสบายใจที่จะใช้มัน - ใช้โดยยอมรับความเสี่ยงของคุณเอง

ตัวอย่างการใช้งาน:

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;
  }
})();

เมื่อลองทำสิ่งนี้ฉันจะได้ผลลัพธ์ตรงข้ามกันรูปหลายเหลี่ยมที่ถูกวาดตามเข็มนาฬิกาจะให้พื้นที่เป็นลบ ไม่ว่าในกรณีใดข้อมูลนี้ยังคงมีประโยชน์มากใน 5 ปีขอบคุณ
Cameron Roberts

@CameronRoberts มาตรฐาน (ดู IETF โดยเฉพาะอย่างยิ่งสำหรับ geoJson) คือการปฏิบัติตาม 'กฎมือขวา' ฉันเดาว่า Google กำลังบ่นอยู่ ในกรณีนั้นวงแหวนรอบนอกจะต้องทวนเข็มนาฬิกา (ให้พื้นที่เป็นบวก) และวงแหวนด้านใน (หลุม) จะหมุนวนตามเข็มนาฬิกา (พื้นที่เชิงลบจะถูกลบออกจากพื้นที่หลัก)
allez l'OM

4

การใช้งานคำตอบของฌอนใน JavaScript:

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 เป็นไลบรารีการจัดการแผนที่ที่ใช้จาวาสคริปต์เช่น googlemaps และมันถูกเขียนและใช้ใน openlayers 2
MSS

คุณช่วยอธิบายนิดหน่อยว่าโค้ดของคุณทำอะไรและทำไมคุณถึงทำมัน?
nbro

@nbro รหัสนี้ใช้คำตอบ LHF มันง่ายที่จะเก็บส่วนที่ไม่ใช่ OpenLayer ไว้ในฟังก์ชั่นจาวาสคริปต์โดยให้จุดยอดเป็นพารามิเตอร์โดยตรง มันทำงานได้ดีและสามารถนำไปปรับใช้กับกรณีของmultiPolygon
allez l'OM


1

ขณะที่ยังอธิบายในเรื่องนี้วิกิพีเดียบทความปฐมนิเทศ Curveได้รับ 3 คะแนนp, qและrบนเครื่องบิน (คือมีพิกัด x และ y) คุณสามารถคำนวณสัญญาณของปัจจัยดังต่อไปนี้

ป้อนคำอธิบายรูปภาพที่นี่

หากดีเทอร์มีแนนต์เป็นลบ (เช่นOrient(p, q, r) < 0) ดังนั้นรูปหลายเหลี่ยมนั้นจะถูกปรับตามเข็มนาฬิกา (CW) ถ้าดีเทอร์มีแนนต์เป็นบวก (เช่นOrient(p, q, r) > 0) รูปหลายเหลี่ยมนั้นจะถูกทวนเข็มนาฬิกา (CCW) ปัจจัยที่เป็นศูนย์ (เช่นOrient(p, q, r) == 0) ถ้าจุดp, qและrมีcollinear collinear

ในสูตรข้างต้นเราย่อหน้าคนในด้านหน้าของพิกัดของp, q และrเพราะเราจะใช้เหมือนกันพิกัด


@ ทิเบตคุณช่วยอธิบายได้ไหมว่าทำไมวิธีการนี้ถึงใช้ไม่ได้ในหลาย ๆ สถานการณ์ถ้ารูปหลายเหลี่ยมเว้า?
nbro

1
โปรดดูตารางสุดท้ายในการอ้างอิงรายการ wiki ในโพสต์ของคุณ มันง่ายสำหรับฉันที่จะยกตัวอย่างผิด ๆ แต่ก็ยากที่จะพิสูจน์
ชาวทิเบต

1
โปรดดูตารางสุดท้ายในการอ้างอิงรายการ wiki ในโพสต์ของคุณ มันง่ายสำหรับฉันที่จะยกตัวอย่างผิด ๆ แต่ก็ยากที่จะพิสูจน์
ชาวทิเบต

1
@ ทิเบตถูกต้อง คุณไม่สามารถนำสามจุดไปตามรูปหลายเหลี่ยมได้ คุณอาจอยู่ในบริเวณนูนหรือเว้าของรูปหลายเหลี่ยมนั้น อ่านวิกิพีเดียอย่างระมัดระวังหนึ่งต้องใช้เวลาสามจุดพร้อมเปลือกนูนที่ล้อมรอบรูปหลายเหลี่ยม จาก "ข้อควรพิจารณาเชิงปฏิบัติ": "เราไม่จำเป็นต้องสร้างตัวนูนของรูปหลายเหลี่ยมเพื่อหาจุดยอดที่เหมาะสมตัวเลือกทั่วไปคือจุดยอดของรูปหลายเหลี่ยมที่มีพิกัด X น้อยที่สุดหากมีหลายตัว เมื่อเลือกพิกัด Y ที่เล็กที่สุดจะรับประกันว่าจะเป็น [a] จุดยอดของลำตัวนูนของรูปหลายเหลี่ยม "
ToolmakerSteve

1
ดังนั้นคำตอบก่อนหน้าของ lhfซึ่งคล้ายกันและอ้างอิงบทความ wiki เดียวกัน แต่ระบุจุดดังกล่าว [เห็นได้ชัดว่ามันไม่สำคัญว่าใครจะเล็กหรือใหญ่ที่สุด x หรือ y ตราบใดที่คน ๆ หนึ่งหลีกเลี่ยงการอยู่ตรงกลาง ได้อย่างมีประสิทธิภาพหนึ่งทำงานจากขอบด้านหนึ่งของกล่องล้อมรอบรูปหลายเหลี่ยมเพื่อรับประกันในภูมิภาคเว้า]
ToolmakerSteve

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
คุณสามารถระบุคำตอบอื่น ๆ ที่ตรงกับคำตอบนี้ได้หรือไม่
nbro

0

วิธีการคำนวณที่ง่ายกว่ามากถ้าคุณรู้จุดภายในรูปหลายเหลี่ยม :

  1. เลือกส่วนของเส้นใดก็ได้จากรูปหลายเหลี่ยมจุดและพิกัดในลำดับนั้น

  2. เพิ่มจุด "ข้างใน" ที่รู้จักและสร้างรูปสามเหลี่ยม

  3. คำนวณ CW หรือ CCW ตามที่แนะนำที่นี่พร้อมทั้งสามคะแนน


อาจใช้งานได้หากรูปหลายเหลี่ยมนูนออกมาทั้งหมด แน่นอนว่ามันไม่น่าเชื่อถือหากมีพื้นที่เว้าใด ๆ - มันง่ายต่อการเลือกจุดที่อยู่ใน "ผิด" ด้านหนึ่งของขอบถ้ำจากนั้นเชื่อมต่อกับขอบนั้น จะได้คำตอบที่ผิด
ToolmakerSteve

มันใช้งานได้แม้ว่ารูปหลายเหลี่ยมจะเว้า จุดนั้นต้องอยู่ภายในรูปหลายเหลี่ยมเว้า อย่างไรก็ตามฉันไม่แน่ใจเกี่ยวกับรูปหลายเหลี่ยมที่ซับซ้อน (ไม่ได้ทดสอบ)
Venkata Goli

"ใช้งานได้แม้ว่ารูปหลายเหลี่ยมจะเว้า" - ตัวอย่างตัวอย่าง: โพลี (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) สิ่งเหล่านี้เป็นทั้งการตกแต่งภายในให้กับรูปหลายเหลี่ยมดั้งเดิม แต่ยังมีขดลวดที่ตรงกันข้าม ดังนั้นหนึ่งในนั้นให้คำตอบที่ผิด
ToolmakerSteve

โดยทั่วไปหากรูปหลายเหลี่ยมมีพื้นที่เว้าให้เลือกส่วนในพื้นที่เว้า เนื่องจากเป็นเว้าคุณสามารถค้นหาจุด "ภายใน" สองจุดที่อยู่ฝั่งตรงข้ามของเส้นนั้น เนื่องจากพวกมันอยู่ตรงข้ามกับเส้นนั้นรูปสามเหลี่ยมที่เกิดขึ้นจึงมีขดลวดที่ตรงกันข้าม สิ้นสุดการพิสูจน์
ToolmakerSteve

0

หลังจากทดสอบการใช้งานที่ไม่น่าเชื่อถือหลายอย่างอัลกอริทึมที่ให้ผลลัพธ์ที่น่าพอใจเกี่ยวกับการวางแนว CW / CCW ออกมาจากกล่องเป็นสิ่งที่ถูกโพสต์โดย OP ในหัวข้อนี้ (shoelace_formula_3 )

เช่นเคยตัวเลขบวกหมายถึงการวางแนว CW ในขณะที่จำนวนลบ CCW


0

นี่คือวิธีแก้ปัญหา swift 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 เพื่อกำหนดทิศทางและย้อนกลับถ้าตามเข็มนาฬิกา (พบว่าจำเป็นสำหรับวัตถุแบบ Owin):

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] = (5,0)

จุด [1] = (6,4)

จุด [2] = (4,5)

จุด [3] = (1,5)

จุด [4] = (1,0)

หากเราสมมติว่า P2 เป็นจุดเหนือสุดและทิศตะวันออกมากที่สุดจุดก่อนหน้าหรือถัดไปจะกำหนดตามเข็มนาฬิกา CW หรือ CCW เนื่องจากจุดเหนือสุดอยู่บนทิศเหนือหาก P1 (ก่อนหน้า) ถึง P2 เคลื่อนที่ไปทางทิศตะวันออกทิศทางคือ CW ในกรณีนี้มันเคลื่อนไปทางตะวันตกดังนั้นทิศทางคือทวนเข็มนาฬิกาตามคำตอบที่ยอมรับ หากจุดก่อนหน้าไม่มีการเคลื่อนที่ในแนวนอนระบบเดียวกันจะใช้กับจุดถัดไป P3 ถ้า P3 เป็นทิศตะวันตกของ P2 แสดงว่าการเคลื่อนที่นั้นเป็น CCW หากการเคลื่อนไหว P2 ถึง P3 เป็นทิศตะวันออกก็คือทิศตะวันตกในกรณีนี้การเคลื่อนไหวคือ CW สมมติว่า 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

@ToolmakerSteve คำสั่ง if ทำงานเสมอถ้า 3 คะแนนนั้นนูน ถ้างบจะกลับมาแล้วคุณจะได้รับคำตอบที่ถูกต้อง ถ้างบจะไม่กลับมาถ้ารูปร่างเว้าและมาก นั่นคือเมื่อคุณต้องทำคณิตศาสตร์ ภาพส่วนใหญ่มีควอดเรนเดียวดังนั้นส่วนนั้นจึงเป็นเรื่องง่าย การเรียกรูทีนย่อยของฉันมากกว่า 99% ได้รับการจัดการโดยคำสั่ง if
VectorVortec

นั่นไม่ได้อยู่ที่ความกังวลของฉัน สูตรนั้นคืออะไร? มันเป็นตัวกำหนดทิศทางตามที่กำหนดในลิงก์ wiki จากคำตอบของ lhf หรือไม่? ถ้าเป็นเช่นนั้นพูดอย่างนั้น อธิบายว่าสิ่งที่คุณกำลังทำคือทำการตรวจสอบอย่างรวดเร็วที่จัดการกับกรณีส่วนใหญ่เพื่อหลีกเลี่ยงคณิตศาสตร์มาตรฐาน ถ้าเป็นเช่นนั้นคำตอบของคุณตอนนี้ทำให้รู้สึกถึงฉัน (ผู้เยาว์เล็กน้อย: จะอ่านง่ายกว่าถ้าคุณใช้.xและ.yโครงสร้างแทนที่จะเป็น[0]และ[1]ฉันไม่รู้ว่ารหัสของคุณพูดว่าอะไรเป็นครั้งแรกที่ฉันมองไปที่มัน)
ToolmakerSteve

เนื่องจากผมไม่ได้มีความเชื่อมั่นในแนวทางของคุณผมดำเนินการวิธีการ LHF ของ ; สูตรจากลิงค์ของเขา ส่วนที่ช้าคือการค้นหาจุดสุดยอดที่เหมาะสม - การค้นหา O (N) เมื่อพบว่าดีเทอร์มิแนนต์คือการดำเนินการ O (1) โดยใช้ 6 คูณกับ 5 เพิ่ม ส่วนสุดท้ายคือสิ่งที่คุณได้รับการปรับให้เหมาะสม แต่คุณทำได้โดยการเพิ่มการทดสอบเพิ่มเติม ฉันเองไม่สามารถพิสูจน์ได้ว่าวิธีการที่ไม่ได้มาตรฐานนั้นจะต้องยืนยันว่าแต่ละขั้นตอนนั้นถูกต้อง - แต่ขอขอบคุณสำหรับการวิเคราะห์ที่น่าสนใจของควอดเรเตอร์!
ToolmakerSteve

0

รหัส C # เพื่อใช้คำตอบของ lhf :

// 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 สำหรับพิกัดมาตรฐาน
Warwick Allison

0

นี่คือการใช้งาน Python 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

หาจุดศูนย์กลางมวลของจุดเหล่านี้

สมมติว่ามีเส้นจากจุดนี้ไปยังจุดของคุณ

หามุมระหว่างสองบรรทัดสำหรับ line0 line1

กว่าทำเพื่อ line1 และ line2

...

...

ถ้ามุมนี้เพิ่มขึ้น monotonically มากกว่าทวนเข็มนาฬิกา

มิฉะนั้นถ้าลดความซ้ำซากมันเป็นตามเข็มนาฬิกา

อื่น (ไม่ใช่ความซ้ำซาก)

คุณไม่สามารถตัดสินใจได้ดังนั้นจึงไม่ฉลาด


โดย "center of mass" ฉันคิดว่าคุณหมายถึง "centroid"?
Vicky Chijwani

อาจใช้งานได้หากรูปหลายเหลี่ยมนูนออกมาทั้งหมด แต่ควรใช้คำตอบที่เหมาะกับรูปหลายเหลี่ยมที่ไม่นูน
ToolmakerSteve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.