พื้นที่ของรูปหลายเหลี่ยมที่ตัดด้วยตนเอง


32

พิจารณารูปหลายเหลี่ยมตัดกันตนเองที่อาจกำหนดโดยรายการจุดยอดในพื้นที่ 2D เช่น

{{0, 0}, {5, 0}, {5, 4}, {1, 4}, {1, 2}, {3, 2}, {3, 3}, {2, 3}, {2, 1}, {4, 1}, {4, 5}, {0, 5}}

มีหลายวิธีในการกำหนดพื้นที่ของรูปหลายเหลี่ยมดังกล่าว แต่สิ่งที่น่าสนใจที่สุดคือกฎที่เป็นคู่ จับจุดใด ๆ บนระนาบลากเส้นจากจุดที่ชี้ไปยังระยะอนันต์ หากบรรทัดนั้นข้ามรูปหลายเหลี่ยมด้วยจำนวนคี่จุดจะเป็นส่วนหนึ่งของพื้นที่ของรูปหลายเหลี่ยมถ้าข้ามรูปหลายเหลี่ยมเป็นจำนวนเท่าตัวจุดนั้นจะไม่เป็นส่วนหนึ่งของรูปหลายเหลี่ยม สำหรับตัวอย่างรูปหลายเหลี่ยมด้านบนนี่คือทั้งโครงร่างและพื้นที่คู่ของมัน:

เค้าโครงพื้นที่

รูปหลายเหลี่ยมโดยทั่วไปจะไม่เป็นมุมฉาก ฉันเลือกตัวอย่างง่ายๆเช่นนี้เพื่อให้นับพื้นที่ได้ง่ายขึ้น

พื้นที่ของตัวอย่างนี้คือ17(ไม่ใช่24หรือ33เป็นคำจำกัดความอื่นหรือพื้นที่อาจให้ผลผลิต)

โปรดทราบว่าภายใต้ข้อกำหนดนี้พื้นที่ของรูปหลายเหลี่ยมนั้นไม่ขึ้นอยู่กับลำดับการพัน

ความท้าทาย

รับรายการของจุดยอดที่มีพิกัดจำนวนเต็มกำหนดรูปหลายเหลี่ยมกำหนดพื้นที่ของมันภายใต้กฎคู่ที่คี่

คุณสามารถเขียนฟังก์ชันหรือโปรแกรมรับอินพุตผ่าน STDIN หรือทางเลือกอื่นใกล้เคียงอาร์กิวเมนต์บรรทัดคำสั่งหรืออาร์กิวเมนต์ฟังก์ชันแล้วส่งคืนผลลัพธ์หรือพิมพ์ไปที่ STDOUT หรือทางเลือกที่ใกล้เคียงที่สุด

คุณสามารถป้อนข้อมูลในรูปแบบรายการหรือรูปแบบสตริงใด ๆ ก็ได้หากยังไม่ได้ทำการประมวลผลล่วงหน้า

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

คุณจะต้องสามารถแก้ปัญหากรณีทดสอบด้านล่างนี้ภายใน 10 วินาทีด้วยเครื่องเดสก์ท็อปที่เหมาะสม (มีกฎบางข้อในระยะนี้ดังนั้นให้ใช้วิจารณญาณที่ดีที่สุดของคุณหากใช้เวลา 20 วินาทีบนแล็ปท็อปของฉันฉันจะให้คุณได้รับข้อสงสัยถ้าใช้เวลาสักครู่ฉันจะไม่) ฉันคิดว่าข้อ จำกัด นี้ ควรเป็นคนใจกว้างมาก แต่ก็ควรที่จะตัดทอนวิธีการที่คุณเพียงแค่แยกรูปหลายเหลี่ยมบนกริดและการนับที่ดีพอหรือใช้แนวทางความน่าจะเป็นเช่น Monte Carlo เป็นนักกีฬาที่ดีและอย่าพยายามเพิ่มประสิทธิภาพของวิธีการเหล่านี้เพื่อให้คุณสามารถทำตามเวลาที่กำหนดได้ ;)

คุณต้องไม่ใช้ฟังก์ชั่นที่มีอยู่ที่เกี่ยวข้องโดยตรงกับรูปหลายเหลี่ยม

นี่คือรหัสกอล์ฟดังนั้นการส่งที่สั้นที่สุด (เป็นไบต์) ชนะ

สมมติฐาน

  • พิกัดทั้งหมดเป็นจำนวนเต็มในช่วง0 ≤ x ≤ 100, 0 ≤ y ≤ 100.
  • จะมีจุดสูงสุดอย่างน้อย3ที่สุด50
  • จะไม่มีจุดยอดซ้ำ ๆ จุดยอดใดจะไม่อยู่บนขอบอีกด้านหนึ่ง ( อาจมีจุด collinear ในรายการ)

กรณีทดสอบ

{{0, 0}, {5, 0}, {5, 4}, {1, 4}, {1, 2}, {3, 2}, {3, 3}, {2, 3}, {2, 1}, {4, 1}, {4, 5}, {0, 5}}
17.0000

{{22, 87}, {6, 3}, {98, 77}, {20, 56}, {96, 52}, {79, 34}, {46, 78}, {52, 73}, {81, 85}, {90, 43}}
2788.39

{{90, 43}, {81, 85}, {52, 73}, {46, 78}, {79, 34}, {96, 52}, {20, 56}, {98, 77}, {6, 3}, {22, 87}}
2788.39

{{70, 33}, {53, 89}, {76, 35}, {14, 56}, {14, 47}, {59, 49}, {12, 32}, {22, 66}, {85, 2}, {2, 81},
 {61, 39}, {1, 49}, {91, 62}, {67, 7}, {19, 55}, {47, 44}, {8, 24}, {46, 18}, {63, 64}, {23, 30}}
2037.98

{{42, 65}, {14, 59}, {97, 10}, {13, 1}, {2, 8}, {88, 80}, {24, 36}, {95, 94}, {18, 9}, {66, 64},
 {91, 5}, {99, 25}, {6, 66}, {48, 55}, {83, 54}, {15, 65}, {10, 60}, {35, 86}, {44, 19}, {48, 43},
 {47, 86}, {29, 5}, {15, 45}, {75, 41}, {9, 9}, {23, 100}, {22, 82}, {34, 21}, {7, 34}, {54, 83}}
3382.46

{{68, 35}, {43, 63}, {66, 98}, {60, 56}, {57, 44}, {90, 52}, {36, 26}, {23, 55}, {66, 1}, {25, 6},
 {84, 65}, {38, 16}, {47, 31}, {44, 90}, {2, 30}, {87, 40}, {19, 51}, {75, 5}, {31, 94}, {85, 56},
 {95, 81}, {79, 80}, {82, 45}, {95, 10}, {27, 15}, {18, 70}, {24, 6}, {12, 73}, {10, 31}, {4, 29},
 {79, 93}, {45, 85}, {12, 10}, {89, 70}, {46, 5}, {56, 67}, {58, 59}, {92, 19}, {83, 49}, {22,77}}
3337.62

{{15, 22}, {71, 65}, {12, 35}, {30, 92}, {12, 92}, {97, 31}, {4, 32}, {39, 43}, {11, 40}, 
 {20, 15}, {71, 100}, {84, 76}, {51, 98}, {35, 94}, {46, 54}, {89, 49}, {28, 35}, {65, 42}, 
 {31, 41}, {48, 34}, {57, 46}, {14, 20}, {45, 28}, {82, 65}, {88, 78}, {55, 30}, {30, 27}, 
 {26, 47}, {51, 93}, {9, 95}, {56, 82}, {86, 56}, {46, 28}, {62, 70}, {98, 10}, {3, 39}, 
 {11, 34}, {17, 64}, {36, 42}, {52, 100}, {38, 11}, {83, 14}, {5, 17}, {72, 70}, {3, 97}, 
 {8, 94}, {64, 60}, {47, 25}, {99, 26}, {99, 69}}
3514.46

1
โดยเฉพาะฉันต้องการแทนที่ตัวคั่นในลักษณะที่ทำให้รายการเป็นเส้นทางผู้ใช้ PostScript ที่ถูกต้องดังนั้นฉันจึงสามารถแยกวิเคราะห์สิ่งทั้งหมดด้วยupathตัวดำเนินการเดียว (อันที่จริงแล้วเป็นการแปลง 1: 1 ที่ง่ายมากระหว่าง seperators }, {เพิ่งจะกลายเป็นlinetoและเครื่องหมายจุลภาคระหว่าง x และ y ถูกลบออกและวงเล็บปีกกาเปิดและปิดจะถูกแทนที่ด้วยส่วนหัวและส่วนท้ายแบบคงที่ ... )
AJMansfield

1
@JMansfield ปกติแล้วฉันจะไม่สนใจการใช้การแสดงรายการแบบเนทีฟที่สะดวก แต่การใช้upathและlinetoฟังดูเหมือนว่าคุณกำลังประมวลผลอินพุตล่วงหน้า คือคุณไม่ได้รับรายการพิกัด แต่เป็นรูปหลายเหลี่ยมจริง
Martin Ender

1
@ Mattoonan โอ้นั่นเป็นจุดที่ดี ใช่คุณอาจคิดว่า
Martin Ender

2
@ เรย์ในขณะที่ทิศทางอาจมีผลต่อจำนวนการข้ามก็จะเพิ่มขึ้นหรือลดลงเพียง 2 โดยรักษาความเท่าเทียมกัน ฉันจะพยายามหาข้อมูลอ้างอิง สำหรับการเริ่มต้นSVGใช้คำนิยามเดียวกัน
Martin Ender

1
Mathematica 12.0 CrossingPolygonมีการสร้างขึ้นใหม่ในฟังก์ชั่นสำหรับการนี้:
alephalpha

คำตอบ:


14

Mathematica, 247 225 222

p=Partition[#,2,1,1]&;{a_,b_}~r~{c_,d_}=Det/@{{a-c,c-d},{a,c}-b}/Det@{a-b,c-d};f=Abs@Tr@MapIndexed[Det@#(-1)^Tr@#2&,p[Join@@MapThread[{1-#,#}&/@#.#2&,{Sort/@Cases[{s_,t_}/;0<=s<=1&&0<=t<=1:>s]/@Outer[r,#,#,1],#}]&@p@#]]/2&

ก่อนอื่นให้เพิ่มจุดตัดของรูปหลายเหลี่ยมลงในรูปหลายเหลี่ยมจากนั้นย้อนกลับขอบบางส่วนจากนั้นจึงสามารถคำนวณพื้นที่ได้เช่นเดียวกับรูปหลายเหลี่ยมอย่างง่าย

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

ตัวอย่าง:

In[2]:= f[{{15, 22}, {71, 65}, {12, 35}, {30, 92}, {12, 92}, {97, 31}, {4, 32}, {39, 43}, {11, 40}, 
 {20, 15}, {71, 100}, {84, 76}, {51, 98}, {35, 94}, {46, 54}, {89, 49}, {28, 35}, {65, 42}, 
 {31, 41}, {48, 34}, {57, 46}, {14, 20}, {45, 28}, {82, 65}, {88, 78}, {55, 30}, {30, 27}, 
 {26, 47}, {51, 93}, {9, 95}, {56, 82}, {86, 56}, {46, 28}, {62, 70}, {98, 10}, {3, 39}, 
 {11, 34}, {17, 64}, {36, 42}, {52, 100}, {38, 11}, {83, 14}, {5, 17}, {72, 70}, {3, 97}, 
 {8, 94}, {64, 60}, {47, 25}, {99, 26}, {99, 69}}]

Out[2]= 3387239559852305316061173112486233884246606945138074528363622677708164\
 6419838924305735780894917246019722157041758816629529815853144003636562\
 9161985438389053702901286180223793349646170997160308182712593965484705\
 3835036745220226127640955614326918918917441670126958689133216326862597\
 0109115619/\
 9638019709367685232385259132839493819254557312303005906194701440047547\
 1858644412915045826470099500628074171987058850811809594585138874868123\
 9385516082170539979030155851141050766098510400285425157652696115518756\
 3100504682294718279622934291498595327654955812053471272558217892957057\
 556160

In[3]:= N[%] (*The numerical value of the last output*)

Out[3]= 3514.46

น่าเสียดายที่ฉันไม่แน่ใจว่าตรรกะนี้จะใช้ได้กับทุกสถานการณ์ คุณลองได้{1,2},{4,4},{4,2},{2,4},{2,1},{5,3}ไหม คุณควรออกมาพร้อมกับ 3.433333333333309 ฉันดูที่การใช้ตรรกะที่คล้ายกัน
MickyT

@MickyT ใช่มันใช้งานได้ มันกลับมาและเป็นค่าตัวเลข103/30 3.43333
alephalpha

ขอโทษสำหรับเรื่องนั้น. ทางออกที่ดี
MickyT

44

Python 2 323 319 ไบต์

exec u"def I(s,a,b=1j):c,d=s;d-=c;c-=a;e=(d*bX;return e*(0<=(b*cX*e<=e*e)and[a+(d*cX*b/e]or[]\nE=lambda p:zip(p,p[1:]+p);S=sorted;P=E(input());print sum((t-b)*(r-l)/2Fl,r@E(S(i.realFa,b@PFe@PFi@I(e,a,b-a)))[:-1]Fb,t@E(S(((i+j)XFe@PFi@I(e,l)Fj@I(e,r)))[::2])".translate({70:u" for ",64:u" in ",88:u".conjugate()).imag"})

ทำรายการของจุดยอดผ่าน STDIN เป็นตัวเลขที่ซับซ้อนในรูปแบบต่อไปนี้

[  X + Yj,  X + Yj,  ...  ]

และเขียนผลลัพธ์ไปที่ STDOUT

รหัสเดียวกันหลังจากการแทนที่สตริงและระยะห่างบางส่วน:

def I(s, a, b = 1j):
    c, d = s; d -= c; c -= a;
    e = (d*b.conjugate()).imag;
    return e * (0 <= (b*c.conjugate()).imag * e <= e*e) and \
           [a + (d*c.conjugate()).imag * b/e] or []

E = lambda p: zip(p, p[1:] + p);
S = sorted;

P = E(input());

print sum(
    (t - b) * (r - l) / 2

    for l, r in E(S(
        i.real for a, b in P for e in P for i in I(e, a, b - a)
    ))[:-1]

    for b, t in E(S(
        ((i + j).conjugate()).imag for e in P for i in I(e, l) for j in I(e, r)
    ))[::2]
)

คำอธิบาย

สำหรับแต่ละจุดที่จุดตัดของสองด้านของรูปหลายเหลี่ยมอินพุต (รวมถึงจุดยอด) ให้ผ่านเส้นแนวตั้งแม้ว่าจุดนั้น

รูปที่ 1

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

นี่คือวิธีที่เราพบกับรูปสี่เหลี่ยมคางหมูเหล่านี้: สำหรับแต่ละคู่ของเส้นแนวตั้งที่ต่อเนื่องกันเราจะพบส่วนของรูปหลายเหลี่ยมแต่ละด้านของรูปหลายเหลี่ยมที่ (เหมาะสม) อยู่ระหว่างสองบรรทัดนี้ (ซึ่งอาจไม่มีอยู่ในบางด้าน) ในภาพประกอบด้านบนนี่คือหกส่วนสีแดงเมื่อพิจารณาเส้นแนวตั้งสีแดงสองเส้น โปรดทราบว่าส่วนเหล่านี้ไม่ได้ตัดกันอย่างถูกต้อง (กล่าวคือพวกเขาอาจพบกันที่จุดสิ้นสุดของพวกเขาตรงกันอย่างสมบูรณ์หรือไม่ตัดกันเลยตั้งแต่อีกครั้งถ้าพวกเขาถูกตัดกันอย่างถูกต้องจะมีเส้นแนวตั้งอีกในระหว่าง;) และมันสมเหตุสมผลแล้วที่จะพูดถึงการเรียงลำดับจากบนลงล่างซึ่งเราทำ ตามกฎที่เป็นเลขคี่เมื่อเราข้ามส่วนแรกเราจะอยู่ในรูปหลายเหลี่ยม เมื่อเราข้ามอันที่สองเราจะออกไป; หนึ่งในสามอีกครั้ง; ที่สี่ออก; และอื่น ๆ ...

โดยรวมนี่เป็นอัลกอริทึมO ( n 3 log n )


4
มันยอดเยี่ยมมาก! ฉันรู้ว่าฉันสามารถไว้ใจคุณสำหรับอันนี้ ;) (คุณอาจต้องการตอบคำถามนี้ใน Stack Overflow)
Martin Ender

@ MartinBüttnerมา ':) :)
Ell

7
ผลงานที่ยอดเยี่ยมและคำอธิบายที่ยอดเยี่ยม
MickyT

1
นี่คือคำตอบที่น่าประทับใจ คุณพัฒนาอัลกอริธึมด้วยตัวเองหรือมีปัญหาในการทำงานนี้อยู่หรือ หากมีงานที่ทำอยู่ฉันจะขอบคุณตัวชี้ที่ฉันสามารถหาได้ ฉันไม่รู้ว่าจะรับมือกับเรื่องนี้อย่างไร
Logic Knight

5
@CarpetPython ฉันพัฒนาตัวเอง แต่ฉันจะแปลกใจมากถ้ามันไม่ได้ทำมาก่อน
Ell

9

Haskell, 549

ดูเหมือนว่าฉันจะเล่นกอล์ฟคันนี้ไม่ไกลพอ แต่แนวคิดแตกต่างจากคำตอบอีกสองคำตอบดังนั้นฉันจึงคิดว่าฉันจะแบ่งปันต่อไป จะดำเนินการเหตุผล O (N ^ 2) เพื่อคำนวณพื้นที่

import Data.List
_%0=2;x%y=x/y
h=sort
z f w@(x:y)=zipWith f(y++[x])w
a=(%2).sum.z(#);(a,b)#(c,d)=b*c-a*d
(r,p)?(s,q)=[(0,p)|p==q]++[(t,v t p r)|u t,u$f r]where f x=(d q p#x)%(r#s);t=f s;u x=x^2<x
v t(x,y)(a,b)=(x+t*a,y+t*b);d=v(-1)
s x=zip(z d x)x
i y=h.(=<<y).(?)=<<y
[]!x=[x];x!_=x
e n(a@(x,p):y)|x>0=(n!y,a):(e(n!y)$tail$dropWhile((/=p).snd)y)|0<1=(n,a):e n y
c[p]k=w x[]where((_,q):x)=e[]p;w((n,y):z)b|q==y=(k,map snd(q:b)):c n(-k)|0<1=w z(y:b);c[]_=[]
b(s,p)=s*a p
u(_,x)(_,y)=h x==h y
f p=abs$sum$map b$nubBy u$take(length p^2)$c[cycle$i$s p]1

ตัวอย่าง:

λ> f test''
33872395598523053160611731124862338842466069451380745283636226777081646419838924305735780894917246019722157041758816629529815853144003636562916198543838905370290128618022379334964617099716030818271259396548470538350367452202261276409556143269189189174416701269586891332163268625970109115619 % 9638019709367685232385259132839493819254557312303005906194701440047547185864441291504582647009950062807417198705885081180959458513887486812393855160821705399790301558511410507660985104002854251576526961155187563100504682294718279622934291498595327654955812053471272558217892957057556160
λ> fromRational (f test'')
3514.4559380388832

ความคิดคือการวนรูปหลายเหลี่ยมใหม่ทุกครั้งที่เกิดการรวมกันของรูปหลายเหลี่ยมโดยไม่มีขอบที่ตัดกัน จากนั้นเราสามารถคำนวณพื้นที่ (ลงนาม) ของรูปหลายเหลี่ยมแต่ละอันโดยใช้สูตรเชือกผูกรองเท้าของเกาส์ ( http://en.wikipedia.org/wiki/Shoelace_formula ) กฎเลขคี่นั้นเรียกร้องว่าเมื่อมีการข้ามการแปลงพื้นที่ของรูปหลายเหลี่ยมใหม่จะถูกนับในเชิงลบเมื่อเทียบกับรูปหลายเหลี่ยมเก่า

ตัวอย่างเช่นพิจารณารูปหลายเหลี่ยมในคำถามเดิม การข้ามที่มุมบนซ้ายจะถูกแปลงเป็นสองเส้นทางซึ่งพบกันที่จุดหนึ่งเท่านั้น ทั้งสองเส้นทางมีทั้งตามเข็มนาฬิกาดังนั้นพื้นที่ของแต่ละคนจะเป็นบวกเว้นแต่เราจะประกาศว่าเส้นทางภายในนั้นมีน้ำหนัก -1 โดยเทียบกับเส้นทางนอก นี่เทียบเท่ากับการกลับทางของ alphaalpha

รูปหลายเหลี่ยมที่ได้มาจากตัวอย่างดั้งเดิม

อีกตัวอย่างพิจารณารูปหลายเหลี่ยมจากความคิดเห็นของ MickyT:

รูปหลายเหลี่ยมมาจากความคิดเห็นของ MickyT

ที่นี่รูปหลายเหลี่ยมบางส่วนเรียงตามเข็มนาฬิกาและทวนเข็มนาฬิกา กฎการข้ามเครื่องหมายการพลิกข้ามทำให้แน่ใจว่าภูมิภาคที่มุ่งเน้นตามเข็มนาฬิกาหยิบปัจจัยพิเศษที่ -1 ทำให้พวกมันมีส่วนร่วมในปริมาณที่เป็นบวกกับพื้นที่

นี่คือวิธีการทำงานของโปรแกรม:

import Data.List  -- for sort and nubBy

-- Rational division, with the unusual convention that x/0 = 2
_%0=2;x%y=x/y

-- Golf
h=sort

-- Define a "cyclic zipWith" operation. Given a list [a,b,c,...z] and a binary
-- operation (@), z (@) [a,b,c,..z] computes the list [b@a, c@b, ..., z@y, a@z]
z f w@(x:y)=zipWith f(y++[x])w

-- The shoelace formula for the signed area of a polygon
a=(%2).sum.z(#)

-- The "cross-product" of two 2d vectors, resulting in a scalar.
(a,b)#(c,d)=b*c-a*d

-- Determine if the line segment from p to p+r intersects the segment from
-- q to q+s.  Evaluates to the singleton list [(t,x)] where p + tr = x is the
-- point of intersection, or the empty list if there is no intersection. 
(r,p)?(s,q)=[(0,p)|p==q]++[(t,v t p r)|u t,u$f r]where f x=(d q p#x)%(r#s);t=f s;u x=x^2<x

-- v computes an affine combination of two vectors; d computes the difference
-- of two vectors.
v t(x,y)(a,b)=(x+t*a,y+t*b);d=v(-1)

-- If x is a list of points describing a polygon, s x will be the list of
-- (displacement, point) pairs describing the edges.
s x=zip(z d x)x

-- Given a list of (displacement, point) pairs describing a polygon's edges,
-- create a new polygon which also has a vertex at every point of intersection.
-- Mercilessly golfed.
i y=h.(=<<y).(?)=<<y


-- Extract a simple polygon; when an intersection point is reached, fast-forward
-- through the polygon until we return to the same point, then continue.  This
-- implements the edge rewiring operation. Also keep track of the first
-- intersection point we saw, so that we can process that polygon next and with
-- opposite sign.
[]!x=[x];x!_=x
e n(a@(x,p):y)|x>0=(n!y,a):(e(n!y)$tail$dropWhile((/=p).snd)y)|0<1=(n,a):e n y

-- Traverse the polygon from some arbitrary starting point, using e to extract
-- simple polygons marked with +/-1 weights.
c[p]k=w x[]where((_,q):x)=e[]p;w((n,y):z)b|q==y=(k,map snd(q:b)):c n(-k)|0<1=w z(y:b);c[]_=[]

-- If the original polygon had N vertices, there could (very conservatively)
-- be up to N^2 points of intersection.  So extract N^2 polygons using c,
-- throwing away duplicates, and add up the weighted areas of each polygon.
b(s,p)=s*a p
u(_,x)(_,y)=h x==h y
f p=abs$sum$map b$nubBy u$take(length p^2)$c[cycle$i$s p]1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.