Без опису
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138
  1. /*******************************************************************************
  2. * *
  3. * Author : Angus Johnson *
  4. * Version : 6.4.2 *
  5. * Date : 27 February 2017 *
  6. * Website : http://www.angusj.com *
  7. * Copyright : Angus Johnson 2010-2017 *
  8. * *
  9. * License: *
  10. * Use, modification & distribution is subject to Boost Software License Ver 1. *
  11. * http://www.boost.org/LICENSE_1_0.txt *
  12. * *
  13. * Attributions: *
  14. * The code in this library is an extension of Bala Vatti's clipping algorithm: *
  15. * "A generic solution to polygon clipping" *
  16. * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
  17. * http://portal.acm.org/citation.cfm?id=129906 *
  18. * *
  19. * Computer graphics and geometric modeling: implementation and algorithms *
  20. * By Max K. Agoston *
  21. * Springer; 1 edition (January 4, 2005) *
  22. * http://books.google.com/books?q=vatti+clipping+agoston *
  23. * *
  24. * See also: *
  25. * "Polygon Offsetting by Computing Winding Numbers" *
  26. * Paper no. DETC2005-85513 pp. 565-575 *
  27. * ASME 2005 International Design Engineering Technical Conferences *
  28. * and Computers and Information in Engineering Conference (IDETC/CIE2005) *
  29. * September 24-28, 2005 , Long Beach, California, USA *
  30. * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
  31. * *
  32. *******************************************************************************/
  33. /*******************************************************************************
  34. * *
  35. * This is a translation of the Delphi Clipper library and the naming style *
  36. * used has retained a Delphi flavour. *
  37. * *
  38. *******************************************************************************/
  39. // Changes introduced. (Venkify).
  40. // * Ability to query Normals along the generated path wrt pivot.
  41. // * Additional information that is helpful if the path needs Tessellation.
  42. // * Above functionality has been tested against single path Offset.
  43. // * Removed unused/unsupported code.
  44. // use_lines: Enables open path clipping. Adds a very minor cost to performance.
  45. #define use_lines
  46. using System;
  47. using System.Collections.Generic;
  48. using Unity.Collections;
  49. //using System.Text; //for Int128.AsString() & StringBuilder
  50. //using System.IO; //debugging with streamReader & StreamWriter
  51. //using System.Windows.Forms; //debugging to clipboard
  52. namespace UnityEngine.Rendering.Universal
  53. {
  54. using ClipInt = Int64;
  55. using Path = List<IntPoint>;
  56. using Paths = List<List<IntPoint>>;
  57. internal struct DoublePoint
  58. {
  59. public double X;
  60. public double Y;
  61. public DoublePoint(double x = 0, double y = 0)
  62. {
  63. this.X = x; this.Y = y;
  64. }
  65. public DoublePoint(DoublePoint dp)
  66. {
  67. this.X = dp.X; this.Y = dp.Y;
  68. }
  69. public DoublePoint(IntPoint ip)
  70. {
  71. this.X = ip.X; this.Y = ip.Y;
  72. }
  73. };
  74. //------------------------------------------------------------------------------
  75. // PolyTree & PolyNode classes
  76. //------------------------------------------------------------------------------
  77. internal class PolyTree : PolyNode
  78. {
  79. internal List<PolyNode> m_AllPolys = new List<PolyNode>();
  80. //The GC probably handles this cleanup more efficiently ...
  81. //~PolyTree(){Clear();}
  82. public void Clear()
  83. {
  84. for (int i = 0; i < m_AllPolys.Count; i++)
  85. m_AllPolys[i] = null;
  86. m_AllPolys.Clear();
  87. m_Childs.Clear();
  88. }
  89. public PolyNode GetFirst()
  90. {
  91. if (m_Childs.Count > 0)
  92. return m_Childs[0];
  93. else
  94. return null;
  95. }
  96. public int Total
  97. {
  98. get
  99. {
  100. int result = m_AllPolys.Count;
  101. //with negative offsets, ignore the hidden outer polygon ...
  102. if (result > 0 && m_Childs[0] != m_AllPolys[0]) result--;
  103. return result;
  104. }
  105. }
  106. }
  107. internal class PolyNode
  108. {
  109. internal PolyNode m_Parent;
  110. internal Path m_polygon = new Path();
  111. internal int m_Index;
  112. internal JoinTypes m_jointype;
  113. internal EndTypes m_endtype;
  114. internal List<PolyNode> m_Childs = new List<PolyNode>();
  115. private bool IsHoleNode()
  116. {
  117. bool result = true;
  118. PolyNode node = m_Parent;
  119. while (node != null)
  120. {
  121. result = !result;
  122. node = node.m_Parent;
  123. }
  124. return result;
  125. }
  126. public int ChildCount
  127. {
  128. get { return m_Childs.Count; }
  129. }
  130. public Path Contour
  131. {
  132. get { return m_polygon; }
  133. }
  134. internal void AddChild(PolyNode Child)
  135. {
  136. int cnt = m_Childs.Count;
  137. m_Childs.Add(Child);
  138. Child.m_Parent = this;
  139. Child.m_Index = cnt;
  140. }
  141. public PolyNode GetNext()
  142. {
  143. if (m_Childs.Count > 0)
  144. return m_Childs[0];
  145. else
  146. return GetNextSiblingUp();
  147. }
  148. internal PolyNode GetNextSiblingUp()
  149. {
  150. if (m_Parent == null)
  151. return null;
  152. else if (m_Index == m_Parent.m_Childs.Count - 1)
  153. return m_Parent.GetNextSiblingUp();
  154. else
  155. return m_Parent.m_Childs[m_Index + 1];
  156. }
  157. public List<PolyNode> Childs
  158. {
  159. get { return m_Childs; }
  160. }
  161. public PolyNode Parent
  162. {
  163. get { return m_Parent; }
  164. }
  165. public bool IsHole
  166. {
  167. get { return IsHoleNode(); }
  168. }
  169. public bool IsOpen { get; set; }
  170. }
  171. //------------------------------------------------------------------------------
  172. // Int128 struct (enables safe math on signed 64bit integers)
  173. // eg Int128 val1((Int64)9223372036854775807); //ie 2^63 -1
  174. // Int128 val2((Int64)9223372036854775807);
  175. // Int128 val3 = val1 * val2;
  176. // val3.ToString => "85070591730234615847396907784232501249" (8.5e+37)
  177. //------------------------------------------------------------------------------
  178. internal struct Int128
  179. {
  180. private Int64 hi;
  181. private UInt64 lo;
  182. public Int128(Int64 _lo)
  183. {
  184. lo = (UInt64)_lo;
  185. if (_lo < 0) hi = -1;
  186. else hi = 0;
  187. }
  188. public Int128(Int64 _hi, UInt64 _lo)
  189. {
  190. lo = _lo;
  191. hi = _hi;
  192. }
  193. public Int128(Int128 val)
  194. {
  195. hi = val.hi;
  196. lo = val.lo;
  197. }
  198. public bool IsNegative()
  199. {
  200. return hi < 0;
  201. }
  202. public static bool operator==(Int128 val1, Int128 val2)
  203. {
  204. if ((object)val1 == (object)val2) return true;
  205. else if ((object)val1 == null || (object)val2 == null) return false;
  206. return (val1.hi == val2.hi && val1.lo == val2.lo);
  207. }
  208. public static bool operator!=(Int128 val1, Int128 val2)
  209. {
  210. return !(val1 == val2);
  211. }
  212. public override bool Equals(System.Object obj)
  213. {
  214. if (obj == null || !(obj is Int128))
  215. return false;
  216. Int128 i128 = (Int128)obj;
  217. return (i128.hi == hi && i128.lo == lo);
  218. }
  219. public override int GetHashCode()
  220. {
  221. return hi.GetHashCode() ^ lo.GetHashCode();
  222. }
  223. public static bool operator>(Int128 val1, Int128 val2)
  224. {
  225. if (val1.hi != val2.hi)
  226. return val1.hi > val2.hi;
  227. else
  228. return val1.lo > val2.lo;
  229. }
  230. public static bool operator<(Int128 val1, Int128 val2)
  231. {
  232. if (val1.hi != val2.hi)
  233. return val1.hi < val2.hi;
  234. else
  235. return val1.lo < val2.lo;
  236. }
  237. public static Int128 operator+(Int128 lhs, Int128 rhs)
  238. {
  239. lhs.hi += rhs.hi;
  240. lhs.lo += rhs.lo;
  241. if (lhs.lo < rhs.lo) lhs.hi++;
  242. return lhs;
  243. }
  244. public static Int128 operator-(Int128 lhs, Int128 rhs)
  245. {
  246. return lhs + -rhs;
  247. }
  248. public static Int128 operator-(Int128 val)
  249. {
  250. if (val.lo == 0)
  251. return new Int128(-val.hi, 0);
  252. else
  253. return new Int128(~val.hi, ~val.lo + 1);
  254. }
  255. public static explicit operator double(Int128 val)
  256. {
  257. const double shift64 = 18446744073709551616.0; //2^64
  258. if (val.hi < 0)
  259. {
  260. if (val.lo == 0)
  261. return (double)val.hi * shift64;
  262. else
  263. return -(double)(~val.lo + ~val.hi * shift64);
  264. }
  265. else
  266. return (double)(val.lo + val.hi * shift64);
  267. }
  268. //nb: Constructing two new Int128 objects every time we want to multiply longs
  269. //is slow. So, although calling the Int128Mul method doesn't look as clean, the
  270. //code runs significantly faster than if we'd used the * operator.
  271. public static Int128 Int128Mul(Int64 lhs, Int64 rhs)
  272. {
  273. bool negate = (lhs < 0) != (rhs < 0);
  274. if (lhs < 0) lhs = -lhs;
  275. if (rhs < 0) rhs = -rhs;
  276. UInt64 int1Hi = (UInt64)lhs >> 32;
  277. UInt64 int1Lo = (UInt64)lhs & 0xFFFFFFFF;
  278. UInt64 int2Hi = (UInt64)rhs >> 32;
  279. UInt64 int2Lo = (UInt64)rhs & 0xFFFFFFFF;
  280. //nb: see comments in clipper.pas
  281. UInt64 a = int1Hi * int2Hi;
  282. UInt64 b = int1Lo * int2Lo;
  283. UInt64 c = int1Hi * int2Lo + int1Lo * int2Hi;
  284. UInt64 lo;
  285. Int64 hi;
  286. hi = (Int64)(a + (c >> 32));
  287. unchecked { lo = (c << 32) + b; }
  288. if (lo < b) hi++;
  289. Int128 result = new Int128(hi, lo);
  290. return negate ? -result : result;
  291. }
  292. };
  293. //------------------------------------------------------------------------------
  294. //------------------------------------------------------------------------------
  295. internal struct IntPoint
  296. {
  297. public ClipInt N;
  298. public ClipInt X;
  299. public ClipInt Y;
  300. public ClipInt D;
  301. public double NX;
  302. public double NY;
  303. public IntPoint(ClipInt X, ClipInt Y)
  304. {
  305. this.X = X; this.Y = Y;
  306. this.NX = 0; this.NY = 0;
  307. this.N = -1; this.D = 0;
  308. }
  309. public IntPoint(double x, double y)
  310. {
  311. this.X = (ClipInt)x; this.Y = (ClipInt)y;
  312. this.NX = 0; this.NY = 0;
  313. this.N = -1; this.D = 0;
  314. }
  315. public IntPoint(IntPoint pt)
  316. {
  317. this.X = pt.X; this.Y = pt.Y;
  318. this.NX = pt.NX; this.NY = pt.NY;
  319. this.N = pt.N; this.D = pt.D;
  320. }
  321. public static bool operator==(IntPoint a, IntPoint b)
  322. {
  323. return a.X == b.X && a.Y == b.Y;
  324. }
  325. public static bool operator!=(IntPoint a, IntPoint b)
  326. {
  327. return a.X != b.X || a.Y != b.Y;
  328. }
  329. public override bool Equals(object obj)
  330. {
  331. if (obj == null) return false;
  332. if (obj is IntPoint)
  333. {
  334. IntPoint a = (IntPoint)obj;
  335. return (X == a.X) && (Y == a.Y);
  336. }
  337. else return false;
  338. }
  339. public override int GetHashCode()
  340. {
  341. //simply prevents a compiler warning
  342. return base.GetHashCode();
  343. }
  344. }// end struct IntPoint
  345. internal struct IntRect
  346. {
  347. public ClipInt left;
  348. public ClipInt top;
  349. public ClipInt right;
  350. public ClipInt bottom;
  351. public IntRect(ClipInt l, ClipInt t, ClipInt r, ClipInt b)
  352. {
  353. this.left = l; this.top = t;
  354. this.right = r; this.bottom = b;
  355. }
  356. public IntRect(IntRect ir)
  357. {
  358. this.left = ir.left; this.top = ir.top;
  359. this.right = ir.right; this.bottom = ir.bottom;
  360. }
  361. }
  362. /// <summary>
  363. /// Options for clip types.
  364. /// </summary>
  365. [Obsolete("Will be removed in 2025.1", true)]
  366. public enum ClipType
  367. {
  368. /// <summary>
  369. /// Use this for intersection.
  370. /// </summary>
  371. ctIntersection,
  372. /// <summary>
  373. /// Use this for union.
  374. /// </summary>
  375. ctUnion,
  376. /// <summary>
  377. /// Use this for difference.
  378. /// </summary>
  379. ctDifference,
  380. /// <summary>
  381. /// Use this for XOR.
  382. /// </summary>
  383. ctXor
  384. };
  385. /// <summary>
  386. /// Options for polygon types.
  387. /// </summary>
  388. [Obsolete("Will be removed in 2025.1", true)]
  389. public enum PolyType
  390. {
  391. /// <summary>
  392. /// Use this for subject.
  393. /// </summary>
  394. ptSubject,
  395. /// <summary>
  396. /// Use this for clip.
  397. /// </summary>
  398. ptClip
  399. };
  400. //By far the most widely used winding rules for polygon filling are
  401. //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
  402. //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
  403. //see http://glprogramming.com/red/chapter11.html
  404. /// <summary>
  405. /// Options for polygon filling types.
  406. /// </summary>
  407. [Obsolete("Will be removed in 2025.1", true)]
  408. public enum PolyFillType
  409. {
  410. /// <summary>
  411. /// Use this for even odd.
  412. /// </summary>
  413. pftEvenOdd,
  414. /// <summary>
  415. /// Use this for non zero.
  416. /// </summary>
  417. pftNonZero,
  418. /// <summary>
  419. /// Use this for positive.
  420. /// </summary>
  421. pftPositive,
  422. /// <summary>
  423. /// Use this for negative.
  424. /// </summary>
  425. pftNegative
  426. };
  427. /// <summary>
  428. /// Options for join types.
  429. /// </summary>
  430. [Obsolete("Will be removed in 2025.1", true)]
  431. public enum JoinType
  432. {
  433. /// <summary>
  434. /// Use this for round.
  435. /// </summary>
  436. jtRound
  437. };
  438. /// <summary>
  439. /// Options for end types.
  440. /// </summary>
  441. [Obsolete("Will be removed in 2025.1", true)]
  442. public enum EndType
  443. {
  444. /// <summary>
  445. /// Use this for closed polygon.
  446. /// </summary>
  447. etClosedPolygon,
  448. /// <summary>
  449. /// Use this for closed line.
  450. /// </summary>
  451. etClosedLine
  452. };
  453. internal enum ClipTypes { ctIntersection, ctUnion, ctDifference, ctXor };
  454. internal enum PolyTypes { ptSubject, ptClip };
  455. internal enum PolyFillTypes { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
  456. internal enum JoinTypes { jtRound };
  457. internal enum EndTypes { etClosedPolygon, etClosedLine };
  458. internal enum EdgeSides { esLeft, esRight };
  459. internal enum Directions { dRightToLeft, dLeftToRight };
  460. internal class TEdge
  461. {
  462. internal IntPoint Bot;
  463. internal IntPoint Curr; //current (updated for every new scanbeam)
  464. internal IntPoint Top;
  465. internal IntPoint Delta;
  466. internal double Dx;
  467. internal PolyTypes PolyTyp;
  468. internal EdgeSides Side; //side only refers to current side of solution poly
  469. internal int WindDelta; //1 or -1 depending on winding direction
  470. internal int WindCnt;
  471. internal int WindCnt2; //winding count of the opposite polytype
  472. internal int OutIdx;
  473. internal TEdge Next;
  474. internal TEdge Prev;
  475. internal TEdge NextInLML;
  476. internal TEdge NextInAEL;
  477. internal TEdge PrevInAEL;
  478. internal TEdge NextInSEL;
  479. internal TEdge PrevInSEL;
  480. };
  481. internal class IntersectNode
  482. {
  483. internal TEdge Edge1;
  484. internal TEdge Edge2;
  485. internal IntPoint Pt;
  486. };
  487. internal class MyIntersectNodeSort : IComparer<IntersectNode>
  488. {
  489. public int Compare(IntersectNode node1, IntersectNode node2)
  490. {
  491. ClipInt i = node2.Pt.Y - node1.Pt.Y;
  492. if (i > 0) return 1;
  493. else if (i < 0) return -1;
  494. else return 0;
  495. }
  496. }
  497. internal class LocalMinima
  498. {
  499. internal ClipInt Y;
  500. internal TEdge LeftBound;
  501. internal TEdge RightBound;
  502. internal LocalMinima Next;
  503. };
  504. internal class Scanbeam
  505. {
  506. internal ClipInt Y;
  507. internal Scanbeam Next;
  508. };
  509. internal class Maxima
  510. {
  511. internal ClipInt X;
  512. internal Maxima Next;
  513. internal Maxima Prev;
  514. };
  515. //OutRec: contains a path in the clipping solution. Edges in the AEL will
  516. //carry a pointer to an OutRec when they are part of the clipping solution.
  517. internal class OutRec
  518. {
  519. internal int Idx;
  520. internal bool IsHole;
  521. internal bool IsOpen;
  522. internal OutRec FirstLeft; //see comments in clipper.pas
  523. internal OutPt Pts;
  524. internal OutPt BottomPt;
  525. internal PolyNode PolyNode;
  526. };
  527. internal class OutPt
  528. {
  529. internal int Idx;
  530. internal IntPoint Pt;
  531. internal OutPt Next;
  532. internal OutPt Prev;
  533. };
  534. internal class Join
  535. {
  536. internal OutPt OutPt1;
  537. internal OutPt OutPt2;
  538. internal IntPoint OffPt;
  539. };
  540. internal class ClipperBase
  541. {
  542. internal const double horizontal = -3.4E+38;
  543. internal const int Skip = -2;
  544. internal const int Unassigned = -1;
  545. internal const double tolerance = 1.0E-20;
  546. internal static bool near_zero(double val) { return (val > -tolerance) && (val < tolerance); }
  547. public const ClipInt loRange = 0x3FFFFFFF;
  548. public const ClipInt hiRange = 0x3FFFFFFFFFFFFFFFL;
  549. internal LocalMinima m_MinimaList;
  550. internal LocalMinima m_CurrentLM;
  551. internal List<List<TEdge>> m_edges = new List<List<TEdge>>();
  552. internal Scanbeam m_Scanbeam;
  553. internal List<OutRec> m_PolyOuts;
  554. internal TEdge m_ActiveEdges;
  555. internal bool m_UseFullRange;
  556. internal bool m_HasOpenPaths;
  557. //------------------------------------------------------------------------------
  558. public bool PreserveCollinear
  559. {
  560. get;
  561. set;
  562. }
  563. //------------------------------------------------------------------------------
  564. public void Swap(ref ClipInt val1, ref ClipInt val2)
  565. {
  566. ClipInt tmp = val1;
  567. val1 = val2;
  568. val2 = tmp;
  569. }
  570. //------------------------------------------------------------------------------
  571. internal static bool IsHorizontal(TEdge e)
  572. {
  573. return e.Delta.Y == 0;
  574. }
  575. //------------------------------------------------------------------------------
  576. internal bool PointIsVertex(IntPoint pt, OutPt pp)
  577. {
  578. OutPt pp2 = pp;
  579. do
  580. {
  581. if (pp2.Pt == pt) return true;
  582. pp2 = pp2.Next;
  583. }
  584. while (pp2 != pp);
  585. return false;
  586. }
  587. //------------------------------------------------------------------------------
  588. internal bool PointOnLineSegment(IntPoint pt,
  589. IntPoint linePt1, IntPoint linePt2, bool UseFullRange)
  590. {
  591. if (UseFullRange)
  592. return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) ||
  593. ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) ||
  594. (((pt.X > linePt1.X) == (pt.X < linePt2.X)) &&
  595. ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) &&
  596. ((Int128.Int128Mul((pt.X - linePt1.X), (linePt2.Y - linePt1.Y)) ==
  597. Int128.Int128Mul((linePt2.X - linePt1.X), (pt.Y - linePt1.Y)))));
  598. else
  599. return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) ||
  600. ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) ||
  601. (((pt.X > linePt1.X) == (pt.X < linePt2.X)) &&
  602. ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) &&
  603. ((pt.X - linePt1.X) * (linePt2.Y - linePt1.Y) ==
  604. (linePt2.X - linePt1.X) * (pt.Y - linePt1.Y)));
  605. }
  606. //------------------------------------------------------------------------------
  607. internal bool PointOnPolygon(IntPoint pt, OutPt pp, bool UseFullRange)
  608. {
  609. OutPt pp2 = pp;
  610. while (true)
  611. {
  612. if (PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange))
  613. return true;
  614. pp2 = pp2.Next;
  615. if (pp2 == pp) break;
  616. }
  617. return false;
  618. }
  619. //------------------------------------------------------------------------------
  620. internal static bool SlopesEqual(TEdge e1, TEdge e2, bool UseFullRange)
  621. {
  622. if (UseFullRange)
  623. return Int128.Int128Mul(e1.Delta.Y, e2.Delta.X) ==
  624. Int128.Int128Mul(e1.Delta.X, e2.Delta.Y);
  625. else
  626. return (ClipInt)(e1.Delta.Y) * (e2.Delta.X) ==
  627. (ClipInt)(e1.Delta.X) * (e2.Delta.Y);
  628. }
  629. //------------------------------------------------------------------------------
  630. internal static bool SlopesEqual(IntPoint pt1, IntPoint pt2,
  631. IntPoint pt3, bool UseFullRange)
  632. {
  633. if (UseFullRange)
  634. return Int128.Int128Mul(pt1.Y - pt2.Y, pt2.X - pt3.X) ==
  635. Int128.Int128Mul(pt1.X - pt2.X, pt2.Y - pt3.Y);
  636. else
  637. return
  638. (ClipInt)(pt1.Y - pt2.Y) * (pt2.X - pt3.X) - (ClipInt)(pt1.X - pt2.X) * (pt2.Y - pt3.Y) == 0;
  639. }
  640. //------------------------------------------------------------------------------
  641. internal static bool SlopesEqual(IntPoint pt1, IntPoint pt2,
  642. IntPoint pt3, IntPoint pt4, bool UseFullRange)
  643. {
  644. if (UseFullRange)
  645. return Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X) ==
  646. Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y);
  647. else
  648. return
  649. (ClipInt)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (ClipInt)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0;
  650. }
  651. //------------------------------------------------------------------------------
  652. internal ClipperBase() //constructor (nb: no external instantiation)
  653. {
  654. m_MinimaList = null;
  655. m_CurrentLM = null;
  656. m_UseFullRange = false;
  657. m_HasOpenPaths = false;
  658. }
  659. //------------------------------------------------------------------------------
  660. public virtual void Clear()
  661. {
  662. DisposeLocalMinimaList();
  663. for (int i = 0; i < m_edges.Count; ++i)
  664. {
  665. for (int j = 0; j < m_edges[i].Count; ++j) m_edges[i][j] = null;
  666. m_edges[i].Clear();
  667. }
  668. m_edges.Clear();
  669. m_UseFullRange = false;
  670. m_HasOpenPaths = false;
  671. }
  672. //------------------------------------------------------------------------------
  673. private void DisposeLocalMinimaList()
  674. {
  675. while (m_MinimaList != null)
  676. {
  677. LocalMinima tmpLm = m_MinimaList.Next;
  678. m_MinimaList = null;
  679. m_MinimaList = tmpLm;
  680. }
  681. m_CurrentLM = null;
  682. }
  683. //------------------------------------------------------------------------------
  684. void RangeTest(IntPoint Pt, ref bool useFullRange)
  685. {
  686. if (useFullRange)
  687. {
  688. if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
  689. throw new ClipperException("Coordinate outside allowed range");
  690. }
  691. else if (Pt.X > loRange || Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)
  692. {
  693. useFullRange = true;
  694. RangeTest(Pt, ref useFullRange);
  695. }
  696. }
  697. //------------------------------------------------------------------------------
  698. private void InitEdge(TEdge e, TEdge eNext,
  699. TEdge ePrev, IntPoint pt)
  700. {
  701. e.Next = eNext;
  702. e.Prev = ePrev;
  703. e.Curr = pt;
  704. e.OutIdx = Unassigned;
  705. }
  706. //------------------------------------------------------------------------------
  707. private void InitEdge2(TEdge e, PolyTypes polyType)
  708. {
  709. if (e.Curr.Y >= e.Next.Curr.Y)
  710. {
  711. e.Bot = e.Curr;
  712. e.Top = e.Next.Curr;
  713. }
  714. else
  715. {
  716. e.Top = e.Curr;
  717. e.Bot = e.Next.Curr;
  718. }
  719. SetDx(e);
  720. e.PolyTyp = polyType;
  721. }
  722. //------------------------------------------------------------------------------
  723. private TEdge FindNextLocMin(TEdge E)
  724. {
  725. TEdge E2;
  726. for (;;)
  727. {
  728. while (E.Bot != E.Prev.Bot || E.Curr == E.Top) E = E.Next;
  729. if (E.Dx != horizontal && E.Prev.Dx != horizontal) break;
  730. while (E.Prev.Dx == horizontal) E = E.Prev;
  731. E2 = E;
  732. while (E.Dx == horizontal) E = E.Next;
  733. if (E.Top.Y == E.Prev.Bot.Y) continue; //ie just an intermediate horz.
  734. if (E2.Prev.Bot.X < E.Bot.X) E = E2;
  735. break;
  736. }
  737. return E;
  738. }
  739. //------------------------------------------------------------------------------
  740. private TEdge ProcessBound(TEdge E, bool LeftBoundIsForward)
  741. {
  742. TEdge EStart, Result = E;
  743. TEdge Horz;
  744. if (Result.OutIdx == Skip)
  745. {
  746. //check if there are edges beyond the skip edge in the bound and if so
  747. //create another LocMin and calling ProcessBound once more ...
  748. E = Result;
  749. if (LeftBoundIsForward)
  750. {
  751. while (E.Top.Y == E.Next.Bot.Y) E = E.Next;
  752. while (E != Result && E.Dx == horizontal) E = E.Prev;
  753. }
  754. else
  755. {
  756. while (E.Top.Y == E.Prev.Bot.Y) E = E.Prev;
  757. while (E != Result && E.Dx == horizontal) E = E.Next;
  758. }
  759. if (E == Result)
  760. {
  761. if (LeftBoundIsForward) Result = E.Next;
  762. else Result = E.Prev;
  763. }
  764. else
  765. {
  766. //there are more edges in the bound beyond result starting with E
  767. if (LeftBoundIsForward)
  768. E = Result.Next;
  769. else
  770. E = Result.Prev;
  771. LocalMinima locMin = new LocalMinima();
  772. locMin.Next = null;
  773. locMin.Y = E.Bot.Y;
  774. locMin.LeftBound = null;
  775. locMin.RightBound = E;
  776. E.WindDelta = 0;
  777. Result = ProcessBound(E, LeftBoundIsForward);
  778. InsertLocalMinima(locMin);
  779. }
  780. return Result;
  781. }
  782. if (E.Dx == horizontal)
  783. {
  784. //We need to be careful with open paths because this may not be a
  785. //true local minima (ie E may be following a skip edge).
  786. //Also, consecutive horz. edges may start heading left before going right.
  787. if (LeftBoundIsForward) EStart = E.Prev;
  788. else EStart = E.Next;
  789. if (EStart.Dx == horizontal) //ie an adjoining horizontal skip edge
  790. {
  791. if (EStart.Bot.X != E.Bot.X && EStart.Top.X != E.Bot.X)
  792. ReverseHorizontal(E);
  793. }
  794. else if (EStart.Bot.X != E.Bot.X)
  795. ReverseHorizontal(E);
  796. }
  797. EStart = E;
  798. if (LeftBoundIsForward)
  799. {
  800. while (Result.Top.Y == Result.Next.Bot.Y && Result.Next.OutIdx != Skip)
  801. Result = Result.Next;
  802. if (Result.Dx == horizontal && Result.Next.OutIdx != Skip)
  803. {
  804. //nb: at the top of a bound, horizontals are added to the bound
  805. //only when the preceding edge attaches to the horizontal's left vertex
  806. //unless a Skip edge is encountered when that becomes the top divide
  807. Horz = Result;
  808. while (Horz.Prev.Dx == horizontal) Horz = Horz.Prev;
  809. if (Horz.Prev.Top.X > Result.Next.Top.X) Result = Horz.Prev;
  810. }
  811. while (E != Result)
  812. {
  813. E.NextInLML = E.Next;
  814. if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X)
  815. ReverseHorizontal(E);
  816. E = E.Next;
  817. }
  818. if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X)
  819. ReverseHorizontal(E);
  820. Result = Result.Next; //move to the edge just beyond current bound
  821. }
  822. else
  823. {
  824. while (Result.Top.Y == Result.Prev.Bot.Y && Result.Prev.OutIdx != Skip)
  825. Result = Result.Prev;
  826. if (Result.Dx == horizontal && Result.Prev.OutIdx != Skip)
  827. {
  828. Horz = Result;
  829. while (Horz.Next.Dx == horizontal) Horz = Horz.Next;
  830. if (Horz.Next.Top.X == Result.Prev.Top.X ||
  831. Horz.Next.Top.X > Result.Prev.Top.X) Result = Horz.Next;
  832. }
  833. while (E != Result)
  834. {
  835. E.NextInLML = E.Prev;
  836. if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X)
  837. ReverseHorizontal(E);
  838. E = E.Prev;
  839. }
  840. if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X)
  841. ReverseHorizontal(E);
  842. Result = Result.Prev; //move to the edge just beyond current bound
  843. }
  844. return Result;
  845. }
  846. //------------------------------------------------------------------------------
  847. public bool AddPath(Path pg, PolyTypes polyType, bool Closed)
  848. {
  849. #if use_lines
  850. if (!Closed && polyType == PolyTypes.ptClip)
  851. throw new ClipperException("AddPath: Open paths must be subject.");
  852. #else
  853. if (!Closed)
  854. throw new ClipperException("AddPath: Open paths have been disabled.");
  855. #endif
  856. int highI = (int)pg.Count - 1;
  857. if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI;
  858. while (highI > 0 && (pg[highI] == pg[highI - 1])) --highI;
  859. if ((Closed && highI < 2) || (!Closed && highI < 1)) return false;
  860. //create a new edge array ...
  861. List<TEdge> edges = new List<TEdge>(highI + 1);
  862. for (int i = 0; i <= highI; i++) edges.Add(new TEdge());
  863. bool IsFlat = true;
  864. //1. Basic (first) edge initialization ...
  865. edges[1].Curr = pg[1];
  866. RangeTest(pg[0], ref m_UseFullRange);
  867. RangeTest(pg[highI], ref m_UseFullRange);
  868. InitEdge(edges[0], edges[1], edges[highI], pg[0]);
  869. InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]);
  870. for (int i = highI - 1; i >= 1; --i)
  871. {
  872. RangeTest(pg[i], ref m_UseFullRange);
  873. InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]);
  874. }
  875. TEdge eStart = edges[0];
  876. //2. Remove duplicate vertices, and (when closed) collinear edges ...
  877. TEdge E = eStart, eLoopStop = eStart;
  878. for (;;)
  879. {
  880. //nb: allows matching start and end points when not Closed ...
  881. if (E.Curr == E.Next.Curr && (Closed || E.Next != eStart))
  882. {
  883. if (E == E.Next) break;
  884. if (E == eStart) eStart = E.Next;
  885. E = RemoveEdge(E);
  886. eLoopStop = E;
  887. continue;
  888. }
  889. if (E.Prev == E.Next)
  890. break; //only two vertices
  891. else if (Closed &&
  892. SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, m_UseFullRange) &&
  893. (!PreserveCollinear ||
  894. !Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr)))
  895. {
  896. //Collinear edges are allowed for open paths but in closed paths
  897. //the default is to merge adjacent collinear edges into a single edge.
  898. //However, if the PreserveCollinear property is enabled, only overlapping
  899. //collinear edges (ie spikes) will be removed from closed paths.
  900. if (E == eStart) eStart = E.Next;
  901. E = RemoveEdge(E);
  902. E = E.Prev;
  903. eLoopStop = E;
  904. continue;
  905. }
  906. E = E.Next;
  907. if ((E == eLoopStop) || (!Closed && E.Next == eStart)) break;
  908. }
  909. if ((!Closed && (E == E.Next)) || (Closed && (E.Prev == E.Next)))
  910. return false;
  911. if (!Closed)
  912. {
  913. m_HasOpenPaths = true;
  914. eStart.Prev.OutIdx = Skip;
  915. }
  916. //3. Do second stage of edge initialization ...
  917. E = eStart;
  918. do
  919. {
  920. InitEdge2(E, polyType);
  921. E = E.Next;
  922. if (IsFlat && E.Curr.Y != eStart.Curr.Y) IsFlat = false;
  923. }
  924. while (E != eStart);
  925. //4. Finally, add edge bounds to LocalMinima list ...
  926. //Totally flat paths must be handled differently when adding them
  927. //to LocalMinima list to avoid endless loops etc ...
  928. if (IsFlat)
  929. {
  930. if (Closed) return false;
  931. E.Prev.OutIdx = Skip;
  932. LocalMinima locMin = new LocalMinima();
  933. locMin.Next = null;
  934. locMin.Y = E.Bot.Y;
  935. locMin.LeftBound = null;
  936. locMin.RightBound = E;
  937. locMin.RightBound.Side = EdgeSides.esRight;
  938. locMin.RightBound.WindDelta = 0;
  939. for (;;)
  940. {
  941. if (E.Bot.X != E.Prev.Top.X) ReverseHorizontal(E);
  942. if (E.Next.OutIdx == Skip) break;
  943. E.NextInLML = E.Next;
  944. E = E.Next;
  945. }
  946. InsertLocalMinima(locMin);
  947. m_edges.Add(edges);
  948. return true;
  949. }
  950. m_edges.Add(edges);
  951. bool leftBoundIsForward;
  952. TEdge EMin = null;
  953. //workaround to avoid an endless loop in the while loop below when
  954. //open paths have matching start and end points ...
  955. if (E.Prev.Bot == E.Prev.Top) E = E.Next;
  956. for (;;)
  957. {
  958. E = FindNextLocMin(E);
  959. if (E == EMin) break;
  960. else if (EMin == null) EMin = E;
  961. //E and E.Prev now share a local minima (left aligned if horizontal).
  962. //Compare their slopes to find which starts which bound ...
  963. LocalMinima locMin = new LocalMinima();
  964. locMin.Next = null;
  965. locMin.Y = E.Bot.Y;
  966. if (E.Dx < E.Prev.Dx)
  967. {
  968. locMin.LeftBound = E.Prev;
  969. locMin.RightBound = E;
  970. leftBoundIsForward = false; //Q.nextInLML = Q.prev
  971. }
  972. else
  973. {
  974. locMin.LeftBound = E;
  975. locMin.RightBound = E.Prev;
  976. leftBoundIsForward = true; //Q.nextInLML = Q.next
  977. }
  978. locMin.LeftBound.Side = EdgeSides.esLeft;
  979. locMin.RightBound.Side = EdgeSides.esRight;
  980. if (!Closed) locMin.LeftBound.WindDelta = 0;
  981. else if (locMin.LeftBound.Next == locMin.RightBound)
  982. locMin.LeftBound.WindDelta = -1;
  983. else locMin.LeftBound.WindDelta = 1;
  984. locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta;
  985. E = ProcessBound(locMin.LeftBound, leftBoundIsForward);
  986. if (E.OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward);
  987. TEdge E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward);
  988. if (E2.OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward);
  989. if (locMin.LeftBound.OutIdx == Skip)
  990. locMin.LeftBound = null;
  991. else if (locMin.RightBound.OutIdx == Skip)
  992. locMin.RightBound = null;
  993. InsertLocalMinima(locMin);
  994. if (!leftBoundIsForward) E = E2;
  995. }
  996. return true;
  997. }
  998. //------------------------------------------------------------------------------
  999. public bool AddPaths(Paths ppg, PolyTypes polyType, bool closed)
  1000. {
  1001. bool result = false;
  1002. for (int i = 0; i < ppg.Count; ++i)
  1003. if (AddPath(ppg[i], polyType, closed)) result = true;
  1004. return result;
  1005. }
  1006. //------------------------------------------------------------------------------
  1007. internal bool Pt2IsBetweenPt1AndPt3(IntPoint pt1, IntPoint pt2, IntPoint pt3)
  1008. {
  1009. if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) return false;
  1010. else if (pt1.X != pt3.X) return (pt2.X > pt1.X) == (pt2.X < pt3.X);
  1011. else return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);
  1012. }
  1013. //------------------------------------------------------------------------------
  1014. TEdge RemoveEdge(TEdge e)
  1015. {
  1016. //removes e from double_linked_list (but without removing from memory)
  1017. e.Prev.Next = e.Next;
  1018. e.Next.Prev = e.Prev;
  1019. TEdge result = e.Next;
  1020. e.Prev = null; //flag as removed (see ClipperBase.Clear)
  1021. return result;
  1022. }
  1023. //------------------------------------------------------------------------------
  1024. private void SetDx(TEdge e)
  1025. {
  1026. e.Delta.X = (e.Top.X - e.Bot.X);
  1027. e.Delta.Y = (e.Top.Y - e.Bot.Y);
  1028. if (e.Delta.Y == 0) e.Dx = horizontal;
  1029. else e.Dx = (double)(e.Delta.X) / (e.Delta.Y);
  1030. }
  1031. //---------------------------------------------------------------------------
  1032. private void InsertLocalMinima(LocalMinima newLm)
  1033. {
  1034. if (m_MinimaList == null)
  1035. {
  1036. m_MinimaList = newLm;
  1037. }
  1038. else if (newLm.Y >= m_MinimaList.Y)
  1039. {
  1040. newLm.Next = m_MinimaList;
  1041. m_MinimaList = newLm;
  1042. }
  1043. else
  1044. {
  1045. LocalMinima tmpLm = m_MinimaList;
  1046. while (tmpLm.Next != null && (newLm.Y < tmpLm.Next.Y))
  1047. tmpLm = tmpLm.Next;
  1048. newLm.Next = tmpLm.Next;
  1049. tmpLm.Next = newLm;
  1050. }
  1051. }
  1052. //------------------------------------------------------------------------------
  1053. internal Boolean PopLocalMinima(ClipInt Y, out LocalMinima current)
  1054. {
  1055. current = m_CurrentLM;
  1056. if (m_CurrentLM != null && m_CurrentLM.Y == Y)
  1057. {
  1058. m_CurrentLM = m_CurrentLM.Next;
  1059. return true;
  1060. }
  1061. return false;
  1062. }
  1063. //------------------------------------------------------------------------------
  1064. private void ReverseHorizontal(TEdge e)
  1065. {
  1066. //swap horizontal edges' top and bottom x's so they follow the natural
  1067. //progression of the bounds - ie so their xbots will align with the
  1068. //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
  1069. Swap(ref e.Top.X, ref e.Bot.X);
  1070. }
  1071. //------------------------------------------------------------------------------
  1072. internal virtual void Reset()
  1073. {
  1074. m_CurrentLM = m_MinimaList;
  1075. if (m_CurrentLM == null) return; //ie nothing to process
  1076. //reset all edges ...
  1077. m_Scanbeam = null;
  1078. LocalMinima lm = m_MinimaList;
  1079. while (lm != null)
  1080. {
  1081. InsertScanbeam(lm.Y);
  1082. TEdge e = lm.LeftBound;
  1083. if (e != null)
  1084. {
  1085. e.Curr = e.Bot;
  1086. e.OutIdx = Unassigned;
  1087. }
  1088. e = lm.RightBound;
  1089. if (e != null)
  1090. {
  1091. e.Curr = e.Bot;
  1092. e.OutIdx = Unassigned;
  1093. }
  1094. lm = lm.Next;
  1095. }
  1096. m_ActiveEdges = null;
  1097. }
  1098. //------------------------------------------------------------------------------
  1099. public static IntRect GetBounds(Paths paths)
  1100. {
  1101. int i = 0, cnt = paths.Count;
  1102. while (i < cnt && paths[i].Count == 0) i++;
  1103. if (i == cnt) return new IntRect(0, 0, 0, 0);
  1104. IntRect result = new IntRect();
  1105. result.left = paths[i][0].X;
  1106. result.right = result.left;
  1107. result.top = paths[i][0].Y;
  1108. result.bottom = result.top;
  1109. for (; i < cnt; i++)
  1110. for (int j = 0; j < paths[i].Count; j++)
  1111. {
  1112. if (paths[i][j].X < result.left) result.left = paths[i][j].X;
  1113. else if (paths[i][j].X > result.right) result.right = paths[i][j].X;
  1114. if (paths[i][j].Y < result.top) result.top = paths[i][j].Y;
  1115. else if (paths[i][j].Y > result.bottom) result.bottom = paths[i][j].Y;
  1116. }
  1117. return result;
  1118. }
  1119. //------------------------------------------------------------------------------
  1120. internal void InsertScanbeam(ClipInt Y)
  1121. {
  1122. //single-linked list: sorted descending, ignoring dups.
  1123. if (m_Scanbeam == null)
  1124. {
  1125. m_Scanbeam = new Scanbeam();
  1126. m_Scanbeam.Next = null;
  1127. m_Scanbeam.Y = Y;
  1128. }
  1129. else if (Y > m_Scanbeam.Y)
  1130. {
  1131. Scanbeam newSb = new Scanbeam();
  1132. newSb.Y = Y;
  1133. newSb.Next = m_Scanbeam;
  1134. m_Scanbeam = newSb;
  1135. }
  1136. else
  1137. {
  1138. Scanbeam sb2 = m_Scanbeam;
  1139. while (sb2.Next != null && (Y <= sb2.Next.Y)) sb2 = sb2.Next;
  1140. if (Y == sb2.Y) return; //ie ignores duplicates
  1141. Scanbeam newSb = new Scanbeam();
  1142. newSb.Y = Y;
  1143. newSb.Next = sb2.Next;
  1144. sb2.Next = newSb;
  1145. }
  1146. }
  1147. //------------------------------------------------------------------------------
  1148. internal Boolean PopScanbeam(out ClipInt Y)
  1149. {
  1150. if (m_Scanbeam == null)
  1151. {
  1152. Y = 0;
  1153. return false;
  1154. }
  1155. Y = m_Scanbeam.Y;
  1156. m_Scanbeam = m_Scanbeam.Next;
  1157. return true;
  1158. }
  1159. //------------------------------------------------------------------------------
  1160. internal Boolean LocalMinimaPending()
  1161. {
  1162. return (m_CurrentLM != null);
  1163. }
  1164. //------------------------------------------------------------------------------
  1165. internal OutRec CreateOutRec()
  1166. {
  1167. OutRec result = new OutRec();
  1168. result.Idx = Unassigned;
  1169. result.IsHole = false;
  1170. result.IsOpen = false;
  1171. result.FirstLeft = null;
  1172. result.Pts = null;
  1173. result.BottomPt = null;
  1174. result.PolyNode = null;
  1175. m_PolyOuts.Add(result);
  1176. result.Idx = m_PolyOuts.Count - 1;
  1177. return result;
  1178. }
  1179. //------------------------------------------------------------------------------
  1180. internal void DisposeOutRec(int index)
  1181. {
  1182. OutRec outRec = m_PolyOuts[index];
  1183. outRec.Pts = null;
  1184. outRec = null;
  1185. m_PolyOuts[index] = null;
  1186. }
  1187. //------------------------------------------------------------------------------
  1188. internal void UpdateEdgeIntoAEL(ref TEdge e)
  1189. {
  1190. if (e.NextInLML == null)
  1191. throw new ClipperException("UpdateEdgeIntoAEL: invalid call");
  1192. TEdge AelPrev = e.PrevInAEL;
  1193. TEdge AelNext = e.NextInAEL;
  1194. e.NextInLML.OutIdx = e.OutIdx;
  1195. if (AelPrev != null)
  1196. AelPrev.NextInAEL = e.NextInLML;
  1197. else m_ActiveEdges = e.NextInLML;
  1198. if (AelNext != null)
  1199. AelNext.PrevInAEL = e.NextInLML;
  1200. e.NextInLML.Side = e.Side;
  1201. e.NextInLML.WindDelta = e.WindDelta;
  1202. e.NextInLML.WindCnt = e.WindCnt;
  1203. e.NextInLML.WindCnt2 = e.WindCnt2;
  1204. e = e.NextInLML;
  1205. e.Curr = e.Bot;
  1206. e.PrevInAEL = AelPrev;
  1207. e.NextInAEL = AelNext;
  1208. if (!IsHorizontal(e)) InsertScanbeam(e.Top.Y);
  1209. }
  1210. //------------------------------------------------------------------------------
  1211. internal void SwapPositionsInAEL(TEdge edge1, TEdge edge2)
  1212. {
  1213. //check that one or other edge hasn't already been removed from AEL ...
  1214. if (edge1.NextInAEL == edge1.PrevInAEL ||
  1215. edge2.NextInAEL == edge2.PrevInAEL) return;
  1216. if (edge1.NextInAEL == edge2)
  1217. {
  1218. TEdge next = edge2.NextInAEL;
  1219. if (next != null)
  1220. next.PrevInAEL = edge1;
  1221. TEdge prev = edge1.PrevInAEL;
  1222. if (prev != null)
  1223. prev.NextInAEL = edge2;
  1224. edge2.PrevInAEL = prev;
  1225. edge2.NextInAEL = edge1;
  1226. edge1.PrevInAEL = edge2;
  1227. edge1.NextInAEL = next;
  1228. }
  1229. else if (edge2.NextInAEL == edge1)
  1230. {
  1231. TEdge next = edge1.NextInAEL;
  1232. if (next != null)
  1233. next.PrevInAEL = edge2;
  1234. TEdge prev = edge2.PrevInAEL;
  1235. if (prev != null)
  1236. prev.NextInAEL = edge1;
  1237. edge1.PrevInAEL = prev;
  1238. edge1.NextInAEL = edge2;
  1239. edge2.PrevInAEL = edge1;
  1240. edge2.NextInAEL = next;
  1241. }
  1242. else
  1243. {
  1244. TEdge next = edge1.NextInAEL;
  1245. TEdge prev = edge1.PrevInAEL;
  1246. edge1.NextInAEL = edge2.NextInAEL;
  1247. if (edge1.NextInAEL != null)
  1248. edge1.NextInAEL.PrevInAEL = edge1;
  1249. edge1.PrevInAEL = edge2.PrevInAEL;
  1250. if (edge1.PrevInAEL != null)
  1251. edge1.PrevInAEL.NextInAEL = edge1;
  1252. edge2.NextInAEL = next;
  1253. if (edge2.NextInAEL != null)
  1254. edge2.NextInAEL.PrevInAEL = edge2;
  1255. edge2.PrevInAEL = prev;
  1256. if (edge2.PrevInAEL != null)
  1257. edge2.PrevInAEL.NextInAEL = edge2;
  1258. }
  1259. if (edge1.PrevInAEL == null)
  1260. m_ActiveEdges = edge1;
  1261. else if (edge2.PrevInAEL == null)
  1262. m_ActiveEdges = edge2;
  1263. }
  1264. //------------------------------------------------------------------------------
  1265. internal void DeleteFromAEL(TEdge e)
  1266. {
  1267. TEdge AelPrev = e.PrevInAEL;
  1268. TEdge AelNext = e.NextInAEL;
  1269. if (AelPrev == null && AelNext == null && (e != m_ActiveEdges))
  1270. return; //already deleted
  1271. if (AelPrev != null)
  1272. AelPrev.NextInAEL = AelNext;
  1273. else m_ActiveEdges = AelNext;
  1274. if (AelNext != null)
  1275. AelNext.PrevInAEL = AelPrev;
  1276. e.NextInAEL = null;
  1277. e.PrevInAEL = null;
  1278. }
  1279. //------------------------------------------------------------------------------
  1280. } //end ClipperBase
  1281. internal class Clipper : ClipperBase
  1282. {
  1283. //InitOptions that can be passed to the constructor ...
  1284. public const int ioReverseSolution = 1;
  1285. public const int ioStrictlySimple = 2;
  1286. public const int ioPreserveCollinear = 4;
  1287. private ClipTypes m_ClipType;
  1288. private Maxima m_Maxima;
  1289. private TEdge m_SortedEdges;
  1290. private List<IntersectNode> m_IntersectList;
  1291. IComparer<IntersectNode> m_IntersectNodeComparer;
  1292. private bool m_ExecuteLocked;
  1293. private PolyFillTypes m_ClipFillType;
  1294. private PolyFillTypes m_SubjFillType;
  1295. private List<Join> m_Joins;
  1296. private List<Join> m_GhostJoins;
  1297. private bool m_UsingPolyTree;
  1298. public Clipper(int InitOptions = 0) : base() //constructor
  1299. {
  1300. m_Scanbeam = null;
  1301. m_Maxima = null;
  1302. m_ActiveEdges = null;
  1303. m_SortedEdges = null;
  1304. m_IntersectList = new List<IntersectNode>();
  1305. m_IntersectNodeComparer = new MyIntersectNodeSort();
  1306. m_ExecuteLocked = false;
  1307. m_UsingPolyTree = false;
  1308. m_PolyOuts = new List<OutRec>();
  1309. m_Joins = new List<Join>();
  1310. m_GhostJoins = new List<Join>();
  1311. ReverseSolution = (ioReverseSolution & InitOptions) != 0;
  1312. StrictlySimple = (ioStrictlySimple & InitOptions) != 0;
  1313. PreserveCollinear = (ioPreserveCollinear & InitOptions) != 0;
  1314. }
  1315. //------------------------------------------------------------------------------
  1316. private void InsertMaxima(ClipInt X)
  1317. {
  1318. //double-linked list: sorted ascending, ignoring dups.
  1319. Maxima newMax = new Maxima();
  1320. newMax.X = X;
  1321. if (m_Maxima == null)
  1322. {
  1323. m_Maxima = newMax;
  1324. m_Maxima.Next = null;
  1325. m_Maxima.Prev = null;
  1326. }
  1327. else if (X < m_Maxima.X)
  1328. {
  1329. newMax.Next = m_Maxima;
  1330. newMax.Prev = null;
  1331. m_Maxima = newMax;
  1332. }
  1333. else
  1334. {
  1335. Maxima m = m_Maxima;
  1336. while (m.Next != null && (X >= m.Next.X)) m = m.Next;
  1337. if (X == m.X) return; //ie ignores duplicates (& CG to clean up newMax)
  1338. //insert newMax between m and m.Next ...
  1339. newMax.Next = m.Next;
  1340. newMax.Prev = m;
  1341. if (m.Next != null) m.Next.Prev = newMax;
  1342. m.Next = newMax;
  1343. }
  1344. }
  1345. //
  1346. public int LastIndex
  1347. {
  1348. get;
  1349. set;
  1350. }
  1351. //------------------------------------------------------------------------------
  1352. public bool ReverseSolution
  1353. {
  1354. get;
  1355. set;
  1356. }
  1357. //------------------------------------------------------------------------------
  1358. public bool StrictlySimple
  1359. {
  1360. get;
  1361. set;
  1362. }
  1363. //------------------------------------------------------------------------------
  1364. public bool Execute(ClipTypes clipType, Paths solution,
  1365. PolyFillTypes FillType = PolyFillTypes.pftEvenOdd)
  1366. {
  1367. return Execute(clipType, solution, FillType, FillType);
  1368. }
  1369. //------------------------------------------------------------------------------
  1370. public bool Execute(ClipTypes clipType, PolyTree polytree,
  1371. PolyFillTypes FillType = PolyFillTypes.pftEvenOdd)
  1372. {
  1373. return Execute(clipType, polytree, FillType, FillType);
  1374. }
  1375. //------------------------------------------------------------------------------
  1376. public bool Execute(ClipTypes clipType, Paths solution,
  1377. PolyFillTypes subjFillType, PolyFillTypes clipFillType)
  1378. {
  1379. if (m_ExecuteLocked) return false;
  1380. if (m_HasOpenPaths)
  1381. throw
  1382. new ClipperException("Error: PolyTree struct is needed for open path clipping.");
  1383. m_ExecuteLocked = true;
  1384. solution.Clear();
  1385. m_SubjFillType = subjFillType;
  1386. m_ClipFillType = clipFillType;
  1387. m_ClipType = clipType;
  1388. m_UsingPolyTree = false;
  1389. bool succeeded;
  1390. try
  1391. {
  1392. succeeded = ExecuteInternal();
  1393. //build the return polygons ...
  1394. if (succeeded) BuildResult(solution);
  1395. }
  1396. finally
  1397. {
  1398. DisposeAllPolyPts();
  1399. m_ExecuteLocked = false;
  1400. }
  1401. return succeeded;
  1402. }
  1403. //------------------------------------------------------------------------------
  1404. public bool Execute(ClipTypes clipType, PolyTree polytree,
  1405. PolyFillTypes subjFillType, PolyFillTypes clipFillType)
  1406. {
  1407. if (m_ExecuteLocked) return false;
  1408. m_ExecuteLocked = true;
  1409. m_SubjFillType = subjFillType;
  1410. m_ClipFillType = clipFillType;
  1411. m_ClipType = clipType;
  1412. m_UsingPolyTree = true;
  1413. bool succeeded;
  1414. try
  1415. {
  1416. succeeded = ExecuteInternal();
  1417. //build the return polygons ...
  1418. if (succeeded) BuildResult2(polytree);
  1419. }
  1420. finally
  1421. {
  1422. DisposeAllPolyPts();
  1423. m_ExecuteLocked = false;
  1424. }
  1425. return succeeded;
  1426. }
  1427. //------------------------------------------------------------------------------
  1428. internal void FixHoleLinkage(OutRec outRec)
  1429. {
  1430. //skip if an outermost polygon or
  1431. //already already points to the correct FirstLeft ...
  1432. if (outRec.FirstLeft == null ||
  1433. (outRec.IsHole != outRec.FirstLeft.IsHole &&
  1434. outRec.FirstLeft.Pts != null)) return;
  1435. OutRec orfl = outRec.FirstLeft;
  1436. while (orfl != null && ((orfl.IsHole == outRec.IsHole) || orfl.Pts == null))
  1437. orfl = orfl.FirstLeft;
  1438. outRec.FirstLeft = orfl;
  1439. }
  1440. //------------------------------------------------------------------------------
  1441. private bool ExecuteInternal()
  1442. {
  1443. try
  1444. {
  1445. Reset();
  1446. m_SortedEdges = null;
  1447. m_Maxima = null;
  1448. ClipInt botY, topY;
  1449. if (!PopScanbeam(out botY)) return false;
  1450. InsertLocalMinimaIntoAEL(botY);
  1451. while (PopScanbeam(out topY) || LocalMinimaPending())
  1452. {
  1453. ProcessHorizontals();
  1454. m_GhostJoins.Clear();
  1455. if (!ProcessIntersections(topY)) return false;
  1456. ProcessEdgesAtTopOfScanbeam(topY);
  1457. botY = topY;
  1458. InsertLocalMinimaIntoAEL(botY);
  1459. }
  1460. //fix orientations ...
  1461. foreach (OutRec outRec in m_PolyOuts)
  1462. {
  1463. if (outRec.Pts == null || outRec.IsOpen) continue;
  1464. if ((outRec.IsHole ^ ReverseSolution) == (Area(outRec) > 0))
  1465. ReversePolyPtLinks(outRec.Pts);
  1466. }
  1467. JoinCommonEdges();
  1468. foreach (OutRec outRec in m_PolyOuts)
  1469. {
  1470. if (outRec.Pts == null)
  1471. continue;
  1472. else if (outRec.IsOpen)
  1473. FixupOutPolyline(outRec);
  1474. else
  1475. FixupOutPolygon(outRec);
  1476. }
  1477. if (StrictlySimple) DoSimplePolygons();
  1478. return true;
  1479. }
  1480. //catch { return false; }
  1481. finally
  1482. {
  1483. m_Joins.Clear();
  1484. m_GhostJoins.Clear();
  1485. }
  1486. }
  1487. //------------------------------------------------------------------------------
  1488. private void DisposeAllPolyPts()
  1489. {
  1490. for (int i = 0; i < m_PolyOuts.Count; ++i) DisposeOutRec(i);
  1491. m_PolyOuts.Clear();
  1492. }
  1493. //------------------------------------------------------------------------------
  1494. private void AddJoin(OutPt Op1, OutPt Op2, IntPoint OffPt)
  1495. {
  1496. Join j = new Join();
  1497. j.OutPt1 = Op1;
  1498. j.OutPt2 = Op2;
  1499. j.OffPt = OffPt;
  1500. m_Joins.Add(j);
  1501. }
  1502. //------------------------------------------------------------------------------
  1503. private void AddGhostJoin(OutPt Op, IntPoint OffPt)
  1504. {
  1505. Join j = new Join();
  1506. j.OutPt1 = Op;
  1507. j.OffPt = OffPt;
  1508. m_GhostJoins.Add(j);
  1509. }
  1510. private void InsertLocalMinimaIntoAEL(ClipInt botY)
  1511. {
  1512. LocalMinima lm;
  1513. while (PopLocalMinima(botY, out lm))
  1514. {
  1515. TEdge lb = lm.LeftBound;
  1516. TEdge rb = lm.RightBound;
  1517. OutPt Op1 = null;
  1518. if (lb == null)
  1519. {
  1520. InsertEdgeIntoAEL(rb, null);
  1521. SetWindingCount(rb);
  1522. if (IsContributing(rb))
  1523. Op1 = AddOutPt(rb, rb.Bot);
  1524. }
  1525. else if (rb == null)
  1526. {
  1527. InsertEdgeIntoAEL(lb, null);
  1528. SetWindingCount(lb);
  1529. if (IsContributing(lb))
  1530. Op1 = AddOutPt(lb, lb.Bot);
  1531. InsertScanbeam(lb.Top.Y);
  1532. }
  1533. else
  1534. {
  1535. InsertEdgeIntoAEL(lb, null);
  1536. InsertEdgeIntoAEL(rb, lb);
  1537. SetWindingCount(lb);
  1538. rb.WindCnt = lb.WindCnt;
  1539. rb.WindCnt2 = lb.WindCnt2;
  1540. if (IsContributing(lb))
  1541. Op1 = AddLocalMinPoly(lb, rb, lb.Bot);
  1542. InsertScanbeam(lb.Top.Y);
  1543. }
  1544. if (rb != null)
  1545. {
  1546. if (IsHorizontal(rb))
  1547. {
  1548. if (rb.NextInLML != null)
  1549. InsertScanbeam(rb.NextInLML.Top.Y);
  1550. AddEdgeToSEL(rb);
  1551. }
  1552. else
  1553. InsertScanbeam(rb.Top.Y);
  1554. }
  1555. if (lb == null || rb == null) continue;
  1556. //if output polygons share an Edge with a horizontal rb, they'll need joining later ...
  1557. if (Op1 != null && IsHorizontal(rb) &&
  1558. m_GhostJoins.Count > 0 && rb.WindDelta != 0)
  1559. {
  1560. for (int i = 0; i < m_GhostJoins.Count; i++)
  1561. {
  1562. //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
  1563. //the 'ghost' join to a real join ready for later ...
  1564. Join j = m_GhostJoins[i];
  1565. if (HorzSegmentsOverlap(j.OutPt1.Pt.X, j.OffPt.X, rb.Bot.X, rb.Top.X))
  1566. AddJoin(j.OutPt1, Op1, j.OffPt);
  1567. }
  1568. }
  1569. if (lb.OutIdx >= 0 && lb.PrevInAEL != null &&
  1570. lb.PrevInAEL.Curr.X == lb.Bot.X &&
  1571. lb.PrevInAEL.OutIdx >= 0 &&
  1572. SlopesEqual(lb.PrevInAEL.Curr, lb.PrevInAEL.Top, lb.Curr, lb.Top, m_UseFullRange) &&
  1573. lb.WindDelta != 0 && lb.PrevInAEL.WindDelta != 0)
  1574. {
  1575. OutPt Op2 = AddOutPt(lb.PrevInAEL, lb.Bot);
  1576. AddJoin(Op1, Op2, lb.Top);
  1577. }
  1578. if (lb.NextInAEL != rb)
  1579. {
  1580. if (rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 &&
  1581. SlopesEqual(rb.PrevInAEL.Curr, rb.PrevInAEL.Top, rb.Curr, rb.Top, m_UseFullRange) &&
  1582. rb.WindDelta != 0 && rb.PrevInAEL.WindDelta != 0)
  1583. {
  1584. OutPt Op2 = AddOutPt(rb.PrevInAEL, rb.Bot);
  1585. AddJoin(Op1, Op2, rb.Top);
  1586. }
  1587. TEdge e = lb.NextInAEL;
  1588. if (e != null)
  1589. while (e != rb)
  1590. {
  1591. //nb: For calculating winding counts etc, IntersectEdges() assumes
  1592. //that param1 will be to the right of param2 ABOVE the intersection ...
  1593. IntersectEdges(rb, e, lb.Curr); //order important here
  1594. e = e.NextInAEL;
  1595. }
  1596. }
  1597. }
  1598. }
  1599. //------------------------------------------------------------------------------
  1600. private void InsertEdgeIntoAEL(TEdge edge, TEdge startEdge)
  1601. {
  1602. if (m_ActiveEdges == null)
  1603. {
  1604. edge.PrevInAEL = null;
  1605. edge.NextInAEL = null;
  1606. m_ActiveEdges = edge;
  1607. }
  1608. else if (startEdge == null && E2InsertsBeforeE1(m_ActiveEdges, edge))
  1609. {
  1610. edge.PrevInAEL = null;
  1611. edge.NextInAEL = m_ActiveEdges;
  1612. m_ActiveEdges.PrevInAEL = edge;
  1613. m_ActiveEdges = edge;
  1614. }
  1615. else
  1616. {
  1617. if (startEdge == null) startEdge = m_ActiveEdges;
  1618. while (startEdge.NextInAEL != null &&
  1619. !E2InsertsBeforeE1(startEdge.NextInAEL, edge))
  1620. startEdge = startEdge.NextInAEL;
  1621. edge.NextInAEL = startEdge.NextInAEL;
  1622. if (startEdge.NextInAEL != null) startEdge.NextInAEL.PrevInAEL = edge;
  1623. edge.PrevInAEL = startEdge;
  1624. startEdge.NextInAEL = edge;
  1625. }
  1626. }
  1627. //----------------------------------------------------------------------
  1628. private bool E2InsertsBeforeE1(TEdge e1, TEdge e2)
  1629. {
  1630. if (e2.Curr.X == e1.Curr.X)
  1631. {
  1632. if (e2.Top.Y > e1.Top.Y)
  1633. return e2.Top.X < TopX(e1, e2.Top.Y);
  1634. else return e1.Top.X > TopX(e2, e1.Top.Y);
  1635. }
  1636. else return e2.Curr.X < e1.Curr.X;
  1637. }
  1638. //------------------------------------------------------------------------------
  1639. private bool IsEvenOddFillType(TEdge edge)
  1640. {
  1641. if (edge.PolyTyp == PolyTypes.ptSubject)
  1642. return m_SubjFillType == PolyFillTypes.pftEvenOdd;
  1643. else
  1644. return m_ClipFillType == PolyFillTypes.pftEvenOdd;
  1645. }
  1646. //------------------------------------------------------------------------------
  1647. private bool IsEvenOddAltFillType(TEdge edge)
  1648. {
  1649. if (edge.PolyTyp == PolyTypes.ptSubject)
  1650. return m_ClipFillType == PolyFillTypes.pftEvenOdd;
  1651. else
  1652. return m_SubjFillType == PolyFillTypes.pftEvenOdd;
  1653. }
  1654. //------------------------------------------------------------------------------
  1655. private bool IsContributing(TEdge edge)
  1656. {
  1657. PolyFillTypes pft, pft2;
  1658. if (edge.PolyTyp == PolyTypes.ptSubject)
  1659. {
  1660. pft = m_SubjFillType;
  1661. pft2 = m_ClipFillType;
  1662. }
  1663. else
  1664. {
  1665. pft = m_ClipFillType;
  1666. pft2 = m_SubjFillType;
  1667. }
  1668. switch (pft)
  1669. {
  1670. case PolyFillTypes.pftEvenOdd:
  1671. //return false if a subj line has been flagged as inside a subj polygon
  1672. if (edge.WindDelta == 0 && edge.WindCnt != 1) return false;
  1673. break;
  1674. case PolyFillTypes.pftNonZero:
  1675. if (Math.Abs(edge.WindCnt) != 1) return false;
  1676. break;
  1677. case PolyFillTypes.pftPositive:
  1678. if (edge.WindCnt != 1) return false;
  1679. break;
  1680. default: //PolyFillTypes.pftNegative
  1681. if (edge.WindCnt != -1) return false;
  1682. break;
  1683. }
  1684. switch (m_ClipType)
  1685. {
  1686. case ClipTypes.ctIntersection:
  1687. switch (pft2)
  1688. {
  1689. case PolyFillTypes.pftEvenOdd:
  1690. case PolyFillTypes.pftNonZero:
  1691. return (edge.WindCnt2 != 0);
  1692. case PolyFillTypes.pftPositive:
  1693. return (edge.WindCnt2 > 0);
  1694. default:
  1695. return (edge.WindCnt2 < 0);
  1696. }
  1697. case ClipTypes.ctUnion:
  1698. switch (pft2)
  1699. {
  1700. case PolyFillTypes.pftEvenOdd:
  1701. case PolyFillTypes.pftNonZero:
  1702. return (edge.WindCnt2 == 0);
  1703. case PolyFillTypes.pftPositive:
  1704. return (edge.WindCnt2 <= 0);
  1705. default:
  1706. return (edge.WindCnt2 >= 0);
  1707. }
  1708. case ClipTypes.ctDifference:
  1709. if (edge.PolyTyp == PolyTypes.ptSubject)
  1710. switch (pft2)
  1711. {
  1712. case PolyFillTypes.pftEvenOdd:
  1713. case PolyFillTypes.pftNonZero:
  1714. return (edge.WindCnt2 == 0);
  1715. case PolyFillTypes.pftPositive:
  1716. return (edge.WindCnt2 <= 0);
  1717. default:
  1718. return (edge.WindCnt2 >= 0);
  1719. }
  1720. else
  1721. switch (pft2)
  1722. {
  1723. case PolyFillTypes.pftEvenOdd:
  1724. case PolyFillTypes.pftNonZero:
  1725. return (edge.WindCnt2 != 0);
  1726. case PolyFillTypes.pftPositive:
  1727. return (edge.WindCnt2 > 0);
  1728. default:
  1729. return (edge.WindCnt2 < 0);
  1730. }
  1731. case ClipTypes.ctXor:
  1732. if (edge.WindDelta == 0) //XOr always contributing unless open
  1733. switch (pft2)
  1734. {
  1735. case PolyFillTypes.pftEvenOdd:
  1736. case PolyFillTypes.pftNonZero:
  1737. return (edge.WindCnt2 == 0);
  1738. case PolyFillTypes.pftPositive:
  1739. return (edge.WindCnt2 <= 0);
  1740. default:
  1741. return (edge.WindCnt2 >= 0);
  1742. }
  1743. else
  1744. return true;
  1745. }
  1746. return true;
  1747. }
  1748. //------------------------------------------------------------------------------
  1749. private void SetWindingCount(TEdge edge)
  1750. {
  1751. TEdge e = edge.PrevInAEL;
  1752. //find the edge of the same polytype that immediately preceeds 'edge' in AEL
  1753. while (e != null && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta == 0))) e = e.PrevInAEL;
  1754. if (e == null)
  1755. {
  1756. PolyFillTypes pft;
  1757. pft = (edge.PolyTyp == PolyTypes.ptSubject ? m_SubjFillType : m_ClipFillType);
  1758. if (edge.WindDelta == 0) edge.WindCnt = (pft == PolyFillTypes.pftNegative ? -1 : 1);
  1759. else edge.WindCnt = edge.WindDelta;
  1760. edge.WindCnt2 = 0;
  1761. e = m_ActiveEdges; //ie get ready to calc WindCnt2
  1762. }
  1763. else if (edge.WindDelta == 0 && m_ClipType != ClipTypes.ctUnion)
  1764. {
  1765. edge.WindCnt = 1;
  1766. edge.WindCnt2 = e.WindCnt2;
  1767. e = e.NextInAEL; //ie get ready to calc WindCnt2
  1768. }
  1769. else if (IsEvenOddFillType(edge))
  1770. {
  1771. //EvenOdd filling ...
  1772. if (edge.WindDelta == 0)
  1773. {
  1774. //are we inside a subj polygon ...
  1775. bool Inside = true;
  1776. TEdge e2 = e.PrevInAEL;
  1777. while (e2 != null)
  1778. {
  1779. if (e2.PolyTyp == e.PolyTyp && e2.WindDelta != 0)
  1780. Inside = !Inside;
  1781. e2 = e2.PrevInAEL;
  1782. }
  1783. edge.WindCnt = (Inside ? 0 : 1);
  1784. }
  1785. else
  1786. {
  1787. edge.WindCnt = edge.WindDelta;
  1788. }
  1789. edge.WindCnt2 = e.WindCnt2;
  1790. e = e.NextInAEL; //ie get ready to calc WindCnt2
  1791. }
  1792. else
  1793. {
  1794. //nonZero, Positive or Negative filling ...
  1795. if (e.WindCnt * e.WindDelta < 0)
  1796. {
  1797. //prev edge is 'decreasing' WindCount (WC) toward zero
  1798. //so we're outside the previous polygon ...
  1799. if (Math.Abs(e.WindCnt) > 1)
  1800. {
  1801. //outside prev poly but still inside another.
  1802. //when reversing direction of prev poly use the same WC
  1803. if (e.WindDelta * edge.WindDelta < 0) edge.WindCnt = e.WindCnt;
  1804. //otherwise continue to 'decrease' WC ...
  1805. else edge.WindCnt = e.WindCnt + edge.WindDelta;
  1806. }
  1807. else
  1808. //now outside all polys of same polytype so set own WC ...
  1809. edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
  1810. }
  1811. else
  1812. {
  1813. //prev edge is 'increasing' WindCount (WC) away from zero
  1814. //so we're inside the previous polygon ...
  1815. if (edge.WindDelta == 0)
  1816. edge.WindCnt = (e.WindCnt < 0 ? e.WindCnt - 1 : e.WindCnt + 1);
  1817. //if wind direction is reversing prev then use same WC
  1818. else if (e.WindDelta * edge.WindDelta < 0)
  1819. edge.WindCnt = e.WindCnt;
  1820. //otherwise add to WC ...
  1821. else edge.WindCnt = e.WindCnt + edge.WindDelta;
  1822. }
  1823. edge.WindCnt2 = e.WindCnt2;
  1824. e = e.NextInAEL; //ie get ready to calc WindCnt2
  1825. }
  1826. //update WindCnt2 ...
  1827. if (IsEvenOddAltFillType(edge))
  1828. {
  1829. //EvenOdd filling ...
  1830. while (e != edge)
  1831. {
  1832. if (e.WindDelta != 0)
  1833. edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);
  1834. e = e.NextInAEL;
  1835. }
  1836. }
  1837. else
  1838. {
  1839. //nonZero, Positive or Negative filling ...
  1840. while (e != edge)
  1841. {
  1842. edge.WindCnt2 += e.WindDelta;
  1843. e = e.NextInAEL;
  1844. }
  1845. }
  1846. }
  1847. //------------------------------------------------------------------------------
  1848. private void AddEdgeToSEL(TEdge edge)
  1849. {
  1850. //SEL pointers in PEdge are use to build transient lists of horizontal edges.
  1851. //However, since we don't need to worry about processing order, all additions
  1852. //are made to the front of the list ...
  1853. if (m_SortedEdges == null)
  1854. {
  1855. m_SortedEdges = edge;
  1856. edge.PrevInSEL = null;
  1857. edge.NextInSEL = null;
  1858. }
  1859. else
  1860. {
  1861. edge.NextInSEL = m_SortedEdges;
  1862. edge.PrevInSEL = null;
  1863. m_SortedEdges.PrevInSEL = edge;
  1864. m_SortedEdges = edge;
  1865. }
  1866. }
  1867. //------------------------------------------------------------------------------
  1868. internal Boolean PopEdgeFromSEL(out TEdge e)
  1869. {
  1870. //Pop edge from front of SEL (ie SEL is a FILO list)
  1871. e = m_SortedEdges;
  1872. if (e == null) return false;
  1873. TEdge oldE = e;
  1874. m_SortedEdges = e.NextInSEL;
  1875. if (m_SortedEdges != null) m_SortedEdges.PrevInSEL = null;
  1876. oldE.NextInSEL = null;
  1877. oldE.PrevInSEL = null;
  1878. return true;
  1879. }
  1880. //------------------------------------------------------------------------------
  1881. private void CopyAELToSEL()
  1882. {
  1883. TEdge e = m_ActiveEdges;
  1884. m_SortedEdges = e;
  1885. while (e != null)
  1886. {
  1887. e.PrevInSEL = e.PrevInAEL;
  1888. e.NextInSEL = e.NextInAEL;
  1889. e = e.NextInAEL;
  1890. }
  1891. }
  1892. //------------------------------------------------------------------------------
  1893. private void SwapPositionsInSEL(TEdge edge1, TEdge edge2)
  1894. {
  1895. if (edge1.NextInSEL == null && edge1.PrevInSEL == null)
  1896. return;
  1897. if (edge2.NextInSEL == null && edge2.PrevInSEL == null)
  1898. return;
  1899. if (edge1.NextInSEL == edge2)
  1900. {
  1901. TEdge next = edge2.NextInSEL;
  1902. if (next != null)
  1903. next.PrevInSEL = edge1;
  1904. TEdge prev = edge1.PrevInSEL;
  1905. if (prev != null)
  1906. prev.NextInSEL = edge2;
  1907. edge2.PrevInSEL = prev;
  1908. edge2.NextInSEL = edge1;
  1909. edge1.PrevInSEL = edge2;
  1910. edge1.NextInSEL = next;
  1911. }
  1912. else if (edge2.NextInSEL == edge1)
  1913. {
  1914. TEdge next = edge1.NextInSEL;
  1915. if (next != null)
  1916. next.PrevInSEL = edge2;
  1917. TEdge prev = edge2.PrevInSEL;
  1918. if (prev != null)
  1919. prev.NextInSEL = edge1;
  1920. edge1.PrevInSEL = prev;
  1921. edge1.NextInSEL = edge2;
  1922. edge2.PrevInSEL = edge1;
  1923. edge2.NextInSEL = next;
  1924. }
  1925. else
  1926. {
  1927. TEdge next = edge1.NextInSEL;
  1928. TEdge prev = edge1.PrevInSEL;
  1929. edge1.NextInSEL = edge2.NextInSEL;
  1930. if (edge1.NextInSEL != null)
  1931. edge1.NextInSEL.PrevInSEL = edge1;
  1932. edge1.PrevInSEL = edge2.PrevInSEL;
  1933. if (edge1.PrevInSEL != null)
  1934. edge1.PrevInSEL.NextInSEL = edge1;
  1935. edge2.NextInSEL = next;
  1936. if (edge2.NextInSEL != null)
  1937. edge2.NextInSEL.PrevInSEL = edge2;
  1938. edge2.PrevInSEL = prev;
  1939. if (edge2.PrevInSEL != null)
  1940. edge2.PrevInSEL.NextInSEL = edge2;
  1941. }
  1942. if (edge1.PrevInSEL == null)
  1943. m_SortedEdges = edge1;
  1944. else if (edge2.PrevInSEL == null)
  1945. m_SortedEdges = edge2;
  1946. }
  1947. //------------------------------------------------------------------------------
  1948. private void AddLocalMaxPoly(TEdge e1, TEdge e2, IntPoint pt)
  1949. {
  1950. AddOutPt(e1, pt);
  1951. if (e2.WindDelta == 0) AddOutPt(e2, pt);
  1952. if (e1.OutIdx == e2.OutIdx)
  1953. {
  1954. e1.OutIdx = Unassigned;
  1955. e2.OutIdx = Unassigned;
  1956. }
  1957. else if (e1.OutIdx < e2.OutIdx)
  1958. AppendPolygon(e1, e2);
  1959. else
  1960. AppendPolygon(e2, e1);
  1961. }
  1962. //------------------------------------------------------------------------------
  1963. private OutPt AddLocalMinPoly(TEdge e1, TEdge e2, IntPoint pt)
  1964. {
  1965. OutPt result;
  1966. TEdge e, prevE;
  1967. if (IsHorizontal(e2) || (e1.Dx > e2.Dx))
  1968. {
  1969. result = AddOutPt(e1, pt);
  1970. e2.OutIdx = e1.OutIdx;
  1971. e1.Side = EdgeSides.esLeft;
  1972. e2.Side = EdgeSides.esRight;
  1973. e = e1;
  1974. if (e.PrevInAEL == e2)
  1975. prevE = e2.PrevInAEL;
  1976. else
  1977. prevE = e.PrevInAEL;
  1978. }
  1979. else
  1980. {
  1981. result = AddOutPt(e2, pt);
  1982. e1.OutIdx = e2.OutIdx;
  1983. e1.Side = EdgeSides.esRight;
  1984. e2.Side = EdgeSides.esLeft;
  1985. e = e2;
  1986. if (e.PrevInAEL == e1)
  1987. prevE = e1.PrevInAEL;
  1988. else
  1989. prevE = e.PrevInAEL;
  1990. }
  1991. if (prevE != null && prevE.OutIdx >= 0 && prevE.Top.Y < pt.Y && e.Top.Y < pt.Y)
  1992. {
  1993. ClipInt xPrev = TopX(prevE, pt.Y);
  1994. ClipInt xE = TopX(e, pt.Y);
  1995. if ((xPrev == xE) && (e.WindDelta != 0) && (prevE.WindDelta != 0) &&
  1996. SlopesEqual(new IntPoint(xPrev, pt.Y), prevE.Top, new IntPoint(xE, pt.Y), e.Top, m_UseFullRange))
  1997. {
  1998. OutPt outPt = AddOutPt(prevE, pt);
  1999. AddJoin(result, outPt, e.Top);
  2000. }
  2001. }
  2002. return result;
  2003. }
  2004. //------------------------------------------------------------------------------
  2005. private OutPt AddOutPt(TEdge e, IntPoint pt)
  2006. {
  2007. if (e.OutIdx < 0)
  2008. {
  2009. OutRec outRec = CreateOutRec();
  2010. outRec.IsOpen = (e.WindDelta == 0);
  2011. OutPt newOp = new OutPt();
  2012. outRec.Pts = newOp;
  2013. newOp.Idx = outRec.Idx;
  2014. newOp.Pt = pt;
  2015. newOp.Next = newOp;
  2016. newOp.Prev = newOp;
  2017. if (!outRec.IsOpen)
  2018. SetHoleState(e, outRec);
  2019. e.OutIdx = outRec.Idx; //nb: do this after SetZ !
  2020. return newOp;
  2021. }
  2022. else
  2023. {
  2024. OutRec outRec = m_PolyOuts[e.OutIdx];
  2025. //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
  2026. OutPt op = outRec.Pts;
  2027. bool ToFront = (e.Side == EdgeSides.esLeft);
  2028. if (ToFront && pt == op.Pt) return op;
  2029. else if (!ToFront && pt == op.Prev.Pt) return op.Prev;
  2030. OutPt newOp = new OutPt();
  2031. newOp.Idx = outRec.Idx;
  2032. newOp.Pt = pt;
  2033. newOp.Next = op;
  2034. newOp.Prev = op.Prev;
  2035. newOp.Prev.Next = newOp;
  2036. op.Prev = newOp;
  2037. if (ToFront) outRec.Pts = newOp;
  2038. return newOp;
  2039. }
  2040. }
  2041. //------------------------------------------------------------------------------
  2042. private OutPt GetLastOutPt(TEdge e)
  2043. {
  2044. OutRec outRec = m_PolyOuts[e.OutIdx];
  2045. if (e.Side == EdgeSides.esLeft)
  2046. return outRec.Pts;
  2047. else
  2048. return outRec.Pts.Prev;
  2049. }
  2050. //------------------------------------------------------------------------------
  2051. internal void SwapPoints(ref IntPoint pt1, ref IntPoint pt2)
  2052. {
  2053. IntPoint tmp = new IntPoint(pt1);
  2054. pt1 = pt2;
  2055. pt2 = tmp;
  2056. }
  2057. //------------------------------------------------------------------------------
  2058. private bool HorzSegmentsOverlap(ClipInt seg1a, ClipInt seg1b, ClipInt seg2a, ClipInt seg2b)
  2059. {
  2060. if (seg1a > seg1b) Swap(ref seg1a, ref seg1b);
  2061. if (seg2a > seg2b) Swap(ref seg2a, ref seg2b);
  2062. return (seg1a < seg2b) && (seg2a < seg1b);
  2063. }
  2064. //------------------------------------------------------------------------------
  2065. private void SetHoleState(TEdge e, OutRec outRec)
  2066. {
  2067. TEdge e2 = e.PrevInAEL;
  2068. TEdge eTmp = null;
  2069. while (e2 != null)
  2070. {
  2071. if (e2.OutIdx >= 0 && e2.WindDelta != 0)
  2072. {
  2073. if (eTmp == null)
  2074. eTmp = e2;
  2075. else if (eTmp.OutIdx == e2.OutIdx)
  2076. eTmp = null; //paired
  2077. }
  2078. e2 = e2.PrevInAEL;
  2079. }
  2080. if (eTmp == null)
  2081. {
  2082. outRec.FirstLeft = null;
  2083. outRec.IsHole = false;
  2084. }
  2085. else
  2086. {
  2087. outRec.FirstLeft = m_PolyOuts[eTmp.OutIdx];
  2088. outRec.IsHole = !outRec.FirstLeft.IsHole;
  2089. }
  2090. }
  2091. //------------------------------------------------------------------------------
  2092. private double GetDx(IntPoint pt1, IntPoint pt2)
  2093. {
  2094. if (pt1.Y == pt2.Y) return horizontal;
  2095. else return (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);
  2096. }
  2097. //---------------------------------------------------------------------------
  2098. private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2)
  2099. {
  2100. OutPt p = btmPt1.Prev;
  2101. while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Prev;
  2102. double dx1p = Math.Abs(GetDx(btmPt1.Pt, p.Pt));
  2103. p = btmPt1.Next;
  2104. while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Next;
  2105. double dx1n = Math.Abs(GetDx(btmPt1.Pt, p.Pt));
  2106. p = btmPt2.Prev;
  2107. while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Prev;
  2108. double dx2p = Math.Abs(GetDx(btmPt2.Pt, p.Pt));
  2109. p = btmPt2.Next;
  2110. while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Next;
  2111. double dx2n = Math.Abs(GetDx(btmPt2.Pt, p.Pt));
  2112. if (Math.Max(dx1p, dx1n) == Math.Max(dx2p, dx2n) &&
  2113. Math.Min(dx1p, dx1n) == Math.Min(dx2p, dx2n))
  2114. return Area(btmPt1) > 0; //if otherwise identical use orientation
  2115. else
  2116. return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
  2117. }
  2118. //------------------------------------------------------------------------------
  2119. private OutPt GetBottomPt(OutPt pp)
  2120. {
  2121. OutPt dups = null;
  2122. OutPt p = pp.Next;
  2123. while (p != pp)
  2124. {
  2125. if (p.Pt.Y > pp.Pt.Y)
  2126. {
  2127. pp = p;
  2128. dups = null;
  2129. }
  2130. else if (p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X)
  2131. {
  2132. if (p.Pt.X < pp.Pt.X)
  2133. {
  2134. dups = null;
  2135. pp = p;
  2136. }
  2137. else
  2138. {
  2139. if (p.Next != pp && p.Prev != pp) dups = p;
  2140. }
  2141. }
  2142. p = p.Next;
  2143. }
  2144. if (dups != null)
  2145. {
  2146. //there appears to be at least 2 vertices at bottomPt so ...
  2147. while (dups != p)
  2148. {
  2149. if (!FirstIsBottomPt(p, dups)) pp = dups;
  2150. dups = dups.Next;
  2151. while (dups.Pt != pp.Pt) dups = dups.Next;
  2152. }
  2153. }
  2154. return pp;
  2155. }
  2156. //------------------------------------------------------------------------------
  2157. private OutRec GetLowermostRec(OutRec outRec1, OutRec outRec2)
  2158. {
  2159. //work out which polygon fragment has the correct hole state ...
  2160. if (outRec1.BottomPt == null)
  2161. outRec1.BottomPt = GetBottomPt(outRec1.Pts);
  2162. if (outRec2.BottomPt == null)
  2163. outRec2.BottomPt = GetBottomPt(outRec2.Pts);
  2164. OutPt bPt1 = outRec1.BottomPt;
  2165. OutPt bPt2 = outRec2.BottomPt;
  2166. if (bPt1.Pt.Y > bPt2.Pt.Y) return outRec1;
  2167. else if (bPt1.Pt.Y < bPt2.Pt.Y) return outRec2;
  2168. else if (bPt1.Pt.X < bPt2.Pt.X) return outRec1;
  2169. else if (bPt1.Pt.X > bPt2.Pt.X) return outRec2;
  2170. else if (bPt1.Next == bPt1) return outRec2;
  2171. else if (bPt2.Next == bPt2) return outRec1;
  2172. else if (FirstIsBottomPt(bPt1, bPt2)) return outRec1;
  2173. else return outRec2;
  2174. }
  2175. //------------------------------------------------------------------------------
  2176. bool OutRec1RightOfOutRec2(OutRec outRec1, OutRec outRec2)
  2177. {
  2178. do
  2179. {
  2180. outRec1 = outRec1.FirstLeft;
  2181. if (outRec1 == outRec2) return true;
  2182. }
  2183. while (outRec1 != null);
  2184. return false;
  2185. }
  2186. //------------------------------------------------------------------------------
  2187. private OutRec GetOutRec(int idx)
  2188. {
  2189. OutRec outrec = m_PolyOuts[idx];
  2190. while (outrec != m_PolyOuts[outrec.Idx])
  2191. outrec = m_PolyOuts[outrec.Idx];
  2192. return outrec;
  2193. }
  2194. //------------------------------------------------------------------------------
  2195. private void AppendPolygon(TEdge e1, TEdge e2)
  2196. {
  2197. OutRec outRec1 = m_PolyOuts[e1.OutIdx];
  2198. OutRec outRec2 = m_PolyOuts[e2.OutIdx];
  2199. OutRec holeStateRec;
  2200. if (OutRec1RightOfOutRec2(outRec1, outRec2))
  2201. holeStateRec = outRec2;
  2202. else if (OutRec1RightOfOutRec2(outRec2, outRec1))
  2203. holeStateRec = outRec1;
  2204. else
  2205. holeStateRec = GetLowermostRec(outRec1, outRec2);
  2206. //get the start and ends of both output polygons and
  2207. //join E2 poly onto E1 poly and delete pointers to E2 ...
  2208. OutPt p1_lft = outRec1.Pts;
  2209. OutPt p1_rt = p1_lft.Prev;
  2210. OutPt p2_lft = outRec2.Pts;
  2211. OutPt p2_rt = p2_lft.Prev;
  2212. //join e2 poly onto e1 poly and delete pointers to e2 ...
  2213. if (e1.Side == EdgeSides.esLeft)
  2214. {
  2215. if (e2.Side == EdgeSides.esLeft)
  2216. {
  2217. //z y x a b c
  2218. ReversePolyPtLinks(p2_lft);
  2219. p2_lft.Next = p1_lft;
  2220. p1_lft.Prev = p2_lft;
  2221. p1_rt.Next = p2_rt;
  2222. p2_rt.Prev = p1_rt;
  2223. outRec1.Pts = p2_rt;
  2224. }
  2225. else
  2226. {
  2227. //x y z a b c
  2228. p2_rt.Next = p1_lft;
  2229. p1_lft.Prev = p2_rt;
  2230. p2_lft.Prev = p1_rt;
  2231. p1_rt.Next = p2_lft;
  2232. outRec1.Pts = p2_lft;
  2233. }
  2234. }
  2235. else
  2236. {
  2237. if (e2.Side == EdgeSides.esRight)
  2238. {
  2239. //a b c z y x
  2240. ReversePolyPtLinks(p2_lft);
  2241. p1_rt.Next = p2_rt;
  2242. p2_rt.Prev = p1_rt;
  2243. p2_lft.Next = p1_lft;
  2244. p1_lft.Prev = p2_lft;
  2245. }
  2246. else
  2247. {
  2248. //a b c x y z
  2249. p1_rt.Next = p2_lft;
  2250. p2_lft.Prev = p1_rt;
  2251. p1_lft.Prev = p2_rt;
  2252. p2_rt.Next = p1_lft;
  2253. }
  2254. }
  2255. outRec1.BottomPt = null;
  2256. if (holeStateRec == outRec2)
  2257. {
  2258. if (outRec2.FirstLeft != outRec1)
  2259. outRec1.FirstLeft = outRec2.FirstLeft;
  2260. outRec1.IsHole = outRec2.IsHole;
  2261. }
  2262. outRec2.Pts = null;
  2263. outRec2.BottomPt = null;
  2264. outRec2.FirstLeft = outRec1;
  2265. int OKIdx = e1.OutIdx;
  2266. int ObsoleteIdx = e2.OutIdx;
  2267. e1.OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly
  2268. e2.OutIdx = Unassigned;
  2269. TEdge e = m_ActiveEdges;
  2270. while (e != null)
  2271. {
  2272. if (e.OutIdx == ObsoleteIdx)
  2273. {
  2274. e.OutIdx = OKIdx;
  2275. e.Side = e1.Side;
  2276. break;
  2277. }
  2278. e = e.NextInAEL;
  2279. }
  2280. outRec2.Idx = outRec1.Idx;
  2281. }
  2282. //------------------------------------------------------------------------------
  2283. private void ReversePolyPtLinks(OutPt pp)
  2284. {
  2285. if (pp == null) return;
  2286. OutPt pp1;
  2287. OutPt pp2;
  2288. pp1 = pp;
  2289. do
  2290. {
  2291. pp2 = pp1.Next;
  2292. pp1.Next = pp1.Prev;
  2293. pp1.Prev = pp2;
  2294. pp1 = pp2;
  2295. }
  2296. while (pp1 != pp);
  2297. }
  2298. //------------------------------------------------------------------------------
  2299. private static void SwapSides(TEdge edge1, TEdge edge2)
  2300. {
  2301. EdgeSides side = edge1.Side;
  2302. edge1.Side = edge2.Side;
  2303. edge2.Side = side;
  2304. }
  2305. //------------------------------------------------------------------------------
  2306. private static void SwapPolyIndexes(TEdge edge1, TEdge edge2)
  2307. {
  2308. int outIdx = edge1.OutIdx;
  2309. edge1.OutIdx = edge2.OutIdx;
  2310. edge2.OutIdx = outIdx;
  2311. }
  2312. //------------------------------------------------------------------------------
  2313. private void IntersectEdges(TEdge e1, TEdge e2, IntPoint pt)
  2314. {
  2315. //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before
  2316. //e2 in AEL except when e1 is being inserted at the intersection point ...
  2317. bool e1Contributing = (e1.OutIdx >= 0);
  2318. bool e2Contributing = (e2.OutIdx >= 0);
  2319. #if use_lines
  2320. //if either edge is on an OPEN path ...
  2321. if (e1.WindDelta == 0 || e2.WindDelta == 0)
  2322. {
  2323. //ignore subject-subject open path intersections UNLESS they
  2324. //are both open paths, AND they are both 'contributing maximas' ...
  2325. if (e1.WindDelta == 0 && e2.WindDelta == 0) return;
  2326. //if intersecting a subj line with a subj poly ...
  2327. else if (e1.PolyTyp == e2.PolyTyp &&
  2328. e1.WindDelta != e2.WindDelta && m_ClipType == ClipTypes.ctUnion)
  2329. {
  2330. if (e1.WindDelta == 0)
  2331. {
  2332. if (e2Contributing)
  2333. {
  2334. AddOutPt(e1, pt);
  2335. if (e1Contributing) e1.OutIdx = Unassigned;
  2336. }
  2337. }
  2338. else
  2339. {
  2340. if (e1Contributing)
  2341. {
  2342. AddOutPt(e2, pt);
  2343. if (e2Contributing) e2.OutIdx = Unassigned;
  2344. }
  2345. }
  2346. }
  2347. else if (e1.PolyTyp != e2.PolyTyp)
  2348. {
  2349. if ((e1.WindDelta == 0) && Math.Abs(e2.WindCnt) == 1 &&
  2350. (m_ClipType != ClipTypes.ctUnion || e2.WindCnt2 == 0))
  2351. {
  2352. AddOutPt(e1, pt);
  2353. if (e1Contributing) e1.OutIdx = Unassigned;
  2354. }
  2355. else if ((e2.WindDelta == 0) && (Math.Abs(e1.WindCnt) == 1) &&
  2356. (m_ClipType != ClipTypes.ctUnion || e1.WindCnt2 == 0))
  2357. {
  2358. AddOutPt(e2, pt);
  2359. if (e2Contributing) e2.OutIdx = Unassigned;
  2360. }
  2361. }
  2362. return;
  2363. }
  2364. #endif
  2365. //update winding counts...
  2366. //assumes that e1 will be to the Right of e2 ABOVE the intersection
  2367. if (e1.PolyTyp == e2.PolyTyp)
  2368. {
  2369. if (IsEvenOddFillType(e1))
  2370. {
  2371. int oldE1WindCnt = e1.WindCnt;
  2372. e1.WindCnt = e2.WindCnt;
  2373. e2.WindCnt = oldE1WindCnt;
  2374. }
  2375. else
  2376. {
  2377. if (e1.WindCnt + e2.WindDelta == 0) e1.WindCnt = -e1.WindCnt;
  2378. else e1.WindCnt += e2.WindDelta;
  2379. if (e2.WindCnt - e1.WindDelta == 0) e2.WindCnt = -e2.WindCnt;
  2380. else e2.WindCnt -= e1.WindDelta;
  2381. }
  2382. }
  2383. else
  2384. {
  2385. if (!IsEvenOddFillType(e2)) e1.WindCnt2 += e2.WindDelta;
  2386. else e1.WindCnt2 = (e1.WindCnt2 == 0) ? 1 : 0;
  2387. if (!IsEvenOddFillType(e1)) e2.WindCnt2 -= e1.WindDelta;
  2388. else e2.WindCnt2 = (e2.WindCnt2 == 0) ? 1 : 0;
  2389. }
  2390. PolyFillTypes e1FillType, e2FillType, e1FillType2, e2FillType2;
  2391. if (e1.PolyTyp == PolyTypes.ptSubject)
  2392. {
  2393. e1FillType = m_SubjFillType;
  2394. e1FillType2 = m_ClipFillType;
  2395. }
  2396. else
  2397. {
  2398. e1FillType = m_ClipFillType;
  2399. e1FillType2 = m_SubjFillType;
  2400. }
  2401. if (e2.PolyTyp == PolyTypes.ptSubject)
  2402. {
  2403. e2FillType = m_SubjFillType;
  2404. e2FillType2 = m_ClipFillType;
  2405. }
  2406. else
  2407. {
  2408. e2FillType = m_ClipFillType;
  2409. e2FillType2 = m_SubjFillType;
  2410. }
  2411. int e1Wc, e2Wc;
  2412. switch (e1FillType)
  2413. {
  2414. case PolyFillTypes.pftPositive: e1Wc = e1.WindCnt; break;
  2415. case PolyFillTypes.pftNegative: e1Wc = -e1.WindCnt; break;
  2416. default: e1Wc = Math.Abs(e1.WindCnt); break;
  2417. }
  2418. switch (e2FillType)
  2419. {
  2420. case PolyFillTypes.pftPositive: e2Wc = e2.WindCnt; break;
  2421. case PolyFillTypes.pftNegative: e2Wc = -e2.WindCnt; break;
  2422. default: e2Wc = Math.Abs(e2.WindCnt); break;
  2423. }
  2424. if (e1Contributing && e2Contributing)
  2425. {
  2426. if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
  2427. (e1.PolyTyp != e2.PolyTyp && m_ClipType != ClipTypes.ctXor))
  2428. {
  2429. AddLocalMaxPoly(e1, e2, pt);
  2430. }
  2431. else
  2432. {
  2433. AddOutPt(e1, pt);
  2434. AddOutPt(e2, pt);
  2435. SwapSides(e1, e2);
  2436. SwapPolyIndexes(e1, e2);
  2437. }
  2438. }
  2439. else if (e1Contributing)
  2440. {
  2441. if (e2Wc == 0 || e2Wc == 1)
  2442. {
  2443. AddOutPt(e1, pt);
  2444. SwapSides(e1, e2);
  2445. SwapPolyIndexes(e1, e2);
  2446. }
  2447. }
  2448. else if (e2Contributing)
  2449. {
  2450. if (e1Wc == 0 || e1Wc == 1)
  2451. {
  2452. AddOutPt(e2, pt);
  2453. SwapSides(e1, e2);
  2454. SwapPolyIndexes(e1, e2);
  2455. }
  2456. }
  2457. else if ((e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))
  2458. {
  2459. //neither edge is currently contributing ...
  2460. ClipInt e1Wc2, e2Wc2;
  2461. switch (e1FillType2)
  2462. {
  2463. case PolyFillTypes.pftPositive: e1Wc2 = e1.WindCnt2; break;
  2464. case PolyFillTypes.pftNegative: e1Wc2 = -e1.WindCnt2; break;
  2465. default: e1Wc2 = Math.Abs(e1.WindCnt2); break;
  2466. }
  2467. switch (e2FillType2)
  2468. {
  2469. case PolyFillTypes.pftPositive: e2Wc2 = e2.WindCnt2; break;
  2470. case PolyFillTypes.pftNegative: e2Wc2 = -e2.WindCnt2; break;
  2471. default: e2Wc2 = Math.Abs(e2.WindCnt2); break;
  2472. }
  2473. if (e1.PolyTyp != e2.PolyTyp)
  2474. {
  2475. AddLocalMinPoly(e1, e2, pt);
  2476. }
  2477. else if (e1Wc == 1 && e2Wc == 1)
  2478. switch (m_ClipType)
  2479. {
  2480. case ClipTypes.ctIntersection:
  2481. if (e1Wc2 > 0 && e2Wc2 > 0)
  2482. AddLocalMinPoly(e1, e2, pt);
  2483. break;
  2484. case ClipTypes.ctUnion:
  2485. if (e1Wc2 <= 0 && e2Wc2 <= 0)
  2486. AddLocalMinPoly(e1, e2, pt);
  2487. break;
  2488. case ClipTypes.ctDifference:
  2489. if (((e1.PolyTyp == PolyTypes.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
  2490. ((e1.PolyTyp == PolyTypes.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
  2491. AddLocalMinPoly(e1, e2, pt);
  2492. break;
  2493. case ClipTypes.ctXor:
  2494. AddLocalMinPoly(e1, e2, pt);
  2495. break;
  2496. }
  2497. else
  2498. SwapSides(e1, e2);
  2499. }
  2500. }
  2501. //------------------------------------------------------------------------------
  2502. private void DeleteFromSEL(TEdge e)
  2503. {
  2504. TEdge SelPrev = e.PrevInSEL;
  2505. TEdge SelNext = e.NextInSEL;
  2506. if (SelPrev == null && SelNext == null && (e != m_SortedEdges))
  2507. return; //already deleted
  2508. if (SelPrev != null)
  2509. SelPrev.NextInSEL = SelNext;
  2510. else m_SortedEdges = SelNext;
  2511. if (SelNext != null)
  2512. SelNext.PrevInSEL = SelPrev;
  2513. e.NextInSEL = null;
  2514. e.PrevInSEL = null;
  2515. }
  2516. //------------------------------------------------------------------------------
  2517. private void ProcessHorizontals()
  2518. {
  2519. TEdge horzEdge; //m_SortedEdges;
  2520. while (PopEdgeFromSEL(out horzEdge))
  2521. ProcessHorizontal(horzEdge);
  2522. }
  2523. //------------------------------------------------------------------------------
  2524. void GetHorzDirection(TEdge HorzEdge, out Directions Dir, out ClipInt Left, out ClipInt Right)
  2525. {
  2526. if (HorzEdge.Bot.X < HorzEdge.Top.X)
  2527. {
  2528. Left = HorzEdge.Bot.X;
  2529. Right = HorzEdge.Top.X;
  2530. Dir = Directions.dLeftToRight;
  2531. }
  2532. else
  2533. {
  2534. Left = HorzEdge.Top.X;
  2535. Right = HorzEdge.Bot.X;
  2536. Dir = Directions.dRightToLeft;
  2537. }
  2538. }
  2539. //------------------------------------------------------------------------
  2540. private void ProcessHorizontal(TEdge horzEdge)
  2541. {
  2542. Directions dir;
  2543. ClipInt horzLeft, horzRight;
  2544. bool IsOpen = horzEdge.WindDelta == 0;
  2545. GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight);
  2546. TEdge eLastHorz = horzEdge, eMaxPair = null;
  2547. while (eLastHorz.NextInLML != null && IsHorizontal(eLastHorz.NextInLML))
  2548. eLastHorz = eLastHorz.NextInLML;
  2549. if (eLastHorz.NextInLML == null)
  2550. eMaxPair = GetMaximaPair(eLastHorz);
  2551. Maxima currMax = m_Maxima;
  2552. if (currMax != null)
  2553. {
  2554. //get the first maxima in range (X) ...
  2555. if (dir == Directions.dLeftToRight)
  2556. {
  2557. while (currMax != null && currMax.X <= horzEdge.Bot.X)
  2558. currMax = currMax.Next;
  2559. if (currMax != null && currMax.X >= eLastHorz.Top.X)
  2560. currMax = null;
  2561. }
  2562. else
  2563. {
  2564. while (currMax.Next != null && currMax.Next.X < horzEdge.Bot.X)
  2565. currMax = currMax.Next;
  2566. if (currMax.X <= eLastHorz.Top.X) currMax = null;
  2567. }
  2568. }
  2569. OutPt op1 = null;
  2570. for (;;) //loop through consec. horizontal edges
  2571. {
  2572. bool IsLastHorz = (horzEdge == eLastHorz);
  2573. TEdge e = GetNextInAEL(horzEdge, dir);
  2574. while (e != null)
  2575. {
  2576. //this code block inserts extra coords into horizontal edges (in output
  2577. //polygons) whereever maxima touch these horizontal edges. This helps
  2578. //'simplifying' polygons (ie if the Simplify property is set).
  2579. if (currMax != null)
  2580. {
  2581. if (dir == Directions.dLeftToRight)
  2582. {
  2583. while (currMax != null && currMax.X < e.Curr.X)
  2584. {
  2585. if (horzEdge.OutIdx >= 0 && !IsOpen)
  2586. AddOutPt(horzEdge, new IntPoint(currMax.X, horzEdge.Bot.Y));
  2587. currMax = currMax.Next;
  2588. }
  2589. }
  2590. else
  2591. {
  2592. while (currMax != null && currMax.X > e.Curr.X)
  2593. {
  2594. if (horzEdge.OutIdx >= 0 && !IsOpen)
  2595. AddOutPt(horzEdge, new IntPoint(currMax.X, horzEdge.Bot.Y));
  2596. currMax = currMax.Prev;
  2597. }
  2598. }
  2599. }
  2600. if ((dir == Directions.dLeftToRight && e.Curr.X > horzRight) ||
  2601. (dir == Directions.dRightToLeft && e.Curr.X < horzLeft)) break;
  2602. //Also break if we've got to the end of an intermediate horizontal edge ...
  2603. //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
  2604. if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != null &&
  2605. e.Dx < horzEdge.NextInLML.Dx) break;
  2606. if (horzEdge.OutIdx >= 0 && !IsOpen) //note: may be done multiple times
  2607. {
  2608. op1 = AddOutPt(horzEdge, e.Curr);
  2609. TEdge eNextHorz = m_SortedEdges;
  2610. while (eNextHorz != null)
  2611. {
  2612. if (eNextHorz.OutIdx >= 0 &&
  2613. HorzSegmentsOverlap(horzEdge.Bot.X,
  2614. horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X))
  2615. {
  2616. OutPt op2 = GetLastOutPt(eNextHorz);
  2617. AddJoin(op2, op1, eNextHorz.Top);
  2618. }
  2619. eNextHorz = eNextHorz.NextInSEL;
  2620. }
  2621. AddGhostJoin(op1, horzEdge.Bot);
  2622. }
  2623. //OK, so far we're still in range of the horizontal Edge but make sure
  2624. //we're at the last of consec. horizontals when matching with eMaxPair
  2625. if (e == eMaxPair && IsLastHorz)
  2626. {
  2627. if (horzEdge.OutIdx >= 0)
  2628. AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge.Top);
  2629. DeleteFromAEL(horzEdge);
  2630. DeleteFromAEL(eMaxPair);
  2631. return;
  2632. }
  2633. if (dir == Directions.dLeftToRight)
  2634. {
  2635. IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y);
  2636. IntersectEdges(horzEdge, e, Pt);
  2637. }
  2638. else
  2639. {
  2640. IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y);
  2641. IntersectEdges(e, horzEdge, Pt);
  2642. }
  2643. TEdge eNext = GetNextInAEL(e, dir);
  2644. SwapPositionsInAEL(horzEdge, e);
  2645. e = eNext;
  2646. } //end while(e != null)
  2647. //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
  2648. if (horzEdge.NextInLML == null || !IsHorizontal(horzEdge.NextInLML)) break;
  2649. UpdateEdgeIntoAEL(ref horzEdge);
  2650. if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Bot);
  2651. GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight);
  2652. } //end for (;;)
  2653. if (horzEdge.OutIdx >= 0 && op1 == null)
  2654. {
  2655. op1 = GetLastOutPt(horzEdge);
  2656. TEdge eNextHorz = m_SortedEdges;
  2657. while (eNextHorz != null)
  2658. {
  2659. if (eNextHorz.OutIdx >= 0 &&
  2660. HorzSegmentsOverlap(horzEdge.Bot.X,
  2661. horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X))
  2662. {
  2663. OutPt op2 = GetLastOutPt(eNextHorz);
  2664. AddJoin(op2, op1, eNextHorz.Top);
  2665. }
  2666. eNextHorz = eNextHorz.NextInSEL;
  2667. }
  2668. AddGhostJoin(op1, horzEdge.Top);
  2669. }
  2670. if (horzEdge.NextInLML != null)
  2671. {
  2672. if (horzEdge.OutIdx >= 0)
  2673. {
  2674. op1 = AddOutPt(horzEdge, horzEdge.Top);
  2675. UpdateEdgeIntoAEL(ref horzEdge);
  2676. if (horzEdge.WindDelta == 0) return;
  2677. //nb: HorzEdge is no longer horizontal here
  2678. TEdge ePrev = horzEdge.PrevInAEL;
  2679. TEdge eNext = horzEdge.NextInAEL;
  2680. if (ePrev != null && ePrev.Curr.X == horzEdge.Bot.X &&
  2681. ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 &&
  2682. (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y &&
  2683. SlopesEqual(horzEdge, ePrev, m_UseFullRange)))
  2684. {
  2685. OutPt op2 = AddOutPt(ePrev, horzEdge.Bot);
  2686. AddJoin(op1, op2, horzEdge.Top);
  2687. }
  2688. else if (eNext != null && eNext.Curr.X == horzEdge.Bot.X &&
  2689. eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 &&
  2690. eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y &&
  2691. SlopesEqual(horzEdge, eNext, m_UseFullRange))
  2692. {
  2693. OutPt op2 = AddOutPt(eNext, horzEdge.Bot);
  2694. AddJoin(op1, op2, horzEdge.Top);
  2695. }
  2696. }
  2697. else
  2698. UpdateEdgeIntoAEL(ref horzEdge);
  2699. }
  2700. else
  2701. {
  2702. if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Top);
  2703. DeleteFromAEL(horzEdge);
  2704. }
  2705. }
  2706. //------------------------------------------------------------------------------
  2707. private TEdge GetNextInAEL(TEdge e, Directions Directions)
  2708. {
  2709. return Directions == Directions.dLeftToRight ? e.NextInAEL : e.PrevInAEL;
  2710. }
  2711. //------------------------------------------------------------------------------
  2712. private bool IsMinima(TEdge e)
  2713. {
  2714. return e != null && (e.Prev.NextInLML != e) && (e.Next.NextInLML != e);
  2715. }
  2716. //------------------------------------------------------------------------------
  2717. private bool IsMaxima(TEdge e, double Y)
  2718. {
  2719. return (e != null && e.Top.Y == Y && e.NextInLML == null);
  2720. }
  2721. //------------------------------------------------------------------------------
  2722. private bool IsIntermediate(TEdge e, double Y)
  2723. {
  2724. return (e.Top.Y == Y && e.NextInLML != null);
  2725. }
  2726. //------------------------------------------------------------------------------
  2727. internal TEdge GetMaximaPair(TEdge e)
  2728. {
  2729. if ((e.Next.Top == e.Top) && e.Next.NextInLML == null)
  2730. return e.Next;
  2731. else if ((e.Prev.Top == e.Top) && e.Prev.NextInLML == null)
  2732. return e.Prev;
  2733. else
  2734. return null;
  2735. }
  2736. //------------------------------------------------------------------------------
  2737. internal TEdge GetMaximaPairEx(TEdge e)
  2738. {
  2739. //as above but returns null if MaxPair isn't in AEL (unless it's horizontal)
  2740. TEdge result = GetMaximaPair(e);
  2741. if (result == null || result.OutIdx == Skip ||
  2742. ((result.NextInAEL == result.PrevInAEL) && !IsHorizontal(result))) return null;
  2743. return result;
  2744. }
  2745. //------------------------------------------------------------------------------
  2746. private bool ProcessIntersections(ClipInt topY)
  2747. {
  2748. if (m_ActiveEdges == null) return true;
  2749. try
  2750. {
  2751. BuildIntersectList(topY);
  2752. if (m_IntersectList.Count == 0) return true;
  2753. if (m_IntersectList.Count == 1 || FixupIntersectionOrder())
  2754. ProcessIntersectList();
  2755. else
  2756. return false;
  2757. }
  2758. catch
  2759. {
  2760. m_SortedEdges = null;
  2761. m_IntersectList.Clear();
  2762. throw new ClipperException("ProcessIntersections error");
  2763. }
  2764. m_SortedEdges = null;
  2765. return true;
  2766. }
  2767. //------------------------------------------------------------------------------
  2768. private void BuildIntersectList(ClipInt topY)
  2769. {
  2770. if (m_ActiveEdges == null) return;
  2771. //prepare for sorting ...
  2772. TEdge e = m_ActiveEdges;
  2773. m_SortedEdges = e;
  2774. while (e != null)
  2775. {
  2776. e.PrevInSEL = e.PrevInAEL;
  2777. e.NextInSEL = e.NextInAEL;
  2778. e.Curr.X = TopX(e, topY);
  2779. e = e.NextInAEL;
  2780. }
  2781. //bubblesort ...
  2782. bool isModified = true;
  2783. while (isModified && m_SortedEdges != null)
  2784. {
  2785. isModified = false;
  2786. e = m_SortedEdges;
  2787. while (e.NextInSEL != null)
  2788. {
  2789. TEdge eNext = e.NextInSEL;
  2790. IntPoint pt;
  2791. if (e.Curr.X > eNext.Curr.X)
  2792. {
  2793. IntersectPoint(e, eNext, out pt);
  2794. if (pt.Y < topY)
  2795. pt = new IntPoint(TopX(e, topY), topY);
  2796. IntersectNode newNode = new IntersectNode();
  2797. newNode.Edge1 = e;
  2798. newNode.Edge2 = eNext;
  2799. newNode.Pt = pt;
  2800. m_IntersectList.Add(newNode);
  2801. SwapPositionsInSEL(e, eNext);
  2802. isModified = true;
  2803. }
  2804. else
  2805. e = eNext;
  2806. }
  2807. if (e.PrevInSEL != null) e.PrevInSEL.NextInSEL = null;
  2808. else break;
  2809. }
  2810. m_SortedEdges = null;
  2811. }
  2812. //------------------------------------------------------------------------------
  2813. private bool EdgesAdjacent(IntersectNode inode)
  2814. {
  2815. return (inode.Edge1.NextInSEL == inode.Edge2) ||
  2816. (inode.Edge1.PrevInSEL == inode.Edge2);
  2817. }
  2818. //------------------------------------------------------------------------------
  2819. private static int IntersectNodeSort(IntersectNode node1, IntersectNode node2)
  2820. {
  2821. //the following typecast is safe because the differences in Pt.Y will
  2822. //be limited to the height of the scanbeam.
  2823. return (int)(node2.Pt.Y - node1.Pt.Y);
  2824. }
  2825. //------------------------------------------------------------------------------
  2826. private bool FixupIntersectionOrder()
  2827. {
  2828. //pre-condition: intersections are sorted bottom-most first.
  2829. //Now it's crucial that intersections are made only between adjacent edges,
  2830. //so to ensure this the order of intersections may need adjusting ...
  2831. m_IntersectList.Sort(m_IntersectNodeComparer);
  2832. CopyAELToSEL();
  2833. int cnt = m_IntersectList.Count;
  2834. for (int i = 0; i < cnt; i++)
  2835. {
  2836. if (!EdgesAdjacent(m_IntersectList[i]))
  2837. {
  2838. int j = i + 1;
  2839. while (j < cnt && !EdgesAdjacent(m_IntersectList[j])) j++;
  2840. if (j == cnt) return false;
  2841. IntersectNode tmp = m_IntersectList[i];
  2842. m_IntersectList[i] = m_IntersectList[j];
  2843. m_IntersectList[j] = tmp;
  2844. }
  2845. SwapPositionsInSEL(m_IntersectList[i].Edge1, m_IntersectList[i].Edge2);
  2846. }
  2847. return true;
  2848. }
  2849. //------------------------------------------------------------------------------
  2850. private void ProcessIntersectList()
  2851. {
  2852. for (int i = 0; i < m_IntersectList.Count; i++)
  2853. {
  2854. IntersectNode iNode = m_IntersectList[i];
  2855. {
  2856. IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt);
  2857. SwapPositionsInAEL(iNode.Edge1, iNode.Edge2);
  2858. }
  2859. }
  2860. m_IntersectList.Clear();
  2861. }
  2862. //------------------------------------------------------------------------------
  2863. internal static ClipInt Round(double value)
  2864. {
  2865. return value < 0 ? (ClipInt)(value - 0.5) : (ClipInt)(value + 0.5);
  2866. }
  2867. //------------------------------------------------------------------------------
  2868. private static ClipInt TopX(TEdge edge, ClipInt currentY)
  2869. {
  2870. if (currentY == edge.Top.Y)
  2871. return edge.Top.X;
  2872. return edge.Bot.X + Round(edge.Dx * (currentY - edge.Bot.Y));
  2873. }
  2874. //------------------------------------------------------------------------------
  2875. private void IntersectPoint(TEdge edge1, TEdge edge2, out IntPoint ip)
  2876. {
  2877. ip = new IntPoint();
  2878. long pivotPoint = -1;
  2879. bool isClamp = (edge2.Curr.N > 0 && edge2.Curr.N < LastIndex) && (edge1.Curr.N > 0 && edge1.Curr.N < LastIndex);
  2880. if (edge1.Curr.N > edge2.Curr.N)
  2881. {
  2882. if (edge2.Curr.N != -1)
  2883. {
  2884. if (isClamp)
  2885. {
  2886. pivotPoint = (edge1.Curr.N > 0) ? edge1.Curr.N - 1 : 0;
  2887. }
  2888. }
  2889. else
  2890. {
  2891. pivotPoint = edge1.Curr.N;
  2892. }
  2893. }
  2894. else
  2895. {
  2896. if (edge1.Curr.N != -1)
  2897. {
  2898. if (isClamp)
  2899. {
  2900. pivotPoint = edge2.Curr.N;
  2901. }
  2902. }
  2903. else
  2904. {
  2905. pivotPoint = (edge2.Curr.N > 0) ? edge2.Curr.N - 1 : 0;
  2906. }
  2907. }
  2908. ip.D = 2; ip.N = isClamp ? pivotPoint : -1;
  2909. //nb: with very large coordinate values, it's possible for SlopesEqual() to
  2910. //return false but for the edge.Dx value be equal due to double precision rounding.
  2911. if (edge1.Dx == edge2.Dx)
  2912. {
  2913. ip.Y = edge1.Curr.Y;
  2914. ip.X = TopX(edge1, ip.Y);
  2915. return;
  2916. }
  2917. double b1, b2;
  2918. if (edge1.Delta.X == 0)
  2919. {
  2920. ip.X = edge1.Bot.X;
  2921. if (IsHorizontal(edge2))
  2922. {
  2923. ip.Y = edge2.Bot.Y;
  2924. }
  2925. else
  2926. {
  2927. b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx);
  2928. ip.Y = Round(ip.X / edge2.Dx + b2);
  2929. }
  2930. }
  2931. else if (edge2.Delta.X == 0)
  2932. {
  2933. ip.X = edge2.Bot.X;
  2934. if (IsHorizontal(edge1))
  2935. {
  2936. ip.Y = edge1.Bot.Y;
  2937. }
  2938. else
  2939. {
  2940. b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx);
  2941. ip.Y = Round(ip.X / edge1.Dx + b1);
  2942. }
  2943. }
  2944. else
  2945. {
  2946. b1 = edge1.Bot.X - edge1.Bot.Y * edge1.Dx;
  2947. b2 = edge2.Bot.X - edge2.Bot.Y * edge2.Dx;
  2948. double q = (b2 - b1) / (edge1.Dx - edge2.Dx);
  2949. ip.Y = Round(q);
  2950. if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx))
  2951. ip.X = Round(edge1.Dx * q + b1);
  2952. else
  2953. ip.X = Round(edge2.Dx * q + b2);
  2954. }
  2955. if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y)
  2956. {
  2957. if (edge1.Top.Y > edge2.Top.Y)
  2958. ip.Y = edge1.Top.Y;
  2959. else
  2960. ip.Y = edge2.Top.Y;
  2961. if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx))
  2962. ip.X = TopX(edge1, ip.Y);
  2963. else
  2964. ip.X = TopX(edge2, ip.Y);
  2965. }
  2966. //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
  2967. if (ip.Y > edge1.Curr.Y)
  2968. {
  2969. ip.Y = edge1.Curr.Y;
  2970. //better to use the more vertical edge to derive X ...
  2971. if (Math.Abs(edge1.Dx) > Math.Abs(edge2.Dx))
  2972. ip.X = TopX(edge2, ip.Y);
  2973. else
  2974. ip.X = TopX(edge1, ip.Y);
  2975. }
  2976. }
  2977. //------------------------------------------------------------------------------
  2978. private void ProcessEdgesAtTopOfScanbeam(ClipInt topY)
  2979. {
  2980. TEdge e = m_ActiveEdges;
  2981. while (e != null)
  2982. {
  2983. //1. process maxima, treating them as if they're 'bent' horizontal edges,
  2984. // but exclude maxima with horizontal edges. nb: e can't be a horizontal.
  2985. bool IsMaximaEdge = IsMaxima(e, topY);
  2986. if (IsMaximaEdge)
  2987. {
  2988. TEdge eMaxPair = GetMaximaPairEx(e);
  2989. IsMaximaEdge = (eMaxPair == null || !IsHorizontal(eMaxPair));
  2990. }
  2991. if (IsMaximaEdge)
  2992. {
  2993. if (StrictlySimple) InsertMaxima(e.Top.X);
  2994. TEdge ePrev = e.PrevInAEL;
  2995. DoMaxima(e);
  2996. if (ePrev == null) e = m_ActiveEdges;
  2997. else e = ePrev.NextInAEL;
  2998. }
  2999. else
  3000. {
  3001. //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
  3002. if (IsIntermediate(e, topY) && IsHorizontal(e.NextInLML))
  3003. {
  3004. UpdateEdgeIntoAEL(ref e);
  3005. if (e.OutIdx >= 0)
  3006. AddOutPt(e, e.Bot);
  3007. AddEdgeToSEL(e);
  3008. }
  3009. else
  3010. {
  3011. e.Curr.X = TopX(e, topY);
  3012. e.Curr.Y = topY;
  3013. }
  3014. //When StrictlySimple and 'e' is being touched by another edge, then
  3015. //make sure both edges have a vertex here ...
  3016. if (StrictlySimple)
  3017. {
  3018. TEdge ePrev = e.PrevInAEL;
  3019. if ((e.OutIdx >= 0) && (e.WindDelta != 0) && ePrev != null &&
  3020. (ePrev.OutIdx >= 0) && (ePrev.Curr.X == e.Curr.X) &&
  3021. (ePrev.WindDelta != 0))
  3022. {
  3023. IntPoint ip = new IntPoint(e.Curr);
  3024. OutPt op = AddOutPt(ePrev, ip);
  3025. OutPt op2 = AddOutPt(e, ip);
  3026. AddJoin(op, op2, ip); //StrictlySimple (type-3) join
  3027. }
  3028. }
  3029. e = e.NextInAEL;
  3030. }
  3031. }
  3032. //3. Process horizontals at the Top of the scanbeam ...
  3033. ProcessHorizontals();
  3034. m_Maxima = null;
  3035. //4. Promote intermediate vertices ...
  3036. e = m_ActiveEdges;
  3037. while (e != null)
  3038. {
  3039. if (IsIntermediate(e, topY))
  3040. {
  3041. OutPt op = null;
  3042. if (e.OutIdx >= 0)
  3043. op = AddOutPt(e, e.Top);
  3044. UpdateEdgeIntoAEL(ref e);
  3045. //if output polygons share an edge, they'll need joining later ...
  3046. TEdge ePrev = e.PrevInAEL;
  3047. TEdge eNext = e.NextInAEL;
  3048. if (ePrev != null && ePrev.Curr.X == e.Bot.X &&
  3049. ePrev.Curr.Y == e.Bot.Y && op != null &&
  3050. ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y &&
  3051. SlopesEqual(e.Curr, e.Top, ePrev.Curr, ePrev.Top, m_UseFullRange) &&
  3052. (e.WindDelta != 0) && (ePrev.WindDelta != 0))
  3053. {
  3054. OutPt op2 = AddOutPt(ePrev, e.Bot);
  3055. AddJoin(op, op2, e.Top);
  3056. }
  3057. else if (eNext != null && eNext.Curr.X == e.Bot.X &&
  3058. eNext.Curr.Y == e.Bot.Y && op != null &&
  3059. eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y &&
  3060. SlopesEqual(e.Curr, e.Top, eNext.Curr, eNext.Top, m_UseFullRange) &&
  3061. (e.WindDelta != 0) && (eNext.WindDelta != 0))
  3062. {
  3063. OutPt op2 = AddOutPt(eNext, e.Bot);
  3064. AddJoin(op, op2, e.Top);
  3065. }
  3066. }
  3067. e = e.NextInAEL;
  3068. }
  3069. }
  3070. //------------------------------------------------------------------------------
  3071. private void DoMaxima(TEdge e)
  3072. {
  3073. TEdge eMaxPair = GetMaximaPairEx(e);
  3074. if (eMaxPair == null)
  3075. {
  3076. if (e.OutIdx >= 0)
  3077. AddOutPt(e, e.Top);
  3078. DeleteFromAEL(e);
  3079. return;
  3080. }
  3081. TEdge eNext = e.NextInAEL;
  3082. while (eNext != null && eNext != eMaxPair)
  3083. {
  3084. IntersectEdges(e, eNext, e.Top);
  3085. SwapPositionsInAEL(e, eNext);
  3086. eNext = e.NextInAEL;
  3087. }
  3088. if (e.OutIdx == Unassigned && eMaxPair.OutIdx == Unassigned)
  3089. {
  3090. DeleteFromAEL(e);
  3091. DeleteFromAEL(eMaxPair);
  3092. }
  3093. else if (e.OutIdx >= 0 && eMaxPair.OutIdx >= 0)
  3094. {
  3095. if (e.OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e.Top);
  3096. DeleteFromAEL(e);
  3097. DeleteFromAEL(eMaxPair);
  3098. }
  3099. #if use_lines
  3100. else if (e.WindDelta == 0)
  3101. {
  3102. if (e.OutIdx >= 0)
  3103. {
  3104. AddOutPt(e, e.Top);
  3105. e.OutIdx = Unassigned;
  3106. }
  3107. DeleteFromAEL(e);
  3108. if (eMaxPair.OutIdx >= 0)
  3109. {
  3110. AddOutPt(eMaxPair, e.Top);
  3111. eMaxPair.OutIdx = Unassigned;
  3112. }
  3113. DeleteFromAEL(eMaxPair);
  3114. }
  3115. #endif
  3116. else throw new ClipperException("DoMaxima error");
  3117. }
  3118. //------------------------------------------------------------------------------
  3119. public static void ReversePaths(Paths polys)
  3120. {
  3121. for (int i = 0; i < polys.Count; i++)
  3122. {
  3123. var poly = polys[i];
  3124. poly.Reverse();
  3125. }
  3126. }
  3127. //------------------------------------------------------------------------------
  3128. public static bool Orientation(Path poly)
  3129. {
  3130. return Area(poly) >= 0;
  3131. }
  3132. //------------------------------------------------------------------------------
  3133. private int PointCount(OutPt pts)
  3134. {
  3135. if (pts == null) return 0;
  3136. int result = 0;
  3137. OutPt p = pts;
  3138. do
  3139. {
  3140. result++;
  3141. p = p.Next;
  3142. }
  3143. while (p != pts);
  3144. return result;
  3145. }
  3146. //------------------------------------------------------------------------------
  3147. private void BuildResult(Paths polyg)
  3148. {
  3149. polyg.Clear();
  3150. polyg.Capacity = m_PolyOuts.Count;
  3151. for (int i = 0; i < m_PolyOuts.Count; i++)
  3152. {
  3153. OutRec outRec = m_PolyOuts[i];
  3154. if (outRec.Pts == null) continue;
  3155. OutPt p = outRec.Pts.Prev;
  3156. int cnt = PointCount(p);
  3157. if (cnt < 2) continue;
  3158. Path pg = new Path(cnt);
  3159. for (int j = 0; j < cnt; j++)
  3160. {
  3161. pg.Add(p.Pt);
  3162. p = p.Prev;
  3163. }
  3164. polyg.Add(pg);
  3165. }
  3166. }
  3167. //------------------------------------------------------------------------------
  3168. private void BuildResult2(PolyTree polytree)
  3169. {
  3170. polytree.Clear();
  3171. //add each output polygon/contour to polytree ...
  3172. polytree.m_AllPolys.Capacity = m_PolyOuts.Count;
  3173. for (int i = 0; i < m_PolyOuts.Count; i++)
  3174. {
  3175. OutRec outRec = m_PolyOuts[i];
  3176. int cnt = PointCount(outRec.Pts);
  3177. if ((outRec.IsOpen && cnt < 2) ||
  3178. (!outRec.IsOpen && cnt < 3)) continue;
  3179. FixHoleLinkage(outRec);
  3180. PolyNode pn = new PolyNode();
  3181. polytree.m_AllPolys.Add(pn);
  3182. outRec.PolyNode = pn;
  3183. pn.m_polygon.Capacity = cnt;
  3184. OutPt op = outRec.Pts.Prev;
  3185. for (int j = 0; j < cnt; j++)
  3186. {
  3187. pn.m_polygon.Add(op.Pt);
  3188. op = op.Prev;
  3189. }
  3190. }
  3191. //fixup PolyNode links etc ...
  3192. polytree.m_Childs.Capacity = m_PolyOuts.Count;
  3193. for (int i = 0; i < m_PolyOuts.Count; i++)
  3194. {
  3195. OutRec outRec = m_PolyOuts[i];
  3196. if (outRec.PolyNode == null) continue;
  3197. else if (outRec.IsOpen)
  3198. {
  3199. outRec.PolyNode.IsOpen = true;
  3200. polytree.AddChild(outRec.PolyNode);
  3201. }
  3202. else if (outRec.FirstLeft != null &&
  3203. outRec.FirstLeft.PolyNode != null)
  3204. outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode);
  3205. else
  3206. polytree.AddChild(outRec.PolyNode);
  3207. }
  3208. }
  3209. //------------------------------------------------------------------------------
  3210. private void FixupOutPolyline(OutRec outrec)
  3211. {
  3212. OutPt pp = outrec.Pts;
  3213. OutPt lastPP = pp.Prev;
  3214. while (pp != lastPP)
  3215. {
  3216. pp = pp.Next;
  3217. if (pp.Pt == pp.Prev.Pt)
  3218. {
  3219. if (pp == lastPP) lastPP = pp.Prev;
  3220. OutPt tmpPP = pp.Prev;
  3221. tmpPP.Next = pp.Next;
  3222. pp.Next.Prev = tmpPP;
  3223. pp = tmpPP;
  3224. }
  3225. }
  3226. if (pp == pp.Prev) outrec.Pts = null;
  3227. }
  3228. //------------------------------------------------------------------------------
  3229. private void FixupOutPolygon(OutRec outRec)
  3230. {
  3231. //FixupOutPolygon() - removes duplicate points and simplifies consecutive
  3232. //parallel edges by removing the middle vertex.
  3233. OutPt lastOK = null;
  3234. outRec.BottomPt = null;
  3235. OutPt pp = outRec.Pts;
  3236. bool preserveCol = PreserveCollinear || StrictlySimple;
  3237. for (;;)
  3238. {
  3239. if (pp.Prev == pp || pp.Prev == pp.Next)
  3240. {
  3241. outRec.Pts = null;
  3242. return;
  3243. }
  3244. //test for duplicate points and collinear edges ...
  3245. if ((pp.Pt == pp.Next.Pt) || (pp.Pt == pp.Prev.Pt) ||
  3246. (SlopesEqual(pp.Prev.Pt, pp.Pt, pp.Next.Pt, m_UseFullRange) &&
  3247. (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp.Prev.Pt, pp.Pt, pp.Next.Pt))))
  3248. {
  3249. lastOK = null;
  3250. pp.Prev.Next = pp.Next;
  3251. pp.Next.Prev = pp.Prev;
  3252. pp = pp.Prev;
  3253. }
  3254. else if (pp == lastOK) break;
  3255. else
  3256. {
  3257. if (lastOK == null) lastOK = pp;
  3258. pp = pp.Next;
  3259. }
  3260. }
  3261. outRec.Pts = pp;
  3262. }
  3263. //------------------------------------------------------------------------------
  3264. OutPt DupOutPt(OutPt outPt, bool InsertAfter)
  3265. {
  3266. OutPt result = new OutPt();
  3267. result.Pt = outPt.Pt;
  3268. result.Idx = outPt.Idx;
  3269. if (InsertAfter)
  3270. {
  3271. result.Next = outPt.Next;
  3272. result.Prev = outPt;
  3273. outPt.Next.Prev = result;
  3274. outPt.Next = result;
  3275. }
  3276. else
  3277. {
  3278. result.Prev = outPt.Prev;
  3279. result.Next = outPt;
  3280. outPt.Prev.Next = result;
  3281. outPt.Prev = result;
  3282. }
  3283. return result;
  3284. }
  3285. //------------------------------------------------------------------------------
  3286. bool GetOverlap(ClipInt a1, ClipInt a2, ClipInt b1, ClipInt b2, out ClipInt Left, out ClipInt Right)
  3287. {
  3288. if (a1 < a2)
  3289. {
  3290. if (b1 < b2) { Left = Math.Max(a1, b1); Right = Math.Min(a2, b2); }
  3291. else { Left = Math.Max(a1, b2); Right = Math.Min(a2, b1); }
  3292. }
  3293. else
  3294. {
  3295. if (b1 < b2) { Left = Math.Max(a2, b1); Right = Math.Min(a1, b2); }
  3296. else { Left = Math.Max(a2, b2); Right = Math.Min(a1, b1); }
  3297. }
  3298. return Left < Right;
  3299. }
  3300. //------------------------------------------------------------------------------
  3301. bool JoinHorz(OutPt op1, OutPt op1b, OutPt op2, OutPt op2b,
  3302. IntPoint Pt, bool DiscardLeft)
  3303. {
  3304. Directions Dir1 = (op1.Pt.X > op1b.Pt.X ?
  3305. Directions.dRightToLeft : Directions.dLeftToRight);
  3306. Directions Dir2 = (op2.Pt.X > op2b.Pt.X ?
  3307. Directions.dRightToLeft : Directions.dLeftToRight);
  3308. if (Dir1 == Dir2) return false;
  3309. //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
  3310. //want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
  3311. //So, to facilitate this while inserting Op1b and Op2b ...
  3312. //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
  3313. //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
  3314. if (Dir1 == Directions.dLeftToRight)
  3315. {
  3316. while (op1.Next.Pt.X <= Pt.X &&
  3317. op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == Pt.Y)
  3318. op1 = op1.Next;
  3319. if (DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next;
  3320. op1b = DupOutPt(op1, !DiscardLeft);
  3321. if (op1b.Pt != Pt)
  3322. {
  3323. op1 = op1b;
  3324. op1.Pt = Pt;
  3325. op1b = DupOutPt(op1, !DiscardLeft);
  3326. }
  3327. }
  3328. else
  3329. {
  3330. while (op1.Next.Pt.X >= Pt.X &&
  3331. op1.Next.Pt.X <= op1.Pt.X && op1.Next.Pt.Y == Pt.Y)
  3332. op1 = op1.Next;
  3333. if (!DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next;
  3334. op1b = DupOutPt(op1, DiscardLeft);
  3335. if (op1b.Pt != Pt)
  3336. {
  3337. op1 = op1b;
  3338. op1.Pt = Pt;
  3339. op1b = DupOutPt(op1, DiscardLeft);
  3340. }
  3341. }
  3342. if (Dir2 == Directions.dLeftToRight)
  3343. {
  3344. while (op2.Next.Pt.X <= Pt.X &&
  3345. op2.Next.Pt.X >= op2.Pt.X && op2.Next.Pt.Y == Pt.Y)
  3346. op2 = op2.Next;
  3347. if (DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next;
  3348. op2b = DupOutPt(op2, !DiscardLeft);
  3349. if (op2b.Pt != Pt)
  3350. {
  3351. op2 = op2b;
  3352. op2.Pt = Pt;
  3353. op2b = DupOutPt(op2, !DiscardLeft);
  3354. }
  3355. }
  3356. else
  3357. {
  3358. while (op2.Next.Pt.X >= Pt.X &&
  3359. op2.Next.Pt.X <= op2.Pt.X && op2.Next.Pt.Y == Pt.Y)
  3360. op2 = op2.Next;
  3361. if (!DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next;
  3362. op2b = DupOutPt(op2, DiscardLeft);
  3363. if (op2b.Pt != Pt)
  3364. {
  3365. op2 = op2b;
  3366. op2.Pt = Pt;
  3367. op2b = DupOutPt(op2, DiscardLeft);
  3368. }
  3369. }
  3370. if ((Dir1 == Directions.dLeftToRight) == DiscardLeft)
  3371. {
  3372. op1.Prev = op2;
  3373. op2.Next = op1;
  3374. op1b.Next = op2b;
  3375. op2b.Prev = op1b;
  3376. }
  3377. else
  3378. {
  3379. op1.Next = op2;
  3380. op2.Prev = op1;
  3381. op1b.Prev = op2b;
  3382. op2b.Next = op1b;
  3383. }
  3384. return true;
  3385. }
  3386. //------------------------------------------------------------------------------
  3387. private bool JoinPoints(Join j, OutRec outRec1, OutRec outRec2)
  3388. {
  3389. OutPt op1 = j.OutPt1, op1b;
  3390. OutPt op2 = j.OutPt2, op2b;
  3391. //There are 3 kinds of joins for output polygons ...
  3392. //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
  3393. //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
  3394. //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
  3395. //location at the Bottom of the overlapping segment (& Join.OffPt is above).
  3396. //3. StrictlySimple joins where edges touch but are not collinear and where
  3397. //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
  3398. bool isHorizontal = (j.OutPt1.Pt.Y == j.OffPt.Y);
  3399. if (isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt))
  3400. {
  3401. //Strictly Simple join ...
  3402. if (outRec1 != outRec2) return false;
  3403. op1b = j.OutPt1.Next;
  3404. while (op1b != op1 && (op1b.Pt == j.OffPt))
  3405. op1b = op1b.Next;
  3406. bool reverse1 = (op1b.Pt.Y > j.OffPt.Y);
  3407. op2b = j.OutPt2.Next;
  3408. while (op2b != op2 && (op2b.Pt == j.OffPt))
  3409. op2b = op2b.Next;
  3410. bool reverse2 = (op2b.Pt.Y > j.OffPt.Y);
  3411. if (reverse1 == reverse2) return false;
  3412. if (reverse1)
  3413. {
  3414. op1b = DupOutPt(op1, false);
  3415. op2b = DupOutPt(op2, true);
  3416. op1.Prev = op2;
  3417. op2.Next = op1;
  3418. op1b.Next = op2b;
  3419. op2b.Prev = op1b;
  3420. j.OutPt1 = op1;
  3421. j.OutPt2 = op1b;
  3422. return true;
  3423. }
  3424. else
  3425. {
  3426. op1b = DupOutPt(op1, true);
  3427. op2b = DupOutPt(op2, false);
  3428. op1.Next = op2;
  3429. op2.Prev = op1;
  3430. op1b.Prev = op2b;
  3431. op2b.Next = op1b;
  3432. j.OutPt1 = op1;
  3433. j.OutPt2 = op1b;
  3434. return true;
  3435. }
  3436. }
  3437. else if (isHorizontal)
  3438. {
  3439. //treat horizontal joins differently to non-horizontal joins since with
  3440. //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
  3441. //may be anywhere along the horizontal edge.
  3442. op1b = op1;
  3443. while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2)
  3444. op1 = op1.Prev;
  3445. while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2)
  3446. op1b = op1b.Next;
  3447. if (op1b.Next == op1 || op1b.Next == op2) return false; //a flat 'polygon'
  3448. op2b = op2;
  3449. while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b)
  3450. op2 = op2.Prev;
  3451. while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1)
  3452. op2b = op2b.Next;
  3453. if (op2b.Next == op2 || op2b.Next == op1) return false; //a flat 'polygon'
  3454. ClipInt Left, Right;
  3455. //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges
  3456. if (!GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, out Left, out Right))
  3457. return false;
  3458. //DiscardLeftSide: when overlapping edges are joined, a spike will created
  3459. //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
  3460. //on the discard Side as either may still be needed for other joins ...
  3461. IntPoint Pt;
  3462. bool DiscardLeftSide;
  3463. if (op1.Pt.X >= Left && op1.Pt.X <= Right)
  3464. {
  3465. Pt = op1.Pt; DiscardLeftSide = (op1.Pt.X > op1b.Pt.X);
  3466. }
  3467. else if (op2.Pt.X >= Left && op2.Pt.X <= Right)
  3468. {
  3469. Pt = op2.Pt; DiscardLeftSide = (op2.Pt.X > op2b.Pt.X);
  3470. }
  3471. else if (op1b.Pt.X >= Left && op1b.Pt.X <= Right)
  3472. {
  3473. Pt = op1b.Pt; DiscardLeftSide = op1b.Pt.X > op1.Pt.X;
  3474. }
  3475. else
  3476. {
  3477. Pt = op2b.Pt; DiscardLeftSide = (op2b.Pt.X > op2.Pt.X);
  3478. }
  3479. j.OutPt1 = op1;
  3480. j.OutPt2 = op2;
  3481. return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide);
  3482. }
  3483. else
  3484. {
  3485. //nb: For non-horizontal joins ...
  3486. // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
  3487. // 2. Jr.OutPt1.Pt > Jr.OffPt.Y
  3488. //make sure the polygons are correctly oriented ...
  3489. op1b = op1.Next;
  3490. while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Next;
  3491. bool Reverse1 = ((op1b.Pt.Y > op1.Pt.Y) ||
  3492. !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange));
  3493. if (Reverse1)
  3494. {
  3495. op1b = op1.Prev;
  3496. while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Prev;
  3497. if ((op1b.Pt.Y > op1.Pt.Y) ||
  3498. !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange)) return false;
  3499. }
  3500. op2b = op2.Next;
  3501. while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Next;
  3502. bool Reverse2 = ((op2b.Pt.Y > op2.Pt.Y) ||
  3503. !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange));
  3504. if (Reverse2)
  3505. {
  3506. op2b = op2.Prev;
  3507. while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Prev;
  3508. if ((op2b.Pt.Y > op2.Pt.Y) ||
  3509. !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange)) return false;
  3510. }
  3511. if ((op1b == op1) || (op2b == op2) || (op1b == op2b) ||
  3512. ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false;
  3513. if (Reverse1)
  3514. {
  3515. op1b = DupOutPt(op1, false);
  3516. op2b = DupOutPt(op2, true);
  3517. op1.Prev = op2;
  3518. op2.Next = op1;
  3519. op1b.Next = op2b;
  3520. op2b.Prev = op1b;
  3521. j.OutPt1 = op1;
  3522. j.OutPt2 = op1b;
  3523. return true;
  3524. }
  3525. else
  3526. {
  3527. op1b = DupOutPt(op1, true);
  3528. op2b = DupOutPt(op2, false);
  3529. op1.Next = op2;
  3530. op2.Prev = op1;
  3531. op1b.Prev = op2b;
  3532. op2b.Next = op1b;
  3533. j.OutPt1 = op1;
  3534. j.OutPt2 = op1b;
  3535. return true;
  3536. }
  3537. }
  3538. }
  3539. //----------------------------------------------------------------------
  3540. public static int PointInPolygon(IntPoint pt, Path path)
  3541. {
  3542. //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
  3543. //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
  3544. //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
  3545. int result = 0, cnt = path.Count;
  3546. if (cnt < 3) return 0;
  3547. IntPoint ip = path[0];
  3548. for (int i = 1; i <= cnt; ++i)
  3549. {
  3550. IntPoint ipNext = (i == cnt ? path[0] : path[i]);
  3551. if (ipNext.Y == pt.Y)
  3552. {
  3553. if ((ipNext.X == pt.X) || (ip.Y == pt.Y &&
  3554. ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1;
  3555. }
  3556. if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y))
  3557. {
  3558. if (ip.X >= pt.X)
  3559. {
  3560. if (ipNext.X > pt.X) result = 1 - result;
  3561. else
  3562. {
  3563. double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
  3564. (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
  3565. if (d == 0) return -1;
  3566. else if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
  3567. }
  3568. }
  3569. else
  3570. {
  3571. if (ipNext.X > pt.X)
  3572. {
  3573. double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
  3574. (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
  3575. if (d == 0) return -1;
  3576. else if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
  3577. }
  3578. }
  3579. }
  3580. ip = ipNext;
  3581. }
  3582. return result;
  3583. }
  3584. //------------------------------------------------------------------------------
  3585. //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
  3586. //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
  3587. private static int PointInPolygon(IntPoint pt, OutPt op)
  3588. {
  3589. //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
  3590. int result = 0;
  3591. OutPt startOp = op;
  3592. ClipInt ptx = pt.X, pty = pt.Y;
  3593. ClipInt poly0x = op.Pt.X, poly0y = op.Pt.Y;
  3594. do
  3595. {
  3596. op = op.Next;
  3597. ClipInt poly1x = op.Pt.X, poly1y = op.Pt.Y;
  3598. if (poly1y == pty)
  3599. {
  3600. if ((poly1x == ptx) || (poly0y == pty &&
  3601. ((poly1x > ptx) == (poly0x < ptx)))) return -1;
  3602. }
  3603. if ((poly0y < pty) != (poly1y < pty))
  3604. {
  3605. if (poly0x >= ptx)
  3606. {
  3607. if (poly1x > ptx) result = 1 - result;
  3608. else
  3609. {
  3610. double d = (double)(poly0x - ptx) * (poly1y - pty) -
  3611. (double)(poly1x - ptx) * (poly0y - pty);
  3612. if (d == 0) return -1;
  3613. if ((d > 0) == (poly1y > poly0y)) result = 1 - result;
  3614. }
  3615. }
  3616. else
  3617. {
  3618. if (poly1x > ptx)
  3619. {
  3620. double d = (double)(poly0x - ptx) * (poly1y - pty) -
  3621. (double)(poly1x - ptx) * (poly0y - pty);
  3622. if (d == 0) return -1;
  3623. if ((d > 0) == (poly1y > poly0y)) result = 1 - result;
  3624. }
  3625. }
  3626. }
  3627. poly0x = poly1x; poly0y = poly1y;
  3628. }
  3629. while (startOp != op);
  3630. return result;
  3631. }
  3632. //------------------------------------------------------------------------------
  3633. private static bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2)
  3634. {
  3635. OutPt op = outPt1;
  3636. do
  3637. {
  3638. //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
  3639. int res = PointInPolygon(op.Pt, outPt2);
  3640. if (res >= 0) return res > 0;
  3641. op = op.Next;
  3642. }
  3643. while (op != outPt1);
  3644. return true;
  3645. }
  3646. //----------------------------------------------------------------------
  3647. private void FixupFirstLefts1(OutRec OldOutRec, OutRec NewOutRec)
  3648. {
  3649. foreach (OutRec outRec in m_PolyOuts)
  3650. {
  3651. OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft);
  3652. if (outRec.Pts != null && firstLeft == OldOutRec)
  3653. {
  3654. if (Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts))
  3655. outRec.FirstLeft = NewOutRec;
  3656. }
  3657. }
  3658. }
  3659. //----------------------------------------------------------------------
  3660. private void FixupFirstLefts2(OutRec innerOutRec, OutRec outerOutRec)
  3661. {
  3662. //A polygon has split into two such that one is now the inner of the other.
  3663. //It's possible that these polygons now wrap around other polygons, so check
  3664. //every polygon that's also contained by OuterOutRec's FirstLeft container
  3665. //(including nil) to see if they've become inner to the new inner polygon ...
  3666. OutRec orfl = outerOutRec.FirstLeft;
  3667. foreach (OutRec outRec in m_PolyOuts)
  3668. {
  3669. if (outRec.Pts == null || outRec == outerOutRec || outRec == innerOutRec)
  3670. continue;
  3671. OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft);
  3672. if (firstLeft != orfl && firstLeft != innerOutRec && firstLeft != outerOutRec)
  3673. continue;
  3674. if (Poly2ContainsPoly1(outRec.Pts, innerOutRec.Pts))
  3675. outRec.FirstLeft = innerOutRec;
  3676. else if (Poly2ContainsPoly1(outRec.Pts, outerOutRec.Pts))
  3677. outRec.FirstLeft = outerOutRec;
  3678. else if (outRec.FirstLeft == innerOutRec || outRec.FirstLeft == outerOutRec)
  3679. outRec.FirstLeft = orfl;
  3680. }
  3681. }
  3682. //----------------------------------------------------------------------
  3683. private void FixupFirstLefts3(OutRec OldOutRec, OutRec NewOutRec)
  3684. {
  3685. //same as FixupFirstLefts1 but doesn't call Poly2ContainsPoly1()
  3686. foreach (OutRec outRec in m_PolyOuts)
  3687. {
  3688. OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft);
  3689. if (outRec.Pts != null && firstLeft == OldOutRec)
  3690. outRec.FirstLeft = NewOutRec;
  3691. }
  3692. }
  3693. //----------------------------------------------------------------------
  3694. private static OutRec ParseFirstLeft(OutRec FirstLeft)
  3695. {
  3696. while (FirstLeft != null && FirstLeft.Pts == null)
  3697. FirstLeft = FirstLeft.FirstLeft;
  3698. return FirstLeft;
  3699. }
  3700. //------------------------------------------------------------------------------
  3701. private void JoinCommonEdges()
  3702. {
  3703. for (int i = 0; i < m_Joins.Count; i++)
  3704. {
  3705. Join join = m_Joins[i];
  3706. OutRec outRec1 = GetOutRec(join.OutPt1.Idx);
  3707. OutRec outRec2 = GetOutRec(join.OutPt2.Idx);
  3708. if (outRec1.Pts == null || outRec2.Pts == null) continue;
  3709. if (outRec1.IsOpen || outRec2.IsOpen) continue;
  3710. //get the polygon fragment with the correct hole state (FirstLeft)
  3711. //before calling JoinPoints() ...
  3712. OutRec holeStateRec;
  3713. if (outRec1 == outRec2) holeStateRec = outRec1;
  3714. else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2;
  3715. else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1;
  3716. else holeStateRec = GetLowermostRec(outRec1, outRec2);
  3717. if (!JoinPoints(join, outRec1, outRec2)) continue;
  3718. if (outRec1 == outRec2)
  3719. {
  3720. //instead of joining two polygons, we've just created a new one by
  3721. //splitting one polygon into two.
  3722. outRec1.Pts = join.OutPt1;
  3723. outRec1.BottomPt = null;
  3724. outRec2 = CreateOutRec();
  3725. outRec2.Pts = join.OutPt2;
  3726. //update all OutRec2.Pts Idx's ...
  3727. UpdateOutPtIdxs(outRec2);
  3728. if (Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts))
  3729. {
  3730. //outRec1 contains outRec2 ...
  3731. outRec2.IsHole = !outRec1.IsHole;
  3732. outRec2.FirstLeft = outRec1;
  3733. if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
  3734. if ((outRec2.IsHole ^ ReverseSolution) == (Area(outRec2) > 0))
  3735. ReversePolyPtLinks(outRec2.Pts);
  3736. }
  3737. else if (Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts))
  3738. {
  3739. //outRec2 contains outRec1 ...
  3740. outRec2.IsHole = outRec1.IsHole;
  3741. outRec1.IsHole = !outRec2.IsHole;
  3742. outRec2.FirstLeft = outRec1.FirstLeft;
  3743. outRec1.FirstLeft = outRec2;
  3744. if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
  3745. if ((outRec1.IsHole ^ ReverseSolution) == (Area(outRec1) > 0))
  3746. ReversePolyPtLinks(outRec1.Pts);
  3747. }
  3748. else
  3749. {
  3750. //the 2 polygons are completely separate ...
  3751. outRec2.IsHole = outRec1.IsHole;
  3752. outRec2.FirstLeft = outRec1.FirstLeft;
  3753. //fixup FirstLeft pointers that may need reassigning to OutRec2
  3754. if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
  3755. }
  3756. }
  3757. else
  3758. {
  3759. //joined 2 polygons together ...
  3760. outRec2.Pts = null;
  3761. outRec2.BottomPt = null;
  3762. outRec2.Idx = outRec1.Idx;
  3763. outRec1.IsHole = holeStateRec.IsHole;
  3764. if (holeStateRec == outRec2)
  3765. outRec1.FirstLeft = outRec2.FirstLeft;
  3766. outRec2.FirstLeft = outRec1;
  3767. //fixup FirstLeft pointers that may need reassigning to OutRec1
  3768. if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1);
  3769. }
  3770. }
  3771. }
  3772. //------------------------------------------------------------------------------
  3773. private void UpdateOutPtIdxs(OutRec outrec)
  3774. {
  3775. OutPt op = outrec.Pts;
  3776. do
  3777. {
  3778. op.Idx = outrec.Idx;
  3779. op = op.Prev;
  3780. }
  3781. while (op != outrec.Pts);
  3782. }
  3783. //------------------------------------------------------------------------------
  3784. private void DoSimplePolygons()
  3785. {
  3786. int i = 0;
  3787. while (i < m_PolyOuts.Count)
  3788. {
  3789. OutRec outrec = m_PolyOuts[i++];
  3790. OutPt op = outrec.Pts;
  3791. if (op == null || outrec.IsOpen) continue;
  3792. do //for each Pt in Polygon until duplicate found do ...
  3793. {
  3794. OutPt op2 = op.Next;
  3795. while (op2 != outrec.Pts)
  3796. {
  3797. if ((op.Pt == op2.Pt) && op2.Next != op && op2.Prev != op)
  3798. {
  3799. //split the polygon into two ...
  3800. OutPt op3 = op.Prev;
  3801. OutPt op4 = op2.Prev;
  3802. op.Prev = op4;
  3803. op4.Next = op;
  3804. op2.Prev = op3;
  3805. op3.Next = op2;
  3806. outrec.Pts = op;
  3807. OutRec outrec2 = CreateOutRec();
  3808. outrec2.Pts = op2;
  3809. UpdateOutPtIdxs(outrec2);
  3810. if (Poly2ContainsPoly1(outrec2.Pts, outrec.Pts))
  3811. {
  3812. //OutRec2 is contained by OutRec1 ...
  3813. outrec2.IsHole = !outrec.IsHole;
  3814. outrec2.FirstLeft = outrec;
  3815. if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
  3816. }
  3817. else if (Poly2ContainsPoly1(outrec.Pts, outrec2.Pts))
  3818. {
  3819. //OutRec1 is contained by OutRec2 ...
  3820. outrec2.IsHole = outrec.IsHole;
  3821. outrec.IsHole = !outrec2.IsHole;
  3822. outrec2.FirstLeft = outrec.FirstLeft;
  3823. outrec.FirstLeft = outrec2;
  3824. if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
  3825. }
  3826. else
  3827. {
  3828. //the 2 polygons are separate ...
  3829. outrec2.IsHole = outrec.IsHole;
  3830. outrec2.FirstLeft = outrec.FirstLeft;
  3831. if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
  3832. }
  3833. op2 = op; //ie get ready for the next iteration
  3834. }
  3835. op2 = op2.Next;
  3836. }
  3837. op = op.Next;
  3838. }
  3839. while (op != outrec.Pts);
  3840. }
  3841. }
  3842. //------------------------------------------------------------------------------
  3843. public static double Area(Path poly)
  3844. {
  3845. int cnt = (int)poly.Count;
  3846. if (cnt < 3) return 0;
  3847. double a = 0;
  3848. for (int i = 0, j = cnt - 1; i < cnt; ++i)
  3849. {
  3850. a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y);
  3851. j = i;
  3852. }
  3853. return -a * 0.5;
  3854. }
  3855. //------------------------------------------------------------------------------
  3856. internal double Area(OutRec outRec)
  3857. {
  3858. return Area(outRec.Pts);
  3859. }
  3860. //------------------------------------------------------------------------------
  3861. internal double Area(OutPt op)
  3862. {
  3863. OutPt opFirst = op;
  3864. if (op == null) return 0;
  3865. double a = 0;
  3866. do
  3867. {
  3868. a = a + (double)(op.Prev.Pt.X + op.Pt.X) * (double)(op.Prev.Pt.Y - op.Pt.Y);
  3869. op = op.Next;
  3870. }
  3871. while (op != opFirst);
  3872. return a * 0.5;
  3873. }
  3874. //------------------------------------------------------------------------------
  3875. // SimplifyPolygon functions ...
  3876. // Convert self-intersecting polygons into simple polygons
  3877. //------------------------------------------------------------------------------
  3878. public static Paths SimplifyPolygon(Path poly,
  3879. PolyFillTypes fillType = PolyFillTypes.pftEvenOdd)
  3880. {
  3881. Paths result = new Paths();
  3882. Clipper c = new Clipper();
  3883. c.StrictlySimple = true;
  3884. c.AddPath(poly, PolyTypes.ptSubject, true);
  3885. c.Execute(ClipTypes.ctUnion, result, fillType, fillType);
  3886. return result;
  3887. }
  3888. //------------------------------------------------------------------------------
  3889. public static Paths SimplifyPolygons(Paths polys,
  3890. PolyFillTypes fillType = PolyFillTypes.pftEvenOdd)
  3891. {
  3892. Paths result = new Paths();
  3893. Clipper c = new Clipper();
  3894. c.StrictlySimple = true;
  3895. c.AddPaths(polys, PolyTypes.ptSubject, true);
  3896. c.Execute(ClipTypes.ctUnion, result, fillType, fillType);
  3897. return result;
  3898. }
  3899. //------------------------------------------------------------------------------
  3900. private static double DistanceSqrd(IntPoint pt1, IntPoint pt2)
  3901. {
  3902. double dx = ((double)pt1.X - pt2.X);
  3903. double dy = ((double)pt1.Y - pt2.Y);
  3904. return (dx * dx + dy * dy);
  3905. }
  3906. //------------------------------------------------------------------------------
  3907. private static double DistanceFromLineSqrd(IntPoint pt, IntPoint ln1, IntPoint ln2)
  3908. {
  3909. //The equation of a line in general form (Ax + By + C = 0)
  3910. //given 2 points (x¹,y¹) & (x²,y²) is ...
  3911. //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
  3912. //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
  3913. //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
  3914. //see http://en.wikipedia.org/wiki/Perpendicular_distance
  3915. double A = ln1.Y - ln2.Y;
  3916. double B = ln2.X - ln1.X;
  3917. double C = A * ln1.X + B * ln1.Y;
  3918. C = A * pt.X + B * pt.Y - C;
  3919. return (C * C) / (A * A + B * B);
  3920. }
  3921. //---------------------------------------------------------------------------
  3922. private static bool SlopesNearCollinear(IntPoint pt1,
  3923. IntPoint pt2, IntPoint pt3, double distSqrd)
  3924. {
  3925. //this function is more accurate when the point that's GEOMETRICALLY
  3926. //between the other 2 points is the one that's tested for distance.
  3927. //nb: with 'spikes', either pt1 or pt3 is geometrically between the other pts
  3928. if (Math.Abs(pt1.X - pt2.X) > Math.Abs(pt1.Y - pt2.Y))
  3929. {
  3930. if ((pt1.X > pt2.X) == (pt1.X < pt3.X))
  3931. return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
  3932. else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))
  3933. return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
  3934. else
  3935. return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
  3936. }
  3937. else
  3938. {
  3939. if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))
  3940. return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
  3941. else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))
  3942. return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
  3943. else
  3944. return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
  3945. }
  3946. }
  3947. //------------------------------------------------------------------------------
  3948. private static bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
  3949. {
  3950. double dx = (double)pt1.X - pt2.X;
  3951. double dy = (double)pt1.Y - pt2.Y;
  3952. return ((dx * dx) + (dy * dy) <= distSqrd);
  3953. }
  3954. //------------------------------------------------------------------------------
  3955. private static OutPt ExcludeOp(OutPt op)
  3956. {
  3957. OutPt result = op.Prev;
  3958. result.Next = op.Next;
  3959. op.Next.Prev = result;
  3960. result.Idx = 0;
  3961. return result;
  3962. }
  3963. //------------------------------------------------------------------------------
  3964. public static Path CleanPolygon(Path path, double distance = 1.415)
  3965. {
  3966. //distance = proximity in units/pixels below which vertices will be stripped.
  3967. //Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have
  3968. //both x & y coords within 1 unit, then the second vertex will be stripped.
  3969. int cnt = path.Count;
  3970. if (cnt == 0) return new Path();
  3971. OutPt[] outPts = new OutPt[cnt];
  3972. for (int i = 0; i < cnt; ++i) outPts[i] = new OutPt();
  3973. for (int i = 0; i < cnt; ++i)
  3974. {
  3975. outPts[i].Pt = path[i];
  3976. outPts[i].Next = outPts[(i + 1) % cnt];
  3977. outPts[i].Next.Prev = outPts[i];
  3978. outPts[i].Idx = 0;
  3979. }
  3980. double distSqrd = distance * distance;
  3981. OutPt op = outPts[0];
  3982. while (op.Idx == 0 && op.Next != op.Prev)
  3983. {
  3984. if (PointsAreClose(op.Pt, op.Prev.Pt, distSqrd))
  3985. {
  3986. op = ExcludeOp(op);
  3987. cnt--;
  3988. }
  3989. else if (PointsAreClose(op.Prev.Pt, op.Next.Pt, distSqrd))
  3990. {
  3991. ExcludeOp(op.Next);
  3992. op = ExcludeOp(op);
  3993. cnt -= 2;
  3994. }
  3995. else if (SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, distSqrd))
  3996. {
  3997. op = ExcludeOp(op);
  3998. cnt--;
  3999. }
  4000. else
  4001. {
  4002. op.Idx = 1;
  4003. op = op.Next;
  4004. }
  4005. }
  4006. if (cnt < 3) cnt = 0;
  4007. Path result = new Path(cnt);
  4008. for (int i = 0; i < cnt; ++i)
  4009. {
  4010. result.Add(op.Pt);
  4011. op = op.Next;
  4012. }
  4013. outPts = null;
  4014. return result;
  4015. }
  4016. //------------------------------------------------------------------------------
  4017. public static Paths CleanPolygons(Paths polys,
  4018. double distance = 1.415)
  4019. {
  4020. Paths result = new Paths(polys.Count);
  4021. for (int i = 0; i < polys.Count; i++)
  4022. result.Add(CleanPolygon(polys[i], distance));
  4023. return result;
  4024. }
  4025. //------------------------------------------------------------------------------
  4026. internal static Paths Minkowski(Path pattern, Path path, bool IsSum, bool IsClosed)
  4027. {
  4028. int delta = (IsClosed ? 1 : 0);
  4029. int polyCnt = pattern.Count;
  4030. int pathCnt = path.Count;
  4031. Paths result = new Paths(pathCnt);
  4032. if (IsSum)
  4033. for (int i = 0; i < pathCnt; i++)
  4034. {
  4035. Path p = new Path(polyCnt);
  4036. for (int patternIndex = 0; patternIndex < pattern.Count; patternIndex++)
  4037. {
  4038. IntPoint ip = pattern[patternIndex];
  4039. p.Add(new IntPoint(path[i].X + ip.X, path[i].Y + ip.Y));
  4040. }
  4041. result.Add(p);
  4042. }
  4043. else
  4044. for (int i = 0; i < pathCnt; i++)
  4045. {
  4046. Path p = new Path(polyCnt);
  4047. for (int patternIndex = 0; patternIndex < pattern.Count; patternIndex++)
  4048. {
  4049. IntPoint ip = pattern[patternIndex];
  4050. p.Add(new IntPoint(path[i].X - ip.X, path[i].Y - ip.Y));
  4051. }
  4052. result.Add(p);
  4053. }
  4054. Paths quads = new Paths((pathCnt + delta) * (polyCnt + 1));
  4055. for (int i = 0; i < pathCnt - 1 + delta; i++)
  4056. for (int j = 0; j < polyCnt; j++)
  4057. {
  4058. Path quad = new Path(4);
  4059. quad.Add(result[i % pathCnt][j % polyCnt]);
  4060. quad.Add(result[(i + 1) % pathCnt][j % polyCnt]);
  4061. quad.Add(result[(i + 1) % pathCnt][(j + 1) % polyCnt]);
  4062. quad.Add(result[i % pathCnt][(j + 1) % polyCnt]);
  4063. if (!Orientation(quad)) quad.Reverse();
  4064. quads.Add(quad);
  4065. }
  4066. return quads;
  4067. }
  4068. //------------------------------------------------------------------------------
  4069. public static Paths MinkowskiSum(Path pattern, Path path, bool pathIsClosed)
  4070. {
  4071. Paths paths = Minkowski(pattern, path, true, pathIsClosed);
  4072. Clipper c = new Clipper();
  4073. c.AddPaths(paths, PolyTypes.ptSubject, true);
  4074. c.Execute(ClipTypes.ctUnion, paths, PolyFillTypes.pftNonZero, PolyFillTypes.pftNonZero);
  4075. return paths;
  4076. }
  4077. //------------------------------------------------------------------------------
  4078. private static Path TranslatePath(Path path, IntPoint delta)
  4079. {
  4080. Path outPath = new Path(path.Count);
  4081. for (int i = 0; i < path.Count; i++)
  4082. outPath.Add(new IntPoint(path[i].X + delta.X, path[i].Y + delta.Y));
  4083. return outPath;
  4084. }
  4085. //------------------------------------------------------------------------------
  4086. public static Paths MinkowskiSum(Path pattern, Paths paths, bool pathIsClosed)
  4087. {
  4088. Paths solution = new Paths();
  4089. Clipper c = new Clipper();
  4090. for (int i = 0; i < paths.Count; ++i)
  4091. {
  4092. Paths tmp = Minkowski(pattern, paths[i], true, pathIsClosed);
  4093. c.AddPaths(tmp, PolyTypes.ptSubject, true);
  4094. if (pathIsClosed)
  4095. {
  4096. Path path = TranslatePath(paths[i], pattern[0]);
  4097. c.AddPath(path, PolyTypes.ptClip, true);
  4098. }
  4099. }
  4100. c.Execute(ClipTypes.ctUnion, solution,
  4101. PolyFillTypes.pftNonZero, PolyFillTypes.pftNonZero);
  4102. return solution;
  4103. }
  4104. //------------------------------------------------------------------------------
  4105. public static Paths MinkowskiDiff(Path poly1, Path poly2)
  4106. {
  4107. Paths paths = Minkowski(poly1, poly2, false, true);
  4108. Clipper c = new Clipper();
  4109. c.AddPaths(paths, PolyTypes.ptSubject, true);
  4110. c.Execute(ClipTypes.ctUnion, paths, PolyFillTypes.pftNonZero, PolyFillTypes.pftNonZero);
  4111. return paths;
  4112. }
  4113. //------------------------------------------------------------------------------
  4114. internal enum NodeType { ntAny, ntOpen, ntClosed };
  4115. public static Paths PolyTreeToPaths(PolyTree polytree)
  4116. {
  4117. Paths result = new Paths();
  4118. result.Capacity = polytree.Total;
  4119. AddPolyNodeToPaths(polytree, NodeType.ntAny, result);
  4120. return result;
  4121. }
  4122. //------------------------------------------------------------------------------
  4123. internal static void AddPolyNodeToPaths(PolyNode polynode, NodeType nt, Paths paths)
  4124. {
  4125. bool match = true;
  4126. switch (nt)
  4127. {
  4128. case NodeType.ntOpen: return;
  4129. case NodeType.ntClosed: match = !polynode.IsOpen; break;
  4130. default: break;
  4131. }
  4132. if (polynode.m_polygon.Count > 0 && match)
  4133. paths.Add(polynode.m_polygon);
  4134. foreach (PolyNode pn in polynode.Childs)
  4135. AddPolyNodeToPaths(pn, nt, paths);
  4136. }
  4137. //------------------------------------------------------------------------------
  4138. public static Paths OpenPathsFromPolyTree(PolyTree polytree)
  4139. {
  4140. Paths result = new Paths();
  4141. result.Capacity = polytree.ChildCount;
  4142. for (int i = 0; i < polytree.ChildCount; i++)
  4143. if (polytree.Childs[i].IsOpen)
  4144. result.Add(polytree.Childs[i].m_polygon);
  4145. return result;
  4146. }
  4147. //------------------------------------------------------------------------------
  4148. public static Paths ClosedPathsFromPolyTree(PolyTree polytree)
  4149. {
  4150. Paths result = new Paths();
  4151. result.Capacity = polytree.Total;
  4152. AddPolyNodeToPaths(polytree, NodeType.ntClosed, result);
  4153. return result;
  4154. }
  4155. //------------------------------------------------------------------------------
  4156. } //end Clipper
  4157. internal class ClipperOffset
  4158. {
  4159. private Paths m_destPolys;
  4160. private Path m_srcPoly;
  4161. private Path m_destPoly;
  4162. private List<DoublePoint> m_normals = new List<DoublePoint>();
  4163. private double m_delta, m_sinA, m_sin, m_cos;
  4164. private double m_StepsPerRad;
  4165. private IntPoint m_lowest;
  4166. private PolyNode m_polyNodes = new PolyNode();
  4167. public double ArcTolerance { get; set; }
  4168. private const double two_pi = Math.PI * 2;
  4169. private const double def_arc_tolerance = 0.25;
  4170. public ClipperOffset(double arcTolerance = def_arc_tolerance)
  4171. {
  4172. ArcTolerance = arcTolerance;
  4173. m_lowest.X = -1;
  4174. }
  4175. //------------------------------------------------------------------------------
  4176. public void Clear()
  4177. {
  4178. m_polyNodes.Childs.Clear();
  4179. m_lowest.X = -1;
  4180. }
  4181. //------------------------------------------------------------------------------
  4182. internal static ClipInt Round(double value)
  4183. {
  4184. return value < 0 ? (ClipInt)(value - 0.5) : (ClipInt)(value + 0.5);
  4185. }
  4186. //------------------------------------------------------------------------------
  4187. public void AddPath(Path path, JoinTypes joinType, EndTypes endType)
  4188. {
  4189. int highI = path.Count - 1;
  4190. if (highI < 0) return;
  4191. PolyNode newNode = new PolyNode();
  4192. newNode.m_jointype = joinType;
  4193. newNode.m_endtype = endType;
  4194. //strip duplicate points from path and also get index to the lowest point ...
  4195. if (endType == EndTypes.etClosedLine || endType == EndTypes.etClosedPolygon)
  4196. while (highI > 0 && path[0] == path[highI]) highI--;
  4197. newNode.m_polygon.Capacity = highI + 1;
  4198. newNode.m_polygon.Add(path[0]);
  4199. int j = 0, k = 0;
  4200. for (int i = 1; i <= highI; i++)
  4201. if (newNode.m_polygon[j] != path[i])
  4202. {
  4203. j++;
  4204. newNode.m_polygon.Add(path[i]);
  4205. if (path[i].Y > newNode.m_polygon[k].Y ||
  4206. (path[i].Y == newNode.m_polygon[k].Y &&
  4207. path[i].X < newNode.m_polygon[k].X)) k = j;
  4208. }
  4209. if (endType == EndTypes.etClosedPolygon && j < 2) return;
  4210. m_polyNodes.AddChild(newNode);
  4211. //if this path's lowest pt is lower than all the others then update m_lowest
  4212. if (endType != EndTypes.etClosedPolygon) return;
  4213. if (m_lowest.X < 0)
  4214. m_lowest = new IntPoint(m_polyNodes.ChildCount - 1, k);
  4215. else
  4216. {
  4217. IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X].m_polygon[(int)m_lowest.Y];
  4218. if (newNode.m_polygon[k].Y > ip.Y ||
  4219. (newNode.m_polygon[k].Y == ip.Y &&
  4220. newNode.m_polygon[k].X < ip.X))
  4221. m_lowest = new IntPoint(m_polyNodes.ChildCount - 1, k);
  4222. }
  4223. }
  4224. //------------------------------------------------------------------------------
  4225. public void AddPaths(Paths paths, JoinTypes joinType, EndTypes endType)
  4226. {
  4227. for (int i = 0; i < paths.Count; i++)
  4228. {
  4229. Path p = paths[i];
  4230. AddPath(p, joinType, endType);
  4231. }
  4232. }
  4233. //------------------------------------------------------------------------------
  4234. private void FixOrientations()
  4235. {
  4236. //fixup orientations of all closed paths if the orientation of the
  4237. //closed path with the lowermost vertex is wrong ...
  4238. if (m_lowest.X >= 0 &&
  4239. !Clipper.Orientation(m_polyNodes.Childs[(int)m_lowest.X].m_polygon))
  4240. {
  4241. for (int i = 0; i < m_polyNodes.ChildCount; i++)
  4242. {
  4243. PolyNode node = m_polyNodes.Childs[i];
  4244. if (node.m_endtype == EndTypes.etClosedPolygon ||
  4245. (node.m_endtype == EndTypes.etClosedLine &&
  4246. Clipper.Orientation(node.m_polygon)))
  4247. node.m_polygon.Reverse();
  4248. }
  4249. }
  4250. else
  4251. {
  4252. for (int i = 0; i < m_polyNodes.ChildCount; i++)
  4253. {
  4254. PolyNode node = m_polyNodes.Childs[i];
  4255. if (node.m_endtype == EndTypes.etClosedLine &&
  4256. !Clipper.Orientation(node.m_polygon))
  4257. node.m_polygon.Reverse();
  4258. }
  4259. }
  4260. }
  4261. //------------------------------------------------------------------------------
  4262. internal static DoublePoint GetUnitNormal(IntPoint pt1, IntPoint pt2)
  4263. {
  4264. double dx = (pt2.X - pt1.X);
  4265. double dy = (pt2.Y - pt1.Y);
  4266. if ((dx == 0) && (dy == 0)) return new DoublePoint();
  4267. double f = 1 * 1.0 / Math.Sqrt(dx * dx + dy * dy);
  4268. dx *= f;
  4269. dy *= f;
  4270. return new DoublePoint(dy, -dx);
  4271. }
  4272. //------------------------------------------------------------------------------
  4273. private void DoOffset(double delta)
  4274. {
  4275. m_destPolys = new Paths();
  4276. m_delta = delta;
  4277. //if Zero offset, just copy any CLOSED polygons to m_p and return ...
  4278. if (ClipperBase.near_zero(delta))
  4279. {
  4280. m_destPolys.Capacity = m_polyNodes.ChildCount;
  4281. for (int i = 0; i < m_polyNodes.ChildCount; i++)
  4282. {
  4283. PolyNode node = m_polyNodes.Childs[i];
  4284. if (node.m_endtype == EndTypes.etClosedPolygon)
  4285. m_destPolys.Add(node.m_polygon);
  4286. }
  4287. return;
  4288. }
  4289. double y;
  4290. if (ArcTolerance <= 0.0)
  4291. y = def_arc_tolerance;
  4292. else if (ArcTolerance > Math.Abs(delta) * def_arc_tolerance)
  4293. y = Math.Abs(delta) * def_arc_tolerance;
  4294. else
  4295. y = ArcTolerance;
  4296. //see offset_triginometry2.svg in the documentation folder ...
  4297. double steps = Math.PI / Math.Acos(1 - y / Math.Abs(delta));
  4298. m_sin = Math.Sin(two_pi / steps);
  4299. m_cos = Math.Cos(two_pi / steps);
  4300. m_StepsPerRad = steps / two_pi;
  4301. if (delta < 0.0) m_sin = -m_sin;
  4302. m_destPolys.Capacity = m_polyNodes.ChildCount * 2;
  4303. for (int i = 0; i < m_polyNodes.ChildCount; i++)
  4304. {
  4305. PolyNode node = m_polyNodes.Childs[i];
  4306. m_srcPoly = node.m_polygon;
  4307. int len = m_srcPoly.Count;
  4308. if (len == 0 || (delta <= 0 && (len < 3 ||
  4309. node.m_endtype != EndTypes.etClosedPolygon)))
  4310. continue;
  4311. m_destPoly = new Path();
  4312. if (len == 1)
  4313. {
  4314. if (node.m_jointype == JoinTypes.jtRound)
  4315. {
  4316. double X = 1.0, Y = 0.0;
  4317. for (int j = 1; j <= steps; j++)
  4318. {
  4319. m_destPoly.Add(new IntPoint(
  4320. Round(m_srcPoly[0].X + X * delta),
  4321. Round(m_srcPoly[0].Y + Y * delta)));
  4322. double X2 = X;
  4323. X = X * m_cos - m_sin * Y;
  4324. Y = X2 * m_sin + Y * m_cos;
  4325. }
  4326. }
  4327. else
  4328. {
  4329. double X = -1.0, Y = -1.0;
  4330. for (int j = 0; j < 4; ++j)
  4331. {
  4332. m_destPoly.Add(new IntPoint(
  4333. Round(m_srcPoly[0].X + X * delta),
  4334. Round(m_srcPoly[0].Y + Y * delta)));
  4335. if (X < 0) X = 1;
  4336. else if (Y < 0) Y = 1;
  4337. else X = -1;
  4338. }
  4339. }
  4340. m_destPolys.Add(m_destPoly);
  4341. continue;
  4342. }
  4343. //build m_normals ...
  4344. m_normals.Clear();
  4345. m_normals.Capacity = len;
  4346. for (int j = 0; j < len - 1; j++)
  4347. m_normals.Add(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
  4348. if (node.m_endtype == EndTypes.etClosedLine ||
  4349. node.m_endtype == EndTypes.etClosedPolygon)
  4350. m_normals.Add(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
  4351. else
  4352. m_normals.Add(new DoublePoint(m_normals[len - 2]));
  4353. if (node.m_endtype == EndTypes.etClosedPolygon)
  4354. {
  4355. int k = len - 1;
  4356. for (int j = 0; j < len; j++)
  4357. OffsetPoint(j, ref k, node.m_jointype);
  4358. m_destPolys.Add(m_destPoly);
  4359. }
  4360. else if (node.m_endtype == EndTypes.etClosedLine)
  4361. {
  4362. int k = len - 1;
  4363. for (int j = 0; j < len; j++)
  4364. OffsetPoint(j, ref k, node.m_jointype);
  4365. m_destPolys.Add(m_destPoly);
  4366. m_destPoly = new Path();
  4367. //re-build m_normals ...
  4368. DoublePoint n = m_normals[len - 1];
  4369. for (int j = len - 1; j > 0; j--)
  4370. m_normals[j] = new DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
  4371. m_normals[0] = new DoublePoint(-n.X, -n.Y);
  4372. k = 0;
  4373. for (int j = len - 1; j >= 0; j--)
  4374. OffsetPoint(j, ref k, node.m_jointype);
  4375. m_destPolys.Add(m_destPoly);
  4376. }
  4377. else
  4378. {
  4379. int k = 0;
  4380. for (int j = 1; j < len - 1; ++j)
  4381. OffsetPoint(j, ref k, node.m_jointype);
  4382. {
  4383. int j = len - 1;
  4384. k = len - 2;
  4385. m_sinA = 0;
  4386. m_normals[j] = new DoublePoint(-m_normals[j].X, -m_normals[j].Y);
  4387. DoRound(j, k);
  4388. }
  4389. //re-build m_normals ...
  4390. for (int j = len - 1; j > 0; j--)
  4391. m_normals[j] = new DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
  4392. m_normals[0] = new DoublePoint(-m_normals[1].X, -m_normals[1].Y);
  4393. k = len - 1;
  4394. for (int j = k - 1; j > 0; --j)
  4395. OffsetPoint(j, ref k, node.m_jointype);
  4396. {
  4397. k = 1;
  4398. m_sinA = 0;
  4399. DoRound(0, 1);
  4400. }
  4401. m_destPolys.Add(m_destPoly);
  4402. }
  4403. }
  4404. }
  4405. //------------------------------------------------------------------------------
  4406. public void Execute(ref Paths solution, double delta, int inputSize)
  4407. {
  4408. solution.Clear();
  4409. FixOrientations();
  4410. DoOffset(delta);
  4411. //now clean up 'corners' ...
  4412. Clipper clpr = new Clipper();
  4413. clpr.AddPaths(m_destPolys, PolyTypes.ptSubject, true);
  4414. clpr.LastIndex = inputSize - 1;
  4415. if (delta > 0)
  4416. {
  4417. clpr.Execute(ClipTypes.ctUnion, solution,
  4418. PolyFillTypes.pftPositive, PolyFillTypes.pftPositive);
  4419. }
  4420. else
  4421. {
  4422. IntRect r = Clipper.GetBounds(m_destPolys);
  4423. Path outer = new Path(4);
  4424. outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
  4425. outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
  4426. outer.Add(new IntPoint(r.right + 10, r.top - 10));
  4427. outer.Add(new IntPoint(r.left - 10, r.top - 10));
  4428. clpr.AddPath(outer, PolyTypes.ptSubject, true);
  4429. clpr.ReverseSolution = true;
  4430. clpr.Execute(ClipTypes.ctUnion, solution, PolyFillTypes.pftNegative, PolyFillTypes.pftNegative);
  4431. if (solution.Count > 0) solution.RemoveAt(0);
  4432. }
  4433. }
  4434. //------------------------------------------------------------------------------
  4435. public void Execute(ref PolyTree solution, double delta)
  4436. {
  4437. solution.Clear();
  4438. FixOrientations();
  4439. DoOffset(delta);
  4440. //now clean up 'corners' ...
  4441. Clipper clpr = new Clipper();
  4442. clpr.AddPaths(m_destPolys, PolyTypes.ptSubject, true);
  4443. if (delta > 0)
  4444. {
  4445. clpr.Execute(ClipTypes.ctUnion, solution,
  4446. PolyFillTypes.pftPositive, PolyFillTypes.pftPositive);
  4447. }
  4448. else
  4449. {
  4450. IntRect r = Clipper.GetBounds(m_destPolys);
  4451. Path outer = new Path(4);
  4452. outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
  4453. outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
  4454. outer.Add(new IntPoint(r.right + 10, r.top - 10));
  4455. outer.Add(new IntPoint(r.left - 10, r.top - 10));
  4456. clpr.AddPath(outer, PolyTypes.ptSubject, true);
  4457. clpr.ReverseSolution = true;
  4458. clpr.Execute(ClipTypes.ctUnion, solution, PolyFillTypes.pftNegative, PolyFillTypes.pftNegative);
  4459. //remove the outer PolyNode rectangle ...
  4460. if (solution.ChildCount == 1 && solution.Childs[0].ChildCount > 0)
  4461. {
  4462. PolyNode outerNode = solution.Childs[0];
  4463. solution.Childs.Capacity = outerNode.ChildCount;
  4464. solution.Childs[0] = outerNode.Childs[0];
  4465. solution.Childs[0].m_Parent = solution;
  4466. for (int i = 1; i < outerNode.ChildCount; i++)
  4467. solution.AddChild(outerNode.Childs[i]);
  4468. }
  4469. else
  4470. solution.Clear();
  4471. }
  4472. }
  4473. //------------------------------------------------------------------------------
  4474. void OffsetPoint(int j, ref int k, JoinTypes jointype)
  4475. {
  4476. //cross product ...
  4477. m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
  4478. if (Math.Abs(m_sinA * m_delta) < 1.0)
  4479. {
  4480. //dot product ...
  4481. double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y);
  4482. if (cosA > 0) // angle ==> 0 degrees
  4483. {
  4484. var item = new IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
  4485. Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta));
  4486. item.NX = m_normals[k].X; item.NY = m_normals[k].Y; item.N = j; item.D = 1;
  4487. m_destPoly.Add(item);
  4488. return;
  4489. }
  4490. //else angle ==> 180 degrees
  4491. }
  4492. else if (m_sinA > 1.0) m_sinA = 1.0;
  4493. else if (m_sinA < -1.0) m_sinA = -1.0;
  4494. if (m_sinA * m_delta < 0)
  4495. {
  4496. var pt = new IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
  4497. Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta));
  4498. pt.NX = m_normals[k].X; pt.NY = m_normals[k].Y;
  4499. m_destPoly.Add(pt);
  4500. pt = m_srcPoly[j];
  4501. pt.NX = m_normals[k].X; pt.NY = m_normals[k].Y; pt.N = j; pt.D = 1;
  4502. m_destPoly.Add(pt);
  4503. pt = new IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
  4504. Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta));
  4505. pt.NX = m_normals[j].X; pt.NY = m_normals[j].Y; pt.N = j; pt.D = 1;
  4506. m_destPoly.Add(pt);
  4507. }
  4508. else
  4509. switch (jointype)
  4510. {
  4511. case JoinTypes.jtRound: DoRound(j, k); break;
  4512. }
  4513. k = j;
  4514. }
  4515. //------------------------------------------------------------------------------
  4516. internal void DoSquare(int j, int k)
  4517. {
  4518. double dx = Math.Tan(Math.Atan2(m_sinA,
  4519. m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4);
  4520. var pt = new IntPoint(
  4521. Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)),
  4522. Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)));
  4523. pt.NX = m_normals[k].X - m_normals[k].Y * dx; pt.NY = m_normals[k].Y + m_normals[k].X * dx;
  4524. m_destPoly.Add(pt);
  4525. pt = new IntPoint(
  4526. Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)),
  4527. Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)));
  4528. pt.NX = m_normals[k].X + m_normals[k].Y * dx; pt.NY = m_normals[k].Y - m_normals[k].X * dx;
  4529. m_destPoly.Add(pt);
  4530. }
  4531. //------------------------------------------------------------------------------
  4532. internal void DoMiter(int j, int k, double r)
  4533. {
  4534. double q = m_delta / r;
  4535. var pt = new IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q),
  4536. Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q));
  4537. pt.NX = (m_normals[k].X + m_normals[j].X) * q; pt.NY = (m_normals[k].Y + m_normals[j].Y) * q;
  4538. m_destPoly.Add(pt);
  4539. }
  4540. //------------------------------------------------------------------------------
  4541. internal void DoRound(int j, int k)
  4542. {
  4543. double a = Math.Atan2(m_sinA,
  4544. m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
  4545. int steps = Math.Max((int)Round(m_StepsPerRad * Math.Abs(a)), 1);
  4546. double X = m_normals[k].X, Y = m_normals[k].Y, X2;
  4547. for (int i = 0; i < steps; ++i)
  4548. {
  4549. var pt = new IntPoint(
  4550. Round(m_srcPoly[j].X + X * m_delta),
  4551. Round(m_srcPoly[j].Y + Y * m_delta));
  4552. pt.NX = X; pt.NY = Y; pt.N = j; pt.D = 1;
  4553. m_destPoly.Add(pt);
  4554. X2 = X;
  4555. X = X * m_cos - m_sin * Y;
  4556. Y = X2 * m_sin + Y * m_cos;
  4557. }
  4558. var pt1 = new IntPoint(
  4559. Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
  4560. Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta));
  4561. pt1.NX = m_normals[j].X; pt1.NY = m_normals[j].Y; pt1.N = j; pt1.D = 1;
  4562. m_destPoly.Add(pt1);
  4563. }
  4564. //------------------------------------------------------------------------------
  4565. }
  4566. class ClipperException : Exception
  4567. {
  4568. public ClipperException(string description) : base(description) {}
  4569. }
  4570. //------------------------------------------------------------------------------
  4571. } //end ClipperLib namespace