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


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.