Clipper.Engine.cs 141 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984
  1. 
  2. #nullable enable
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Runtime.CompilerServices;
  7. namespace Clipper2Lib
  8. {
  9. [Flags]
  10. public enum PointInPolygonResult
  11. {
  12. IsOn = 0,
  13. IsInside = 1,
  14. IsOutside = 2
  15. };
  16. [Flags]
  17. internal enum VertexFlags
  18. {
  19. None = 0,
  20. OpenStart = 1,
  21. OpenEnd = 2,
  22. LocalMax = 4,
  23. LocalMin = 8
  24. };
  25. internal class Vertex
  26. {
  27. public readonly Point64 pt;
  28. public Vertex? next;
  29. public Vertex? prev;
  30. public VertexFlags flags;
  31. public Vertex(Point64 pt, VertexFlags flags, Vertex? prev)
  32. {
  33. this.pt = pt;
  34. this.flags = flags;
  35. next = null;
  36. this.prev = prev;
  37. }
  38. };
  39. internal readonly struct LocalMinima
  40. {
  41. public readonly Vertex vertex;
  42. public readonly PathType polytype;
  43. public readonly bool isOpen;
  44. public LocalMinima(Vertex vertex, PathType polytype, bool isOpen = false)
  45. {
  46. this.vertex = vertex;
  47. this.polytype = polytype;
  48. this.isOpen = isOpen;
  49. }
  50. public static bool operator ==(LocalMinima lm1, LocalMinima lm2)
  51. {
  52. return ReferenceEquals(lm1.vertex, lm2.vertex);
  53. }
  54. public static bool operator !=(LocalMinima lm1, LocalMinima lm2)
  55. {
  56. return !(lm1 == lm2);
  57. }
  58. public override bool Equals(object? obj)
  59. {
  60. return obj is LocalMinima minima && this == minima;
  61. }
  62. public override int GetHashCode()
  63. {
  64. return vertex.GetHashCode();
  65. }
  66. };
  67. // IntersectNode: a structure representing 2 intersecting edges.
  68. // Intersections must be sorted so they are processed from the largest
  69. // Y coordinates to the smallest while keeping edges adjacent.
  70. internal struct IntersectNode
  71. {
  72. public readonly Point64 pt;
  73. public readonly Active edge1;
  74. public readonly Active edge2;
  75. public IntersectNode(Point64 pt, Active edge1, Active edge2)
  76. {
  77. this.pt = pt;
  78. this.edge1 = edge1;
  79. this.edge2 = edge2;
  80. }
  81. };
  82. internal struct LocMinSorter : IComparer<LocalMinima>
  83. {
  84. public int Compare(LocalMinima locMin1, LocalMinima locMin2)
  85. {
  86. return locMin2.vertex.pt.Y.CompareTo(locMin1.vertex.pt.Y);
  87. }
  88. }
  89. // OutPt: vertex data structure for clipping solutions
  90. internal class OutPt
  91. {
  92. public Point64 pt;
  93. public OutPt? next;
  94. public OutPt prev;
  95. public OutRec outrec;
  96. public Joiner? joiner;
  97. public OutPt(Point64 pt, OutRec outrec)
  98. {
  99. this.pt = pt;
  100. this.outrec = outrec;
  101. next = this;
  102. prev = this;
  103. joiner = null;
  104. }
  105. };
  106. // OutRec: path data structure for clipping solutions
  107. internal class OutRec
  108. {
  109. public int idx;
  110. public OutRec? owner;
  111. public List<OutRec>? splits;
  112. public Active? frontEdge;
  113. public Active? backEdge;
  114. public OutPt? pts;
  115. public PolyPathNode? polypath;
  116. public Rect64 bounds;
  117. public Path64 path;
  118. public bool isOpen;
  119. public OutRec()
  120. {
  121. bounds = new Rect64();
  122. path = new Path64();
  123. }
  124. };
  125. // Joiner: structure used in merging "touching" solution polygons
  126. internal class Joiner
  127. {
  128. public int idx;
  129. public OutPt op1;
  130. public OutPt? op2;
  131. public Joiner? next1;
  132. public Joiner? next2;
  133. public Joiner? nextH;
  134. public Joiner(OutPt op1, OutPt? op2, Joiner? nextH)
  135. {
  136. idx = -1;
  137. this.nextH = nextH;
  138. this.op1 = op1;
  139. this.op2 = op2;
  140. next1 = op1.joiner;
  141. op1.joiner = this;
  142. if (op2 != null)
  143. {
  144. next2 = op2.joiner;
  145. op2.joiner = this;
  146. }
  147. else
  148. next2 = null;
  149. }
  150. }
  151. ///////////////////////////////////////////////////////////////////
  152. // Important: UP and DOWN here are premised on Y-axis positive down
  153. // displays, which is the orientation used in Clipper's development.
  154. ///////////////////////////////////////////////////////////////////
  155. internal class Active
  156. {
  157. public Point64 bot;
  158. public Point64 top;
  159. public long curX; // current (updated at every new scanline)
  160. public double dx;
  161. public int windDx; // 1 or -1 depending on winding direction
  162. public int windCount;
  163. public int windCount2; // winding count of the opposite polytype
  164. public OutRec? outrec;
  165. // AEL: 'active edge list' (Vatti's AET - active edge table)
  166. // a linked list of all edges (from left to right) that are present
  167. // (or 'active') within the current scanbeam (a horizontal 'beam' that
  168. // sweeps from bottom to top over the paths in the clipping operation).
  169. public Active? prevInAEL;
  170. public Active? nextInAEL;
  171. // SEL: 'sorted edge list' (Vatti's ST - sorted table)
  172. // linked list used when sorting edges into their new positions at the
  173. // top of scanbeams, but also (re)used to process horizontals.
  174. public Active? prevInSEL;
  175. public Active? nextInSEL;
  176. public Active? jump;
  177. public Vertex? vertexTop;
  178. public LocalMinima localMin; // the bottom of an edge 'bound' (also Vatti)
  179. internal bool isLeftBound;
  180. };
  181. public class ClipperBase
  182. {
  183. private ClipType _cliptype;
  184. private FillRule _fillrule;
  185. private Active? _actives;
  186. private Active? _sel;
  187. private Joiner? _horzJoiners;
  188. private readonly List<LocalMinima> _minimaList;
  189. private readonly List<IntersectNode> _intersectList;
  190. private readonly List<Vertex> _vertexList;
  191. private readonly List<OutRec> _outrecList;
  192. private readonly List<Joiner?> _joinerList;
  193. private readonly List<long> _scanlineList;
  194. private int _currentLocMin;
  195. private long _currentBotY;
  196. private bool _isSortedMinimaList;
  197. private bool _hasOpenPaths;
  198. internal bool _using_polytree;
  199. internal bool _succeeded;
  200. public bool PreserveCollinear { get; set; }
  201. public bool ReverseSolution { get; set; }
  202. #if USINGZ
  203. public delegate void ZCallback64(Point64 bot1, Point64 top1,
  204. Point64 bot2, Point64 top2, ref Point64 intersectPt);
  205. public long DefaultZ { get; set; }
  206. protected ZCallback64? _zCallback;
  207. #endif
  208. public ClipperBase()
  209. {
  210. _minimaList = new List<LocalMinima>();
  211. _intersectList = new List<IntersectNode>();
  212. _vertexList = new List<Vertex>();
  213. _outrecList = new List<OutRec>();
  214. _joinerList = new List<Joiner?>();
  215. _scanlineList = new List<long>();
  216. PreserveCollinear = true;
  217. }
  218. #if USINGZ
  219. private bool XYCoordsEqual(Point64 pt1, Point64 pt2)
  220. {
  221. return (pt1.X == pt2.X && pt1.Y == pt2.Y);
  222. }
  223. private void SetZ(Active e1, Active e2, ref Point64 intersectPt)
  224. {
  225. if (_zCallback == null) return;
  226. // prioritize subject vertices over clip vertices
  227. // and pass the subject vertices before clip vertices in the callback
  228. if (GetPolyType(e1) == PathType.Subject)
  229. {
  230. if (XYCoordsEqual(intersectPt, e1.bot))
  231. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e1.bot.Z);
  232. else if (XYCoordsEqual(intersectPt, e1.top))
  233. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e1.top.Z);
  234. else if (XYCoordsEqual(intersectPt, e2.bot))
  235. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e2.bot.Z);
  236. else if (XYCoordsEqual(intersectPt, e2.top))
  237. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e2.top.Z);
  238. else
  239. intersectPt = new Point64(intersectPt.X, intersectPt.Y, DefaultZ);
  240. _zCallback(e1.bot, e1.top, e2.bot, e2.top, ref intersectPt);
  241. }
  242. else
  243. {
  244. if (XYCoordsEqual(intersectPt, e2.bot))
  245. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e2.bot.Z);
  246. else if (XYCoordsEqual(intersectPt, e2.top))
  247. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e2.top.Z);
  248. else if (XYCoordsEqual(intersectPt, e1.bot))
  249. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e1.bot.Z);
  250. else if (XYCoordsEqual(intersectPt, e1.top))
  251. intersectPt = new Point64(intersectPt.X, intersectPt.Y, e1.top.Z);
  252. else
  253. intersectPt = new Point64(intersectPt.X, intersectPt.Y, DefaultZ);
  254. _zCallback(e2.bot, e2.top, e1.bot, e1.top, ref intersectPt);
  255. }
  256. }
  257. #endif
  258. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  259. private static bool IsOdd(int val)
  260. {
  261. return ((val & 1) != 0);
  262. }
  263. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  264. private static bool IsHotEdge(Active ae)
  265. {
  266. return ae.outrec != null;
  267. }
  268. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  269. private static bool IsOpen(Active ae)
  270. {
  271. return ae.localMin.isOpen;
  272. }
  273. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  274. private static bool IsOpenEnd(Active ae)
  275. {
  276. return ae.localMin.isOpen && IsOpenEnd(ae.vertexTop!);
  277. }
  278. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  279. private static bool IsOpenEnd(Vertex v)
  280. {
  281. return (v.flags & (VertexFlags.OpenStart | VertexFlags.OpenEnd)) != VertexFlags.None;
  282. }
  283. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  284. private static Active? GetPrevHotEdge(Active ae)
  285. {
  286. Active? prev = ae.prevInAEL;
  287. while (prev != null && (IsOpen(prev) || !IsHotEdge(prev)))
  288. prev = prev.prevInAEL;
  289. return prev;
  290. }
  291. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  292. private static bool IsFront(Active ae)
  293. {
  294. return (ae == ae.outrec!.frontEdge);
  295. }
  296. /*******************************************************************************
  297. * Dx: 0(90deg) *
  298. * | *
  299. * +inf (180deg) <--- o --. -inf (0deg) *
  300. *******************************************************************************/
  301. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  302. private static double GetDx(Point64 pt1, Point64 pt2)
  303. {
  304. double dy = pt2.Y - pt1.Y;
  305. if (dy != 0)
  306. return (pt2.X - pt1.X) / dy;
  307. if (pt2.X > pt1.X)
  308. return double.NegativeInfinity;
  309. return double.PositiveInfinity;
  310. }
  311. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  312. private static long TopX(Active ae, long currentY)
  313. {
  314. if ((currentY == ae.top.Y) || (ae.top.X == ae.bot.X)) return ae.top.X;
  315. if (currentY == ae.bot.Y) return ae.bot.X;
  316. return ae.bot.X + (long)Math.Round(ae.dx * (currentY - ae.bot.Y));
  317. }
  318. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  319. private static bool IsHorizontal(Active ae)
  320. {
  321. return (ae.top.Y == ae.bot.Y);
  322. }
  323. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  324. private static bool IsHeadingRightHorz(Active ae)
  325. {
  326. return (double.IsNegativeInfinity(ae.dx));
  327. }
  328. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  329. private static bool IsHeadingLeftHorz(Active ae)
  330. {
  331. return (double.IsPositiveInfinity(ae.dx));
  332. }
  333. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  334. private static void SwapActives(ref Active ae1, ref Active ae2)
  335. {
  336. (ae2, ae1) = (ae1, ae2);
  337. }
  338. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  339. private static PathType GetPolyType(Active ae)
  340. {
  341. return ae.localMin.polytype;
  342. }
  343. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  344. private static bool IsSamePolyType(Active ae1, Active ae2)
  345. {
  346. return ae1.localMin.polytype == ae2.localMin.polytype;
  347. }
  348. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  349. private static void SetDx(Active ae)
  350. {
  351. ae.dx = GetDx(ae.bot, ae.top);
  352. }
  353. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  354. private static Vertex NextVertex(Active ae)
  355. {
  356. if (ae.windDx > 0)
  357. return ae.vertexTop!.next!;
  358. return ae.vertexTop!.prev!;
  359. }
  360. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  361. private static Vertex PrevPrevVertex(Active ae)
  362. {
  363. if (ae.windDx > 0)
  364. return ae.vertexTop!.prev!.prev!;
  365. return ae.vertexTop!.next!.next!;
  366. }
  367. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  368. private static bool IsMaxima(Vertex vertex)
  369. {
  370. return ((vertex.flags & VertexFlags.LocalMax) != VertexFlags.None);
  371. }
  372. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  373. private static bool IsMaxima(Active ae)
  374. {
  375. return IsMaxima(ae.vertexTop!);
  376. }
  377. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  378. private static Active? GetMaximaPair(Active ae)
  379. {
  380. Active? ae2;
  381. ae2 = ae.nextInAEL;
  382. while (ae2 != null)
  383. {
  384. if (ae2.vertexTop == ae.vertexTop) return ae2; // Found!
  385. ae2 = ae2.nextInAEL;
  386. }
  387. return null;
  388. }
  389. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  390. private static Vertex? GetCurrYMaximaVertex(Active ae)
  391. {
  392. Vertex? result = ae.vertexTop;
  393. if (ae.windDx > 0)
  394. while (result!.next!.pt.Y == result.pt.Y) result = result.next;
  395. else
  396. while (result!.prev!.pt.Y == result.pt.Y) result = result.prev;
  397. if (!IsMaxima(result)) result = null; // not a maxima
  398. return result;
  399. }
  400. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  401. private static Active? GetHorzMaximaPair(Active horz, Vertex maxVert)
  402. {
  403. // we can't be sure whether the MaximaPair is on the left or right, so ...
  404. Active? result = horz.prevInAEL;
  405. while (result != null && result.curX >= maxVert.pt.X)
  406. {
  407. if (result.vertexTop == maxVert) return result; // Found!
  408. result = result.prevInAEL;
  409. }
  410. result = horz.nextInAEL;
  411. while (result != null && TopX(result, horz.top.Y) <= maxVert.pt.X)
  412. {
  413. if (result.vertexTop == maxVert) return result; // Found!
  414. result = result.nextInAEL;
  415. }
  416. return null;
  417. }
  418. private struct IntersectListSort : IComparer<IntersectNode>
  419. {
  420. public int Compare(IntersectNode a, IntersectNode b)
  421. {
  422. if (a.pt.Y == b.pt.Y)
  423. {
  424. if (a.pt.X == b.pt.X) return 0;
  425. return (a.pt.X < b.pt.X) ? -1 : 1;
  426. }
  427. return (a.pt.Y > b.pt.Y) ? -1 : 1;
  428. }
  429. }
  430. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  431. private static void SetSides(OutRec outrec, Active startEdge, Active endEdge)
  432. {
  433. outrec.frontEdge = startEdge;
  434. outrec.backEdge = endEdge;
  435. }
  436. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  437. private static void SwapOutrecs(Active ae1, Active ae2)
  438. {
  439. OutRec? or1 = ae1.outrec; // at least one edge has
  440. OutRec? or2 = ae2.outrec; // an assigned outrec
  441. if (or1 == or2)
  442. {
  443. Active? ae = or1!.frontEdge;
  444. or1.frontEdge = or1.backEdge;
  445. or1.backEdge = ae;
  446. return;
  447. }
  448. if (or1 != null)
  449. {
  450. if (ae1 == or1.frontEdge)
  451. or1.frontEdge = ae2;
  452. else
  453. or1.backEdge = ae2;
  454. }
  455. if (or2 != null)
  456. {
  457. if (ae2 == or2.frontEdge)
  458. or2.frontEdge = ae1;
  459. else
  460. or2.backEdge = ae1;
  461. }
  462. ae1.outrec = or2;
  463. ae2.outrec = or1;
  464. }
  465. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  466. private static double Area(OutPt op)
  467. {
  468. // https://en.wikipedia.org/wiki/Shoelace_formula
  469. double area = 0.0;
  470. OutPt op2 = op;
  471. do
  472. {
  473. area += (double)(op2.prev.pt.Y + op2.pt.Y) *
  474. (op2.prev.pt.X - op2.pt.X);
  475. op2 = op2.next!;
  476. } while (op2 != op);
  477. return area * 0.5;
  478. }
  479. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  480. private static double AreaTriangle(Point64 pt1, Point64 pt2, Point64 pt3)
  481. {
  482. return (double)(pt3.Y + pt1.Y) * (pt3.X - pt1.X) +
  483. (double)(pt1.Y + pt2.Y) * (pt1.X - pt2.X) +
  484. (double)(pt2.Y + pt3.Y) * (pt2.X - pt3.X);
  485. }
  486. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  487. private static OutRec? GetRealOutRec(OutRec? outRec)
  488. {
  489. while ((outRec != null) && (outRec.pts == null))
  490. outRec = outRec.owner;
  491. return outRec;
  492. }
  493. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  494. private static void UncoupleOutRec(Active ae)
  495. {
  496. OutRec? outrec = ae.outrec;
  497. if (outrec == null) return;
  498. outrec.frontEdge!.outrec = null;
  499. outrec.backEdge!.outrec = null;
  500. outrec.frontEdge = null;
  501. outrec.backEdge = null;
  502. }
  503. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  504. private static bool OutrecIsAscending(Active hotEdge)
  505. {
  506. return (hotEdge == hotEdge.outrec!.frontEdge);
  507. }
  508. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  509. private static void SwapFrontBackSides(OutRec outrec)
  510. {
  511. // while this proc. is needed for open paths
  512. // it's almost never needed for closed paths
  513. Active ae2 = outrec.frontEdge!;
  514. outrec.frontEdge = outrec.backEdge;
  515. outrec.backEdge = ae2;
  516. outrec.pts = outrec.pts!.next;
  517. }
  518. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  519. private static bool EdgesAdjacentInAEL(IntersectNode inode)
  520. {
  521. return (inode.edge1.nextInAEL == inode.edge2) || (inode.edge1.prevInAEL == inode.edge2);
  522. }
  523. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  524. protected void ClearSolution()
  525. {
  526. while (_actives != null) DeleteFromAEL(_actives);
  527. _scanlineList.Clear();
  528. DisposeIntersectNodes();
  529. _joinerList.Clear();
  530. _horzJoiners = null;
  531. _outrecList.Clear();
  532. }
  533. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  534. public void Clear()
  535. {
  536. ClearSolution();
  537. _minimaList.Clear();
  538. _vertexList.Clear();
  539. _currentLocMin = 0;
  540. _isSortedMinimaList = false;
  541. _hasOpenPaths = false;
  542. }
  543. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  544. protected void Reset()
  545. {
  546. if (!_isSortedMinimaList)
  547. {
  548. _minimaList.Sort(new LocMinSorter());
  549. _isSortedMinimaList = true;
  550. }
  551. _scanlineList.Capacity = _minimaList.Count;
  552. for (int i = _minimaList.Count - 1; i >= 0; i--)
  553. _scanlineList.Add(_minimaList[i].vertex.pt.Y);
  554. _currentBotY = 0;
  555. _currentLocMin = 0;
  556. _actives = null;
  557. _sel = null;
  558. _succeeded = true;
  559. }
  560. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  561. private void InsertScanline(long y)
  562. {
  563. int index = _scanlineList.BinarySearch(y);
  564. if (index >= 0) return;
  565. index = ~index;
  566. _scanlineList.Insert(index, y);
  567. }
  568. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  569. private bool PopScanline(out long y)
  570. {
  571. int cnt = _scanlineList.Count - 1;
  572. if (cnt < 0)
  573. {
  574. y = 0;
  575. return false;
  576. }
  577. y = _scanlineList[cnt];
  578. _scanlineList.RemoveAt(cnt--);
  579. while (cnt >= 0 && y == _scanlineList[cnt])
  580. _scanlineList.RemoveAt(cnt--);
  581. return true;
  582. }
  583. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  584. private bool HasLocMinAtY(long y)
  585. {
  586. return (_currentLocMin < _minimaList.Count && _minimaList[_currentLocMin].vertex.pt.Y == y);
  587. }
  588. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  589. private LocalMinima PopLocalMinima()
  590. {
  591. return _minimaList[_currentLocMin++];
  592. }
  593. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  594. private void AddLocMin(Vertex vert, PathType polytype, bool isOpen)
  595. {
  596. // make sure the vertex is added only once ...
  597. if ((vert.flags & VertexFlags.LocalMin) != VertexFlags.None) return;
  598. vert.flags |= VertexFlags.LocalMin;
  599. LocalMinima lm = new LocalMinima(vert, polytype, isOpen);
  600. _minimaList.Add(lm);
  601. }
  602. protected void AddPathsToVertexList(Paths64 paths, PathType polytype, bool isOpen)
  603. {
  604. int totalVertCnt = 0;
  605. foreach (Path64 path in paths) totalVertCnt += path.Count;
  606. _vertexList.Capacity = _vertexList.Count + totalVertCnt;
  607. foreach (Path64 path in paths)
  608. {
  609. Vertex? v0 = null, prev_v = null, curr_v;
  610. foreach (Point64 pt in path)
  611. {
  612. if (v0 == null)
  613. {
  614. v0 = new Vertex(pt, VertexFlags.None, null);
  615. _vertexList.Add(v0);
  616. prev_v = v0;
  617. }
  618. else if (prev_v!.pt != pt) // ie skips duplicates
  619. {
  620. curr_v = new Vertex(pt, VertexFlags.None, prev_v);
  621. _vertexList.Add(curr_v);
  622. prev_v.next = curr_v;
  623. prev_v = curr_v;
  624. }
  625. }
  626. if (prev_v == null || prev_v.prev == null) continue;
  627. if (!isOpen && prev_v.pt == v0!.pt) prev_v = prev_v.prev;
  628. prev_v.next = v0;
  629. v0!.prev = prev_v;
  630. if (!isOpen && prev_v.next == prev_v) continue;
  631. // OK, we have a valid path
  632. bool going_up, going_up0;
  633. if (isOpen)
  634. {
  635. curr_v = v0.next;
  636. while (curr_v != v0 && curr_v!.pt.Y == v0.pt.Y)
  637. curr_v = curr_v.next;
  638. going_up = curr_v.pt.Y <= v0.pt.Y;
  639. if (going_up)
  640. {
  641. v0.flags = VertexFlags.OpenStart;
  642. AddLocMin(v0, polytype, true);
  643. }
  644. else
  645. v0.flags = VertexFlags.OpenStart | VertexFlags.LocalMax;
  646. }
  647. else // closed path
  648. {
  649. prev_v = v0.prev;
  650. while (prev_v != v0 && prev_v!.pt.Y == v0.pt.Y)
  651. prev_v = prev_v.prev;
  652. if (prev_v == v0)
  653. continue; // only open paths can be completely flat
  654. going_up = prev_v.pt.Y > v0.pt.Y;
  655. }
  656. going_up0 = going_up;
  657. prev_v = v0;
  658. curr_v = v0.next;
  659. while (curr_v != v0)
  660. {
  661. if (curr_v!.pt.Y > prev_v.pt.Y && going_up)
  662. {
  663. prev_v.flags |= VertexFlags.LocalMax;
  664. going_up = false;
  665. }
  666. else if (curr_v.pt.Y < prev_v.pt.Y && !going_up)
  667. {
  668. going_up = true;
  669. AddLocMin(prev_v, polytype, isOpen);
  670. }
  671. prev_v = curr_v;
  672. curr_v = curr_v.next;
  673. }
  674. if (isOpen)
  675. {
  676. prev_v.flags |= VertexFlags.OpenEnd;
  677. if (going_up)
  678. prev_v.flags |= VertexFlags.LocalMax;
  679. else
  680. AddLocMin(prev_v, polytype, isOpen);
  681. }
  682. else if (going_up != going_up0)
  683. {
  684. if (going_up0) AddLocMin(prev_v, polytype, false);
  685. else prev_v.flags |= VertexFlags.LocalMax;
  686. }
  687. }
  688. }
  689. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  690. public void AddSubject(Path64 path)
  691. {
  692. AddPath(path, PathType.Subject);
  693. }
  694. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  695. public void AddOpenSubject(Path64 path)
  696. {
  697. AddPath(path, PathType.Subject, true);
  698. }
  699. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  700. public void AddClip(Path64 path)
  701. {
  702. AddPath(path, PathType.Clip);
  703. }
  704. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  705. protected void AddPath(Path64 path, PathType polytype, bool isOpen = false)
  706. {
  707. Paths64 tmp = new Paths64(1) { path };
  708. AddPaths(tmp, polytype, isOpen);
  709. }
  710. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  711. protected void AddPaths(Paths64 paths, PathType polytype, bool isOpen = false)
  712. {
  713. if (isOpen) _hasOpenPaths = true;
  714. _isSortedMinimaList = false;
  715. AddPathsToVertexList(paths, polytype, isOpen);
  716. }
  717. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  718. private bool IsContributingClosed(Active ae)
  719. {
  720. switch (_fillrule)
  721. {
  722. case FillRule.Positive:
  723. if (ae.windCount != 1) return false;
  724. break;
  725. case FillRule.Negative:
  726. if (ae.windCount != -1) return false;
  727. break;
  728. case FillRule.NonZero:
  729. if (Math.Abs(ae.windCount) != 1) return false;
  730. break;
  731. }
  732. switch (_cliptype)
  733. {
  734. case ClipType.Intersection:
  735. return _fillrule switch
  736. {
  737. FillRule.Positive => ae.windCount2 > 0,
  738. FillRule.Negative => ae.windCount2 < 0,
  739. _ => ae.windCount2 != 0,
  740. };
  741. case ClipType.Union:
  742. return _fillrule switch
  743. {
  744. FillRule.Positive => ae.windCount2 <= 0,
  745. FillRule.Negative => ae.windCount2 >= 0,
  746. _ => ae.windCount2 == 0,
  747. };
  748. case ClipType.Difference:
  749. bool result = _fillrule switch
  750. {
  751. FillRule.Positive => (ae.windCount2 <= 0),
  752. FillRule.Negative => (ae.windCount2 >= 0),
  753. _ => (ae.windCount2 == 0),
  754. };
  755. return (GetPolyType(ae) == PathType.Subject) ? result : !result;
  756. case ClipType.Xor:
  757. return true; // XOr is always contributing unless open
  758. default:
  759. return false;
  760. }
  761. }
  762. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  763. private bool IsContributingOpen(Active ae)
  764. {
  765. bool isInClip, isInSubj;
  766. switch (_fillrule)
  767. {
  768. case FillRule.Positive:
  769. isInSubj = ae.windCount > 0;
  770. isInClip = ae.windCount2 > 0;
  771. break;
  772. case FillRule.Negative:
  773. isInSubj = ae.windCount < 0;
  774. isInClip = ae.windCount2 < 0;
  775. break;
  776. default:
  777. isInSubj = ae.windCount != 0;
  778. isInClip = ae.windCount2 != 0;
  779. break;
  780. }
  781. bool result = _cliptype switch
  782. {
  783. ClipType.Intersection => isInClip,
  784. ClipType.Union => !isInSubj && !isInClip,
  785. _ => !isInClip
  786. };
  787. return result;
  788. }
  789. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  790. private void SetWindCountForClosedPathEdge(Active ae)
  791. {
  792. // Wind counts refer to polygon regions not edges, so here an edge's WindCnt
  793. // indicates the higher of the wind counts for the two regions touching the
  794. // edge. (nb: Adjacent regions can only ever have their wind counts differ by
  795. // one. Also, open paths have no meaningful wind directions or counts.)
  796. Active? ae2 = ae.prevInAEL;
  797. // find the nearest closed path edge of the same PolyType in AEL (heading left)
  798. PathType pt = GetPolyType(ae);
  799. while (ae2 != null && (GetPolyType(ae2) != pt || IsOpen(ae2))) ae2 = ae2.prevInAEL;
  800. if (ae2 == null)
  801. {
  802. ae.windCount = ae.windDx;
  803. ae2 = _actives;
  804. }
  805. else if (_fillrule == FillRule.EvenOdd)
  806. {
  807. ae.windCount = ae.windDx;
  808. ae.windCount2 = ae2.windCount2;
  809. ae2 = ae2.nextInAEL;
  810. }
  811. else
  812. {
  813. // NonZero, positive, or negative filling here ...
  814. // when e2's WindCnt is in the SAME direction as its WindDx,
  815. // then polygon will fill on the right of 'e2' (and 'e' will be inside)
  816. // nb: neither e2.WindCnt nor e2.WindDx should ever be 0.
  817. if (ae2.windCount * ae2.windDx < 0)
  818. {
  819. // opposite directions so 'ae' is outside 'ae2' ...
  820. if (Math.Abs(ae2.windCount) > 1)
  821. {
  822. // outside prev poly but still inside another.
  823. if (ae2.windDx * ae.windDx < 0)
  824. // reversing direction so use the same WC
  825. ae.windCount = ae2.windCount;
  826. else
  827. // otherwise keep 'reducing' the WC by 1 (i.e. towards 0) ...
  828. ae.windCount = ae2.windCount + ae.windDx;
  829. }
  830. else
  831. // now outside all polys of same polytype so set own WC ...
  832. ae.windCount = (IsOpen(ae) ? 1 : ae.windDx);
  833. }
  834. else
  835. {
  836. //'ae' must be inside 'ae2'
  837. if (ae2.windDx * ae.windDx < 0)
  838. // reversing direction so use the same WC
  839. ae.windCount = ae2.windCount;
  840. else
  841. // otherwise keep 'increasing' the WC by 1 (i.e. away from 0) ...
  842. ae.windCount = ae2.windCount + ae.windDx;
  843. }
  844. ae.windCount2 = ae2.windCount2;
  845. ae2 = ae2.nextInAEL; // i.e. get ready to calc WindCnt2
  846. }
  847. // update windCount2 ...
  848. if (_fillrule == FillRule.EvenOdd)
  849. while (ae2 != ae)
  850. {
  851. if (GetPolyType(ae2!) != pt && !IsOpen(ae2!))
  852. ae.windCount2 = (ae.windCount2 == 0 ? 1 : 0);
  853. ae2 = ae2!.nextInAEL;
  854. }
  855. else
  856. while (ae2 != ae)
  857. {
  858. if (GetPolyType(ae2!) != pt && !IsOpen(ae2!))
  859. ae.windCount2 += ae2!.windDx;
  860. ae2 = ae2!.nextInAEL;
  861. }
  862. }
  863. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  864. private void SetWindCountForOpenPathEdge(Active ae)
  865. {
  866. Active? ae2 = _actives;
  867. if (_fillrule == FillRule.EvenOdd)
  868. {
  869. int cnt1 = 0, cnt2 = 0;
  870. while (ae2 != ae)
  871. {
  872. if (GetPolyType(ae2!) == PathType.Clip)
  873. cnt2++;
  874. else if (!IsOpen(ae2!))
  875. cnt1++;
  876. ae2 = ae2!.nextInAEL;
  877. }
  878. ae.windCount = (IsOdd(cnt1) ? 1 : 0);
  879. ae.windCount2 = (IsOdd(cnt2) ? 1 : 0);
  880. }
  881. else
  882. {
  883. while (ae2 != ae)
  884. {
  885. if (GetPolyType(ae2!) == PathType.Clip)
  886. ae.windCount2 += ae2!.windDx;
  887. else if (!IsOpen(ae2!))
  888. ae.windCount += ae2!.windDx;
  889. ae2 = ae2!.nextInAEL;
  890. }
  891. }
  892. }
  893. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  894. private static bool IsValidAelOrder(Active resident, Active newcomer)
  895. {
  896. if (newcomer.curX != resident.curX)
  897. return newcomer.curX > resident.curX;
  898. // get the turning direction a1.top, a2.bot, a2.top
  899. double d = InternalClipper.CrossProduct(resident.top, newcomer.bot, newcomer.top);
  900. if (d != 0) return (d < 0);
  901. // edges must be collinear to get here
  902. // for starting open paths, place them according to
  903. // the direction they're about to turn
  904. if (!IsMaxima(resident) && (resident.top.Y > newcomer.top.Y))
  905. {
  906. return InternalClipper.CrossProduct(newcomer.bot,
  907. resident.top, NextVertex(resident).pt) <= 0;
  908. }
  909. if (!IsMaxima(newcomer) && (newcomer.top.Y > resident.top.Y))
  910. {
  911. return InternalClipper.CrossProduct(newcomer.bot,
  912. newcomer.top, NextVertex(newcomer).pt) >= 0;
  913. }
  914. long y = newcomer.bot.Y;
  915. bool newcomerIsLeft = newcomer.isLeftBound;
  916. if (resident.bot.Y != y || resident.localMin.vertex.pt.Y != y)
  917. return newcomer.isLeftBound;
  918. // resident must also have just been inserted
  919. if (resident.isLeftBound != newcomerIsLeft)
  920. return newcomerIsLeft;
  921. if (InternalClipper.CrossProduct(PrevPrevVertex(resident).pt,
  922. resident.bot, resident.top) == 0) return true;
  923. // compare turning direction of the alternate bound
  924. return (InternalClipper.CrossProduct(PrevPrevVertex(resident).pt,
  925. newcomer.bot, PrevPrevVertex(newcomer).pt) > 0) == newcomerIsLeft;
  926. }
  927. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  928. private void InsertLeftEdge(Active ae)
  929. {
  930. Active ae2;
  931. if (_actives == null)
  932. {
  933. ae.prevInAEL = null;
  934. ae.nextInAEL = null;
  935. _actives = ae;
  936. }
  937. else if (!IsValidAelOrder(_actives, ae))
  938. {
  939. ae.prevInAEL = null;
  940. ae.nextInAEL = _actives;
  941. _actives.prevInAEL = ae;
  942. _actives = ae;
  943. }
  944. else
  945. {
  946. ae2 = _actives;
  947. while (ae2.nextInAEL != null && IsValidAelOrder(ae2.nextInAEL, ae))
  948. ae2 = ae2.nextInAEL;
  949. ae.nextInAEL = ae2.nextInAEL;
  950. if (ae2.nextInAEL != null) ae2.nextInAEL.prevInAEL = ae;
  951. ae.prevInAEL = ae2;
  952. ae2.nextInAEL = ae;
  953. }
  954. }
  955. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  956. private static void InsertRightEdge(Active ae, Active ae2)
  957. {
  958. ae2.nextInAEL = ae.nextInAEL;
  959. if (ae.nextInAEL != null) ae.nextInAEL.prevInAEL = ae2;
  960. ae2.prevInAEL = ae;
  961. ae.nextInAEL = ae2;
  962. }
  963. private void InsertLocalMinimaIntoAEL(long botY)
  964. {
  965. LocalMinima localMinima;
  966. Active? leftBound, rightBound;
  967. // Add any local minima (if any) at BotY ...
  968. // NB horizontal local minima edges should contain locMin.vertex.prev
  969. while (HasLocMinAtY(botY))
  970. {
  971. localMinima = PopLocalMinima();
  972. if ((localMinima.vertex.flags & VertexFlags.OpenStart) != VertexFlags.None)
  973. {
  974. leftBound = null;
  975. }
  976. else
  977. {
  978. leftBound = new Active
  979. {
  980. bot = localMinima.vertex.pt,
  981. curX = localMinima.vertex.pt.X,
  982. windDx = -1,
  983. vertexTop = localMinima.vertex.prev,
  984. top = localMinima.vertex.prev!.pt,
  985. outrec = null,
  986. localMin = localMinima
  987. };
  988. SetDx(leftBound);
  989. }
  990. if ((localMinima.vertex.flags & VertexFlags.OpenEnd) != VertexFlags.None)
  991. {
  992. rightBound = null;
  993. }
  994. else
  995. {
  996. rightBound = new Active
  997. {
  998. bot = localMinima.vertex.pt,
  999. curX = localMinima.vertex.pt.X,
  1000. windDx = 1,
  1001. vertexTop = localMinima.vertex.next, // i.e. ascending
  1002. top = localMinima.vertex.next!.pt,
  1003. outrec = null,
  1004. localMin = localMinima
  1005. };
  1006. SetDx(rightBound);
  1007. }
  1008. // Currently LeftB is just the descending bound and RightB is the ascending.
  1009. // Now if the LeftB isn't on the left of RightB then we need swap them.
  1010. if (leftBound != null && rightBound != null)
  1011. {
  1012. if (IsHorizontal(leftBound))
  1013. {
  1014. if (IsHeadingRightHorz(leftBound)) SwapActives(ref leftBound, ref rightBound);
  1015. }
  1016. else if (IsHorizontal(rightBound))
  1017. {
  1018. if (IsHeadingLeftHorz(rightBound)) SwapActives(ref leftBound, ref rightBound);
  1019. }
  1020. else if (leftBound.dx < rightBound.dx)
  1021. SwapActives(ref leftBound, ref rightBound);
  1022. //so when leftBound has windDx == 1, the polygon will be oriented
  1023. //counter-clockwise in Cartesian coords (clockwise with inverted Y).
  1024. }
  1025. else if (leftBound == null)
  1026. {
  1027. leftBound = rightBound;
  1028. rightBound = null;
  1029. }
  1030. bool contributing;
  1031. leftBound!.isLeftBound = true;
  1032. InsertLeftEdge(leftBound);
  1033. if (IsOpen(leftBound))
  1034. {
  1035. SetWindCountForOpenPathEdge(leftBound);
  1036. contributing = IsContributingOpen(leftBound);
  1037. }
  1038. else
  1039. {
  1040. SetWindCountForClosedPathEdge(leftBound);
  1041. contributing = IsContributingClosed(leftBound);
  1042. }
  1043. if (rightBound != null)
  1044. {
  1045. rightBound.windCount = leftBound.windCount;
  1046. rightBound.windCount2 = leftBound.windCount2;
  1047. InsertRightEdge(leftBound, rightBound); ///////
  1048. if (contributing)
  1049. {
  1050. AddLocalMinPoly(leftBound, rightBound, leftBound.bot, true);
  1051. if (!IsHorizontal(leftBound) && TestJoinWithPrev1(leftBound))
  1052. {
  1053. OutPt op = AddOutPt(leftBound.prevInAEL!, leftBound.bot);
  1054. AddJoin(op, leftBound.outrec!.pts!);
  1055. }
  1056. }
  1057. while (rightBound.nextInAEL != null &&
  1058. IsValidAelOrder(rightBound.nextInAEL, rightBound))
  1059. {
  1060. IntersectEdges(rightBound, rightBound.nextInAEL, rightBound.bot);
  1061. SwapPositionsInAEL(rightBound, rightBound.nextInAEL);
  1062. }
  1063. if (!IsHorizontal(rightBound) && TestJoinWithNext1(rightBound))
  1064. {
  1065. OutPt op = AddOutPt(rightBound.nextInAEL!, rightBound.bot);
  1066. AddJoin(rightBound.outrec!.pts!, op);
  1067. }
  1068. if (IsHorizontal(rightBound))
  1069. PushHorz(rightBound);
  1070. else
  1071. InsertScanline(rightBound.top.Y);
  1072. }
  1073. else if (contributing)
  1074. StartOpenPath(leftBound, leftBound.bot);
  1075. if (IsHorizontal(leftBound))
  1076. PushHorz(leftBound);
  1077. else
  1078. InsertScanline(leftBound.top.Y);
  1079. } // while (HasLocMinAtY())
  1080. }
  1081. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1082. private void PushHorz(Active ae)
  1083. {
  1084. ae.nextInSEL = _sel;
  1085. _sel = ae;
  1086. }
  1087. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1088. private bool PopHorz(out Active? ae)
  1089. {
  1090. ae = _sel;
  1091. if (_sel == null) return false;
  1092. _sel = _sel.nextInSEL;
  1093. return true;
  1094. }
  1095. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1096. private static bool TestJoinWithPrev1(Active e)
  1097. {
  1098. // this is marginally quicker than TestJoinWithPrev2
  1099. // but can only be used when e.PrevInAEL.currX is accurate
  1100. return IsHotEdge(e) && !IsOpen(e) &&
  1101. (e.prevInAEL != null) && (e.prevInAEL.curX == e.curX) &&
  1102. IsHotEdge(e.prevInAEL) && !IsOpen(e.prevInAEL) &&
  1103. (InternalClipper.CrossProduct(e.prevInAEL.top, e.bot, e.top) == 0);
  1104. }
  1105. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1106. private static bool TestJoinWithPrev2(Active e, Point64 currPt)
  1107. {
  1108. return IsHotEdge(e) && !IsOpen(e) &&
  1109. (e.prevInAEL != null) && !IsOpen(e.prevInAEL) &&
  1110. IsHotEdge(e.prevInAEL) && (e.prevInAEL.top.Y < e.bot.Y) &&
  1111. (Math.Abs(TopX(e.prevInAEL, currPt.Y) - currPt.X) < 2) &&
  1112. (InternalClipper.CrossProduct(e.prevInAEL.top, currPt, e.top) == 0);
  1113. }
  1114. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1115. private static bool TestJoinWithNext1(Active e)
  1116. {
  1117. // this is marginally quicker than TestJoinWithNext2
  1118. // but can only be used when e.NextInAEL.currX is accurate
  1119. return IsHotEdge(e) && !IsOpen(e) &&
  1120. (e.nextInAEL != null) && (e.nextInAEL.curX == e.curX) &&
  1121. IsHotEdge(e.nextInAEL) && !IsOpen(e.nextInAEL) &&
  1122. (InternalClipper.CrossProduct(e.nextInAEL.top, e.bot, e.top) == 0);
  1123. }
  1124. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1125. private static bool TestJoinWithNext2(Active e, Point64 currPt)
  1126. {
  1127. return IsHotEdge(e) && !IsOpen(e) &&
  1128. (e.nextInAEL != null) && !IsOpen(e.nextInAEL) &&
  1129. IsHotEdge(e.nextInAEL) && (e.nextInAEL.top.Y < e.bot.Y) &&
  1130. (Math.Abs(TopX(e.nextInAEL, currPt.Y) - currPt.X) < 2) &&
  1131. (InternalClipper.CrossProduct(e.nextInAEL.top, currPt, e.top) == 0);
  1132. }
  1133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1134. private OutPt AddLocalMinPoly(Active ae1, Active ae2, Point64 pt, bool isNew = false)
  1135. {
  1136. OutRec outrec = new OutRec();
  1137. _outrecList.Add(outrec);
  1138. outrec.idx = _outrecList.Count - 1;
  1139. outrec.pts = null;
  1140. outrec.polypath = null;
  1141. ae1.outrec = outrec;
  1142. ae2.outrec = outrec;
  1143. // Setting the owner and inner/outer states (above) is an essential
  1144. // precursor to setting edge 'sides' (ie left and right sides of output
  1145. // polygons) and hence the orientation of output paths ...
  1146. if (IsOpen(ae1))
  1147. {
  1148. outrec.owner = null;
  1149. outrec.isOpen = true;
  1150. if (ae1.windDx > 0)
  1151. SetSides(outrec, ae1, ae2);
  1152. else
  1153. SetSides(outrec, ae2, ae1);
  1154. }
  1155. else
  1156. {
  1157. outrec.isOpen = false;
  1158. Active? prevHotEdge = GetPrevHotEdge(ae1);
  1159. // e.windDx is the winding direction of the **input** paths
  1160. // and unrelated to the winding direction of output polygons.
  1161. // Output orientation is determined by e.outrec.frontE which is
  1162. // the ascending edge (see AddLocalMinPoly).
  1163. if (prevHotEdge != null)
  1164. {
  1165. outrec.owner = prevHotEdge.outrec;
  1166. if (OutrecIsAscending(prevHotEdge) == isNew)
  1167. SetSides(outrec, ae2, ae1);
  1168. else
  1169. SetSides(outrec, ae1, ae2);
  1170. }
  1171. else
  1172. {
  1173. outrec.owner = null;
  1174. if (isNew)
  1175. SetSides(outrec, ae1, ae2);
  1176. else
  1177. SetSides(outrec, ae2, ae1);
  1178. }
  1179. }
  1180. OutPt op = new OutPt(pt, outrec);
  1181. outrec.pts = op;
  1182. return op;
  1183. }
  1184. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1185. private OutPt? AddLocalMaxPoly(Active ae1, Active ae2, Point64 pt)
  1186. {
  1187. if (IsFront(ae1) == IsFront(ae2))
  1188. {
  1189. if (IsOpenEnd(ae1))
  1190. SwapFrontBackSides(ae1.outrec!);
  1191. else if (IsOpenEnd(ae2))
  1192. SwapFrontBackSides(ae2.outrec!);
  1193. else
  1194. {
  1195. _succeeded = false;
  1196. return null;
  1197. }
  1198. }
  1199. OutPt result = AddOutPt(ae1, pt);
  1200. if (ae1.outrec == ae2.outrec)
  1201. {
  1202. OutRec outrec = ae1.outrec!;
  1203. outrec.pts = result;
  1204. UncoupleOutRec(ae1);
  1205. if (!IsOpen(ae1))
  1206. CleanCollinear(outrec);
  1207. result = outrec.pts;
  1208. outrec.owner = GetRealOutRec(outrec.owner);
  1209. if (_using_polytree && outrec.owner is { frontEdge: null })
  1210. outrec.owner = GetRealOutRec(outrec.owner.owner);
  1211. }
  1212. // and to preserve the winding orientation of outrec ...
  1213. else if (IsOpen(ae1))
  1214. {
  1215. if (ae1.windDx < 0)
  1216. JoinOutrecPaths(ae1, ae2);
  1217. else
  1218. JoinOutrecPaths(ae2, ae1);
  1219. }
  1220. else if (ae1.outrec!.idx < ae2.outrec!.idx)
  1221. JoinOutrecPaths(ae1, ae2);
  1222. else
  1223. JoinOutrecPaths(ae2, ae1);
  1224. return result;
  1225. }
  1226. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1227. private static void JoinOutrecPaths(Active ae1, Active ae2)
  1228. {
  1229. // join ae2 outrec path onto ae1 outrec path and then delete ae2 outrec path
  1230. // pointers. (NB Only very rarely do the joining ends share the same coords.)
  1231. OutPt p1Start = ae1.outrec!.pts!;
  1232. OutPt p2Start = ae2.outrec!.pts!;
  1233. OutPt p1End = p1Start.next!;
  1234. OutPt p2End = p2Start.next!;
  1235. if (IsFront(ae1))
  1236. {
  1237. p2End.prev = p1Start;
  1238. p1Start.next = p2End;
  1239. p2Start.next = p1End;
  1240. p1End.prev = p2Start;
  1241. ae1.outrec.pts = p2Start;
  1242. // nb: if IsOpen(e1) then e1 & e2 must be a 'maximaPair'
  1243. ae1.outrec.frontEdge = ae2.outrec.frontEdge;
  1244. if (ae1.outrec.frontEdge != null)
  1245. ae1.outrec.frontEdge!.outrec = ae1.outrec;
  1246. }
  1247. else
  1248. {
  1249. p1End.prev = p2Start;
  1250. p2Start.next = p1End;
  1251. p1Start.next = p2End;
  1252. p2End.prev = p1Start;
  1253. ae1.outrec.backEdge = ae2.outrec.backEdge;
  1254. if (ae1.outrec.backEdge != null)
  1255. ae1.outrec.backEdge!.outrec = ae1.outrec;
  1256. }
  1257. // an owner must have a lower idx otherwise
  1258. // it won't be a valid owner
  1259. if (ae2.outrec.owner != null &&
  1260. ae2.outrec.owner.idx < ae1.outrec.idx)
  1261. {
  1262. if (ae1.outrec.owner == null || ae2.outrec.owner.idx < ae1.outrec.owner.idx)
  1263. ae1.outrec.owner = ae2.outrec.owner;
  1264. }
  1265. // after joining, the ae2.OutRec must contains no vertices ...
  1266. ae2.outrec.frontEdge = null;
  1267. ae2.outrec.backEdge = null;
  1268. ae2.outrec.pts = null;
  1269. ae2.outrec.owner = ae1.outrec; // this may be redundant
  1270. if (IsOpenEnd(ae1))
  1271. {
  1272. ae2.outrec.pts = ae1.outrec.pts;
  1273. ae1.outrec.pts = null;
  1274. }
  1275. // and ae1 and ae2 are maxima and are about to be dropped from the Actives list.
  1276. ae1.outrec = null;
  1277. ae2.outrec = null;
  1278. }
  1279. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1280. private static OutPt AddOutPt(Active ae, Point64 pt)
  1281. {
  1282. OutPt newOp;
  1283. // Outrec.OutPts: a circular doubly-linked-list of POutPt where ...
  1284. // opFront[.Prev]* ~~~> opBack & opBack == opFront.Next
  1285. OutRec outrec = ae.outrec!;
  1286. bool toFront = IsFront(ae);
  1287. OutPt opFront = outrec.pts!;
  1288. OutPt opBack = opFront.next!;
  1289. if (toFront && (pt == opFront.pt)) newOp = opFront;
  1290. else if (!toFront && (pt == opBack.pt)) newOp = opBack;
  1291. else
  1292. {
  1293. newOp = new OutPt(pt, outrec);
  1294. opBack.prev = newOp;
  1295. newOp.prev = opFront;
  1296. newOp.next = opBack;
  1297. opFront.next = newOp;
  1298. if (toFront) outrec.pts = newOp;
  1299. }
  1300. return newOp;
  1301. }
  1302. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1303. private OutPt StartOpenPath(Active ae, Point64 pt)
  1304. {
  1305. OutRec outrec = new OutRec();
  1306. _outrecList.Add(outrec);
  1307. outrec.idx = _outrecList.Count - 1;
  1308. outrec.owner = null;
  1309. outrec.isOpen = true;
  1310. outrec.pts = null;
  1311. outrec.polypath = null;
  1312. if (ae.windDx > 0)
  1313. {
  1314. outrec.frontEdge = ae;
  1315. outrec.backEdge = null;
  1316. }
  1317. else
  1318. {
  1319. outrec.frontEdge = null;
  1320. outrec.backEdge = ae;
  1321. }
  1322. ae.outrec = outrec;
  1323. OutPt op = new OutPt(pt, outrec);
  1324. outrec.pts = op;
  1325. return op;
  1326. }
  1327. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1328. private void UpdateEdgeIntoAEL(Active ae)
  1329. {
  1330. ae.bot = ae.top;
  1331. ae.vertexTop = NextVertex(ae);
  1332. ae.top = ae.vertexTop!.pt;
  1333. ae.curX = ae.bot.X;
  1334. SetDx(ae);
  1335. if (IsHorizontal(ae)) return;
  1336. InsertScanline(ae.top.Y);
  1337. if (TestJoinWithPrev1(ae))
  1338. {
  1339. OutPt op1 = AddOutPt(ae.prevInAEL!, ae.bot);
  1340. OutPt op2 = AddOutPt(ae, ae.bot);
  1341. AddJoin(op1, op2);
  1342. }
  1343. }
  1344. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1345. private static Active? FindEdgeWithMatchingLocMin(Active e)
  1346. {
  1347. Active? result = e.nextInAEL;
  1348. while (result != null)
  1349. {
  1350. if (result.localMin == e.localMin) return result;
  1351. if (!IsHorizontal(result) && e.bot != result.bot) result = null;
  1352. else result = result.nextInAEL;
  1353. }
  1354. result = e.prevInAEL;
  1355. while (result != null)
  1356. {
  1357. if (result.localMin == e.localMin) return result;
  1358. if (!IsHorizontal(result) && e.bot != result.bot) return null;
  1359. result = result.prevInAEL;
  1360. }
  1361. return result;
  1362. }
  1363. private OutPt? IntersectEdges(Active ae1, Active ae2, Point64 pt)
  1364. {
  1365. OutPt? resultOp = null;
  1366. // MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
  1367. if (_hasOpenPaths && (IsOpen(ae1) || IsOpen(ae2)))
  1368. {
  1369. if (IsOpen(ae1) && IsOpen(ae2)) return null;
  1370. // the following line avoids duplicating quite a bit of code
  1371. if (IsOpen(ae2)) SwapActives(ref ae1, ref ae2);
  1372. if (_cliptype == ClipType.Union)
  1373. {
  1374. if (!IsHotEdge(ae2)) return null;
  1375. }
  1376. else if (ae2.localMin.polytype == PathType.Subject)
  1377. return null;
  1378. switch (_fillrule)
  1379. {
  1380. case FillRule.Positive:
  1381. if (ae2.windCount != 1) return null; break;
  1382. case FillRule.Negative:
  1383. if (ae2.windCount != -1) return null; break;
  1384. default:
  1385. if (Math.Abs(ae2.windCount) != 1) return null; break;
  1386. }
  1387. // toggle contribution ...
  1388. if (IsHotEdge(ae1))
  1389. {
  1390. resultOp = AddOutPt(ae1, pt);
  1391. #if USINGZ
  1392. SetZ(ae1, ae2, ref resultOp.pt);
  1393. #endif
  1394. if (IsFront(ae1))
  1395. ae1.outrec!.frontEdge = null;
  1396. else
  1397. ae1.outrec!.backEdge = null;
  1398. ae1.outrec = null;
  1399. }
  1400. // horizontal edges can pass under open paths at a LocMins
  1401. else if (pt == ae1.localMin.vertex.pt &&
  1402. !IsOpenEnd(ae1.localMin.vertex))
  1403. {
  1404. // find the other side of the LocMin and
  1405. // if it's 'hot' join up with it ...
  1406. Active? ae3 = FindEdgeWithMatchingLocMin(ae1);
  1407. if (ae3 != null && IsHotEdge(ae3))
  1408. {
  1409. ae1.outrec = ae3.outrec;
  1410. if (ae1.windDx > 0)
  1411. SetSides(ae3.outrec!, ae1, ae3);
  1412. else
  1413. SetSides(ae3.outrec!, ae3, ae1);
  1414. return ae3.outrec!.pts;
  1415. }
  1416. resultOp = StartOpenPath(ae1, pt);
  1417. }
  1418. else
  1419. resultOp = StartOpenPath(ae1, pt);
  1420. #if USINGZ
  1421. SetZ(ae1, ae2, ref resultOp.pt);
  1422. #endif
  1423. return resultOp;
  1424. }
  1425. // MANAGING CLOSED PATHS FROM HERE ON
  1426. // UPDATE WINDING COUNTS...
  1427. int oldE1WindCount, oldE2WindCount;
  1428. if (ae1.localMin.polytype == ae2.localMin.polytype)
  1429. {
  1430. if (_fillrule == FillRule.EvenOdd)
  1431. {
  1432. oldE1WindCount = ae1.windCount;
  1433. ae1.windCount = ae2.windCount;
  1434. ae2.windCount = oldE1WindCount;
  1435. }
  1436. else
  1437. {
  1438. if (ae1.windCount + ae2.windDx == 0)
  1439. ae1.windCount = -ae1.windCount;
  1440. else
  1441. ae1.windCount += ae2.windDx;
  1442. if (ae2.windCount - ae1.windDx == 0)
  1443. ae2.windCount = -ae2.windCount;
  1444. else
  1445. ae2.windCount -= ae1.windDx;
  1446. }
  1447. }
  1448. else
  1449. {
  1450. if (_fillrule != FillRule.EvenOdd)
  1451. ae1.windCount2 += ae2.windDx;
  1452. else
  1453. ae1.windCount2 = (ae1.windCount2 == 0 ? 1 : 0);
  1454. if (_fillrule != FillRule.EvenOdd)
  1455. ae2.windCount2 -= ae1.windDx;
  1456. else
  1457. ae2.windCount2 = (ae2.windCount2 == 0 ? 1 : 0);
  1458. }
  1459. switch (_fillrule)
  1460. {
  1461. case FillRule.Positive:
  1462. oldE1WindCount = ae1.windCount;
  1463. oldE2WindCount = ae2.windCount;
  1464. break;
  1465. case FillRule.Negative:
  1466. oldE1WindCount = -ae1.windCount;
  1467. oldE2WindCount = -ae2.windCount;
  1468. break;
  1469. default:
  1470. oldE1WindCount = Math.Abs(ae1.windCount);
  1471. oldE2WindCount = Math.Abs(ae2.windCount);
  1472. break;
  1473. }
  1474. bool e1WindCountIs0or1 = oldE1WindCount == 0 || oldE1WindCount == 1;
  1475. bool e2WindCountIs0or1 = oldE2WindCount == 0 || oldE2WindCount == 1;
  1476. if ((!IsHotEdge(ae1) && !e1WindCountIs0or1) || (!IsHotEdge(ae2) && !e2WindCountIs0or1)) return null;
  1477. // NOW PROCESS THE INTERSECTION ...
  1478. // if both edges are 'hot' ...
  1479. if (IsHotEdge(ae1) && IsHotEdge(ae2))
  1480. {
  1481. if ((oldE1WindCount != 0 && oldE1WindCount != 1) || (oldE2WindCount != 0 && oldE2WindCount != 1) ||
  1482. (ae1.localMin.polytype != ae2.localMin.polytype && _cliptype != ClipType.Xor))
  1483. {
  1484. resultOp = AddLocalMaxPoly(ae1, ae2, pt);
  1485. #if USINGZ
  1486. if (resultOp != null)
  1487. SetZ(ae1, ae2, ref resultOp.pt);
  1488. #endif
  1489. }
  1490. else if (IsFront(ae1) || (ae1.outrec == ae2.outrec))
  1491. {
  1492. // this 'else if' condition isn't strictly needed but
  1493. // it's sensible to split polygons that ony touch at
  1494. // a common vertex (not at common edges).
  1495. resultOp = AddLocalMaxPoly(ae1, ae2, pt);
  1496. OutPt op2 = AddLocalMinPoly(ae1, ae2, pt);
  1497. #if USINGZ
  1498. if (resultOp != null)
  1499. SetZ(ae1, ae2, ref resultOp.pt);
  1500. SetZ(ae1, ae2, ref op2.pt);
  1501. #endif
  1502. if (resultOp != null && resultOp.pt == op2.pt &&
  1503. !IsHorizontal(ae1) && !IsHorizontal(ae2) &&
  1504. (InternalClipper.CrossProduct(ae1.bot, resultOp.pt, ae2.bot) == 0))
  1505. AddJoin(resultOp, op2);
  1506. }
  1507. else
  1508. {
  1509. // can't treat as maxima & minima
  1510. resultOp = AddOutPt(ae1, pt);
  1511. #if USINGZ
  1512. OutPt op2 = AddOutPt(ae2, pt);
  1513. SetZ(ae1, ae2, ref resultOp.pt);
  1514. SetZ(ae1, ae2, ref op2.pt);
  1515. #else
  1516. AddOutPt(ae2, pt);
  1517. #endif
  1518. SwapOutrecs(ae1, ae2);
  1519. }
  1520. }
  1521. // if one or other edge is 'hot' ...
  1522. else if (IsHotEdge(ae1))
  1523. {
  1524. resultOp = AddOutPt(ae1, pt);
  1525. #if USINGZ
  1526. SetZ(ae1, ae2, ref resultOp.pt);
  1527. #endif
  1528. SwapOutrecs(ae1, ae2);
  1529. }
  1530. else if (IsHotEdge(ae2))
  1531. {
  1532. resultOp = AddOutPt(ae2, pt);
  1533. #if USINGZ
  1534. SetZ(ae1, ae2, ref resultOp.pt);
  1535. #endif
  1536. SwapOutrecs(ae1, ae2);
  1537. }
  1538. // neither edge is 'hot'
  1539. else
  1540. {
  1541. long e1Wc2, e2Wc2;
  1542. switch (_fillrule)
  1543. {
  1544. case FillRule.Positive:
  1545. e1Wc2 = ae1.windCount2;
  1546. e2Wc2 = ae2.windCount2;
  1547. break;
  1548. case FillRule.Negative:
  1549. e1Wc2 = -ae1.windCount2;
  1550. e2Wc2 = -ae2.windCount2;
  1551. break;
  1552. default:
  1553. e1Wc2 = Math.Abs(ae1.windCount2);
  1554. e2Wc2 = Math.Abs(ae2.windCount2);
  1555. break;
  1556. }
  1557. if (!IsSamePolyType(ae1, ae2))
  1558. {
  1559. resultOp = AddLocalMinPoly(ae1, ae2, pt);
  1560. #if USINGZ
  1561. SetZ(ae1, ae2, ref resultOp.pt);
  1562. #endif
  1563. }
  1564. else if (oldE1WindCount == 1 && oldE2WindCount == 1)
  1565. {
  1566. resultOp = null;
  1567. switch (_cliptype)
  1568. {
  1569. case ClipType.Union:
  1570. if (e1Wc2 > 0 && e2Wc2 > 0) return null;
  1571. resultOp = AddLocalMinPoly(ae1, ae2, pt);
  1572. break;
  1573. case ClipType.Difference:
  1574. if (((GetPolyType(ae1) == PathType.Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
  1575. ((GetPolyType(ae1) == PathType.Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
  1576. {
  1577. resultOp = AddLocalMinPoly(ae1, ae2, pt);
  1578. }
  1579. break;
  1580. case ClipType.Xor:
  1581. resultOp = AddLocalMinPoly(ae1, ae2, pt);
  1582. break;
  1583. default: // ClipType.Intersection:
  1584. if (e1Wc2 <= 0 || e2Wc2 <= 0) return null;
  1585. resultOp = AddLocalMinPoly(ae1, ae2, pt);
  1586. break;
  1587. }
  1588. #if USINGZ
  1589. if (resultOp != null) SetZ(ae1, ae2, ref resultOp.pt);
  1590. #endif
  1591. }
  1592. }
  1593. return resultOp;
  1594. }
  1595. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1596. private void DeleteFromAEL(Active ae)
  1597. {
  1598. Active? prev = ae.prevInAEL;
  1599. Active? next = ae.nextInAEL;
  1600. if (prev == null && next == null && (ae != _actives)) return; // already deleted
  1601. if (prev != null)
  1602. prev.nextInAEL = next;
  1603. else
  1604. _actives = next;
  1605. if (next != null) next.prevInAEL = prev;
  1606. // delete &ae;
  1607. }
  1608. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1609. private void AdjustCurrXAndCopyToSEL(long topY)
  1610. {
  1611. Active? ae = _actives;
  1612. _sel = ae;
  1613. while (ae != null)
  1614. {
  1615. ae.prevInSEL = ae.prevInAEL;
  1616. ae.nextInSEL = ae.nextInAEL;
  1617. ae.jump = ae.nextInSEL;
  1618. ae.curX = TopX(ae, topY);
  1619. // NB don't update ae.curr.Y yet (see AddNewIntersectNode)
  1620. ae = ae.nextInAEL;
  1621. }
  1622. }
  1623. protected void ExecuteInternal(ClipType ct, FillRule fillRule)
  1624. {
  1625. if (ct == ClipType.None) return;
  1626. _fillrule = fillRule;
  1627. _cliptype = ct;
  1628. Reset();
  1629. if (!PopScanline(out long y)) return;
  1630. while (_succeeded)
  1631. {
  1632. InsertLocalMinimaIntoAEL(y);
  1633. Active? ae;
  1634. while (PopHorz(out ae)) DoHorizontal(ae!);
  1635. ConvertHorzTrialsToJoins();
  1636. _currentBotY = y; // bottom of scanbeam
  1637. if (!PopScanline(out y))
  1638. break; // y new top of scanbeam
  1639. DoIntersections(y);
  1640. DoTopOfScanbeam(y);
  1641. while (PopHorz(out ae)) DoHorizontal(ae!);
  1642. }
  1643. if (_succeeded) ProcessJoinList();
  1644. }
  1645. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1646. private void DoIntersections(long topY)
  1647. {
  1648. if (BuildIntersectList(topY))
  1649. {
  1650. ProcessIntersectList();
  1651. DisposeIntersectNodes();
  1652. }
  1653. }
  1654. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1655. private void DisposeIntersectNodes()
  1656. {
  1657. _intersectList.Clear();
  1658. }
  1659. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1660. private void AddNewIntersectNode(Active ae1, Active ae2, long topY)
  1661. {
  1662. if (!InternalClipper.GetIntersectPt(
  1663. ae1.bot, ae1.top, ae2.bot, ae2.top, out Point64 ip))
  1664. ip = new Point64(ae1.curX, topY);
  1665. if (ip.Y > _currentBotY || ip.Y < topY)
  1666. {
  1667. double absDx1 = Math.Abs(ae1.dx);
  1668. double absDx2 = Math.Abs(ae2.dx);
  1669. if (absDx1 > 100 && absDx2 > 100)
  1670. {
  1671. if (absDx1 > absDx2)
  1672. ip = InternalClipper.GetClosestPtOnSegment(ip, ae1.bot, ae1.top);
  1673. else
  1674. ip = InternalClipper.GetClosestPtOnSegment(ip, ae2.bot, ae2.top);
  1675. }
  1676. else if (absDx1 > 100)
  1677. ip = InternalClipper.GetClosestPtOnSegment(ip, ae1.bot, ae1.top);
  1678. else if (absDx2 > 100)
  1679. ip = InternalClipper.GetClosestPtOnSegment(ip, ae2.bot, ae2.top);
  1680. else
  1681. {
  1682. if (ip.Y < topY) ip.Y = topY;
  1683. else ip.Y = _currentBotY;
  1684. if (absDx1 < absDx2) ip.X = TopX(ae1, ip.Y);
  1685. else ip.X = TopX(ae2, topY);
  1686. }
  1687. }
  1688. IntersectNode node = new IntersectNode(ip, ae1, ae2);
  1689. _intersectList.Add(node);
  1690. }
  1691. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1692. private static Active? ExtractFromSEL(Active ae)
  1693. {
  1694. Active? res = ae.nextInSEL;
  1695. if (res != null)
  1696. res.prevInSEL = ae.prevInSEL;
  1697. ae.prevInSEL!.nextInSEL = res;
  1698. return res;
  1699. }
  1700. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1701. private static void Insert1Before2InSEL(Active ae1, Active ae2)
  1702. {
  1703. ae1.prevInSEL = ae2.prevInSEL;
  1704. if (ae1.prevInSEL != null)
  1705. ae1.prevInSEL.nextInSEL = ae1;
  1706. ae1.nextInSEL = ae2;
  1707. ae2.prevInSEL = ae1;
  1708. }
  1709. private bool BuildIntersectList(long topY)
  1710. {
  1711. if (_actives == null || _actives.nextInAEL == null) return false;
  1712. // Calculate edge positions at the top of the current scanbeam, and from this
  1713. // we will determine the intersections required to reach these new positions.
  1714. AdjustCurrXAndCopyToSEL(topY);
  1715. // Find all edge intersections in the current scanbeam using a stable merge
  1716. // sort that ensures only adjacent edges are intersecting. Intersect info is
  1717. // stored in FIntersectList ready to be processed in ProcessIntersectList.
  1718. // Re merge sorts see https://stackoverflow.com/a/46319131/359538
  1719. Active? left = _sel, right, lEnd, rEnd, currBase, prevBase, tmp;
  1720. while (left!.jump != null)
  1721. {
  1722. prevBase = null;
  1723. while (left != null && left.jump != null)
  1724. {
  1725. currBase = left;
  1726. right = left.jump;
  1727. lEnd = right;
  1728. rEnd = right.jump;
  1729. left.jump = rEnd;
  1730. while (left != lEnd && right != rEnd)
  1731. {
  1732. if (right!.curX < left!.curX)
  1733. {
  1734. tmp = right.prevInSEL!;
  1735. for (; ; )
  1736. {
  1737. AddNewIntersectNode(tmp, right, topY);
  1738. if (tmp == left) break;
  1739. tmp = tmp.prevInSEL!;
  1740. }
  1741. tmp = right;
  1742. right = ExtractFromSEL(tmp);
  1743. lEnd = right;
  1744. Insert1Before2InSEL(tmp, left);
  1745. if (left == currBase)
  1746. {
  1747. currBase = tmp;
  1748. currBase.jump = rEnd;
  1749. if (prevBase == null) _sel = currBase;
  1750. else prevBase.jump = currBase;
  1751. }
  1752. }
  1753. else left = left.nextInSEL;
  1754. }
  1755. prevBase = currBase;
  1756. left = rEnd;
  1757. }
  1758. left = _sel;
  1759. }
  1760. return _intersectList.Count > 0;
  1761. }
  1762. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1763. private void ProcessIntersectList()
  1764. {
  1765. // We now have a list of intersections required so that edges will be
  1766. // correctly positioned at the top of the scanbeam. However, it's important
  1767. // that edge intersections are processed from the bottom up, but it's also
  1768. // crucial that intersections only occur between adjacent edges.
  1769. // First we do a quicksort so intersections proceed in a bottom up order ...
  1770. _intersectList.Sort(new IntersectListSort());
  1771. // Now as we process these intersections, we must sometimes adjust the order
  1772. // to ensure that intersecting edges are always adjacent ...
  1773. for (int i = 0; i < _intersectList.Count; ++i)
  1774. {
  1775. if (!EdgesAdjacentInAEL(_intersectList[i]))
  1776. {
  1777. int j = i + 1;
  1778. while (!EdgesAdjacentInAEL(_intersectList[j])) j++;
  1779. // swap
  1780. (_intersectList[j], _intersectList[i]) =
  1781. (_intersectList[i], _intersectList[j]);
  1782. }
  1783. IntersectNode node = _intersectList[i];
  1784. IntersectEdges(node.edge1, node.edge2, node.pt);
  1785. SwapPositionsInAEL(node.edge1, node.edge2);
  1786. if (TestJoinWithPrev2(node.edge2, node.pt))
  1787. {
  1788. OutPt op1 = AddOutPt(node.edge2.prevInAEL!, node.pt);
  1789. OutPt op2 = AddOutPt(node.edge2, node.pt);
  1790. if (op1 != op2) AddJoin(op1, op2);
  1791. }
  1792. else if (TestJoinWithNext2(node.edge1, node.pt))
  1793. {
  1794. OutPt op1 = AddOutPt(node.edge1, node.pt);
  1795. OutPt op2 = AddOutPt(node.edge1.nextInAEL!, node.pt);
  1796. if (op1 != op2) AddJoin(op1, op2);
  1797. }
  1798. }
  1799. }
  1800. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1801. private void SwapPositionsInAEL(Active ae1, Active ae2)
  1802. {
  1803. // preconditon: ae1 must be immediately to the left of ae2
  1804. Active? next = ae2.nextInAEL;
  1805. if (next != null) next.prevInAEL = ae1;
  1806. Active? prev = ae1.prevInAEL;
  1807. if (prev != null) prev.nextInAEL = ae2;
  1808. ae2.prevInAEL = prev;
  1809. ae2.nextInAEL = ae1;
  1810. ae1.prevInAEL = ae2;
  1811. ae1.nextInAEL = next;
  1812. if (ae2.prevInAEL == null) _actives = ae2;
  1813. }
  1814. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1815. private static bool ResetHorzDirection(Active horz, Active? maxPair,
  1816. out long leftX, out long rightX)
  1817. {
  1818. if (horz.bot.X == horz.top.X)
  1819. {
  1820. // the horizontal edge is going nowhere ...
  1821. leftX = horz.curX;
  1822. rightX = horz.curX;
  1823. Active? ae = horz.nextInAEL;
  1824. while (ae != null && ae != maxPair) ae = ae.nextInAEL;
  1825. return ae != null;
  1826. }
  1827. if (horz.curX < horz.top.X)
  1828. {
  1829. leftX = horz.curX;
  1830. rightX = horz.top.X;
  1831. return true;
  1832. }
  1833. leftX = horz.top.X;
  1834. rightX = horz.curX;
  1835. return false; // right to left
  1836. }
  1837. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1838. private static bool HorzIsSpike(Active horz)
  1839. {
  1840. Point64 nextPt = NextVertex(horz).pt;
  1841. return (horz.bot.X < horz.top.X) != (horz.top.X < nextPt.X);
  1842. }
  1843. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1844. private static void TrimHorz(Active horzEdge, bool preserveCollinear)
  1845. {
  1846. bool wasTrimmed = false;
  1847. Point64 pt = NextVertex(horzEdge).pt;
  1848. while (pt.Y == horzEdge.top.Y)
  1849. {
  1850. // always trim 180 deg. spikes (in closed paths)
  1851. // but otherwise break if preserveCollinear = true
  1852. if (preserveCollinear &&
  1853. (pt.X < horzEdge.top.X) != (horzEdge.bot.X < horzEdge.top.X))
  1854. break;
  1855. horzEdge.vertexTop = NextVertex(horzEdge);
  1856. horzEdge.top = pt;
  1857. wasTrimmed = true;
  1858. if (IsMaxima(horzEdge)) break;
  1859. pt = NextVertex(horzEdge).pt;
  1860. }
  1861. if (wasTrimmed) SetDx(horzEdge); // +/-infinity
  1862. }
  1863. private void DoHorizontal(Active horz)
  1864. /*******************************************************************************
  1865. * Notes: Horizontal edges (HEs) at scanline intersections (i.e. at the top or *
  1866. * bottom of a scanbeam) are processed as if layered.The order in which HEs *
  1867. * are processed doesn't matter. HEs intersect with the bottom vertices of *
  1868. * other HEs[#] and with non-horizontal edges [*]. Once these intersections *
  1869. * are completed, intermediate HEs are 'promoted' to the next edge in their *
  1870. * bounds, and they in turn may be intersected[%] by other HEs. *
  1871. * *
  1872. * eg: 3 horizontals at a scanline: / | / / *
  1873. * | / | (HE3)o ========%========== o *
  1874. * o ======= o(HE2) / | / / *
  1875. * o ============#=========*======*========#=========o (HE1) *
  1876. * / | / | / *
  1877. *******************************************************************************/
  1878. {
  1879. Point64 pt;
  1880. bool horzIsOpen = IsOpen(horz);
  1881. long Y = horz.bot.Y;
  1882. Vertex? vertex_max = null;
  1883. Active? maxPair = null;
  1884. if (!horzIsOpen)
  1885. {
  1886. vertex_max = GetCurrYMaximaVertex(horz);
  1887. if (vertex_max != null)
  1888. {
  1889. maxPair = GetHorzMaximaPair(horz, vertex_max);
  1890. // remove 180 deg.spikes and also simplify
  1891. // consecutive horizontals when PreserveCollinear = true
  1892. if (vertex_max != horz.vertexTop)
  1893. TrimHorz(horz, PreserveCollinear);
  1894. }
  1895. }
  1896. bool isLeftToRight =
  1897. ResetHorzDirection(horz, maxPair, out long leftX, out long rightX);
  1898. if (IsHotEdge(horz))
  1899. AddOutPt(horz, new Point64(horz.curX, Y));
  1900. OutPt? op;
  1901. for (; ; )
  1902. {
  1903. if (horzIsOpen && IsMaxima(horz) && !IsOpenEnd(horz))
  1904. {
  1905. vertex_max = GetCurrYMaximaVertex(horz);
  1906. if (vertex_max != null)
  1907. maxPair = GetHorzMaximaPair(horz, vertex_max);
  1908. }
  1909. // loops through consec. horizontal edges (if open)
  1910. Active? ae;
  1911. if (isLeftToRight) ae = horz.nextInAEL;
  1912. else ae = horz.prevInAEL;
  1913. while (ae != null)
  1914. {
  1915. if (ae == maxPair)
  1916. {
  1917. if (IsHotEdge(horz))
  1918. {
  1919. while (horz.vertexTop != ae.vertexTop)
  1920. {
  1921. AddOutPt(horz, horz.top);
  1922. UpdateEdgeIntoAEL(horz);
  1923. }
  1924. op = AddLocalMaxPoly(horz, ae, horz.top);
  1925. if (op != null && !IsOpen(horz) && op.pt == horz.top)
  1926. AddTrialHorzJoin(op);
  1927. }
  1928. DeleteFromAEL(ae);
  1929. DeleteFromAEL(horz);
  1930. return;
  1931. }
  1932. // if horzEdge is a maxima, keep going until we reach
  1933. // its maxima pair, otherwise check for break conditions
  1934. if (vertex_max != horz.vertexTop || IsOpenEnd(horz))
  1935. {
  1936. // otherwise stop when 'ae' is beyond the end of the horizontal line
  1937. if ((isLeftToRight && ae.curX > rightX) ||
  1938. (!isLeftToRight && ae.curX < leftX)) break;
  1939. if (ae.curX == horz.top.X && !IsHorizontal(ae))
  1940. {
  1941. pt = NextVertex(horz).pt;
  1942. if (isLeftToRight)
  1943. {
  1944. // with open paths we'll only break once past horz's end
  1945. if (IsOpen(ae) && !IsSamePolyType(ae, horz) && !IsHotEdge(ae))
  1946. {
  1947. if (TopX(ae, pt.Y) > pt.X) break;
  1948. }
  1949. // otherwise we'll only break when horz's outslope is greater than e's
  1950. else if (TopX(ae, pt.Y) >= pt.X) break;
  1951. }
  1952. else
  1953. {
  1954. // with open paths we'll only break once past horz's end
  1955. if (IsOpen(ae) && !IsSamePolyType(ae, horz) && !IsHotEdge(ae))
  1956. {
  1957. if (TopX(ae, pt.Y) < pt.X) break;
  1958. }
  1959. // otherwise we'll only break when horz's outslope is greater than e's
  1960. else if (TopX(ae, pt.Y) <= pt.X) break;
  1961. }
  1962. }
  1963. }
  1964. pt = new Point64(ae.curX, Y);
  1965. if (isLeftToRight)
  1966. {
  1967. op = IntersectEdges(horz, ae, pt);
  1968. SwapPositionsInAEL(horz, ae);
  1969. if (IsHotEdge(horz) && op != null &&
  1970. !IsOpen(horz) && op.pt == pt)
  1971. AddTrialHorzJoin(op);
  1972. if (!IsHorizontal(ae) && TestJoinWithPrev1(ae))
  1973. {
  1974. op = AddOutPt(ae.prevInAEL!, pt);
  1975. OutPt op2 = AddOutPt(ae, pt);
  1976. AddJoin(op, op2);
  1977. }
  1978. horz.curX = ae.curX;
  1979. ae = horz.nextInAEL;
  1980. }
  1981. else
  1982. {
  1983. op = IntersectEdges(ae, horz, pt);
  1984. SwapPositionsInAEL(ae, horz);
  1985. if (IsHotEdge(horz) && op != null &&
  1986. !IsOpen(horz) && op.pt == pt)
  1987. AddTrialHorzJoin(op);
  1988. if (!IsHorizontal(ae) && TestJoinWithNext1(ae))
  1989. {
  1990. op = AddOutPt(ae, pt);
  1991. OutPt op2 = AddOutPt(ae.nextInAEL!, pt);
  1992. AddJoin(op, op2);
  1993. }
  1994. horz.curX = ae.curX;
  1995. ae = horz.prevInAEL;
  1996. }
  1997. } // we've reached the end of this horizontal
  1998. // check if we've finished looping through consecutive horizontals
  1999. if (horzIsOpen && IsOpenEnd(horz))
  2000. {
  2001. if (IsHotEdge(horz))
  2002. {
  2003. AddOutPt(horz, horz.top);
  2004. if (IsFront(horz))
  2005. horz.outrec!.frontEdge = null;
  2006. else
  2007. horz.outrec!.backEdge = null;
  2008. }
  2009. horz.outrec = null;
  2010. DeleteFromAEL(horz); // ie open at top
  2011. return;
  2012. }
  2013. if (NextVertex(horz).pt.Y != horz.top.Y) break;
  2014. // there must be a following (consecutive) horizontal
  2015. if (IsHotEdge(horz))
  2016. AddOutPt(horz, horz.top);
  2017. UpdateEdgeIntoAEL(horz);
  2018. if (PreserveCollinear && !horzIsOpen && HorzIsSpike(horz))
  2019. TrimHorz(horz, true);
  2020. isLeftToRight = ResetHorzDirection(horz, maxPair, out leftX, out rightX);
  2021. } // end for loop and end of (possible consecutive) horizontals
  2022. if (IsHotEdge(horz))
  2023. {
  2024. op = AddOutPt(horz, horz.top);
  2025. if (!IsOpen(horz))
  2026. AddTrialHorzJoin(op);
  2027. }
  2028. else
  2029. op = null;
  2030. if ((horzIsOpen && !IsOpenEnd(horz)) ||
  2031. (!horzIsOpen && vertex_max != horz.vertexTop))
  2032. {
  2033. UpdateEdgeIntoAEL(horz); // this is the end of an intermediate horiz.
  2034. if (IsOpen(horz)) return;
  2035. if (isLeftToRight && TestJoinWithNext1(horz))
  2036. {
  2037. OutPt op2 = AddOutPt(horz.nextInAEL!, horz.bot);
  2038. AddJoin(op!, op2);
  2039. }
  2040. else if (!isLeftToRight && TestJoinWithPrev1(horz))
  2041. {
  2042. OutPt op2 = AddOutPt(horz.prevInAEL!, horz.bot);
  2043. AddJoin(op2, op!);
  2044. }
  2045. }
  2046. else if (IsHotEdge(horz))
  2047. AddLocalMaxPoly(horz, maxPair!, horz.top);
  2048. else
  2049. {
  2050. DeleteFromAEL(maxPair!);
  2051. DeleteFromAEL(horz);
  2052. }
  2053. }
  2054. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2055. private void DoTopOfScanbeam(long y)
  2056. {
  2057. _sel = null; // sel_ is reused to flag horizontals (see PushHorz below)
  2058. Active? ae = _actives;
  2059. while (ae != null)
  2060. {
  2061. // NB 'ae' will never be horizontal here
  2062. if (ae.top.Y == y)
  2063. {
  2064. ae.curX = ae.top.X;
  2065. if (IsMaxima(ae))
  2066. {
  2067. ae = DoMaxima(ae); // TOP OF BOUND (MAXIMA)
  2068. continue;
  2069. }
  2070. // INTERMEDIATE VERTEX ...
  2071. if (IsHotEdge(ae))
  2072. AddOutPt(ae, ae.top);
  2073. UpdateEdgeIntoAEL(ae);
  2074. if (IsHorizontal(ae))
  2075. PushHorz(ae); // horizontals are processed later
  2076. }
  2077. else // i.e. not the top of the edge
  2078. ae.curX = TopX(ae, y);
  2079. ae = ae.nextInAEL;
  2080. }
  2081. }
  2082. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2083. private Active? DoMaxima(Active ae)
  2084. {
  2085. Active? prevE;
  2086. Active? nextE, maxPair;
  2087. prevE = ae.prevInAEL;
  2088. nextE = ae.nextInAEL;
  2089. if (IsOpenEnd(ae))
  2090. {
  2091. if (IsHotEdge(ae)) AddOutPt(ae, ae.top);
  2092. if (!IsHorizontal(ae))
  2093. {
  2094. if (IsHotEdge(ae))
  2095. {
  2096. if (IsFront(ae))
  2097. ae.outrec!.frontEdge = null;
  2098. else
  2099. ae.outrec!.backEdge = null;
  2100. ae.outrec = null;
  2101. }
  2102. DeleteFromAEL(ae);
  2103. }
  2104. return nextE;
  2105. }
  2106. maxPair = GetMaximaPair(ae);
  2107. if (maxPair == null) return nextE; // eMaxPair is horizontal
  2108. // only non-horizontal maxima here.
  2109. // process any edges between maxima pair ...
  2110. while (nextE != maxPair)
  2111. {
  2112. IntersectEdges(ae, nextE!, ae.top);
  2113. SwapPositionsInAEL(ae, nextE!);
  2114. nextE = ae.nextInAEL;
  2115. }
  2116. if (IsOpen(ae))
  2117. {
  2118. if (IsHotEdge(ae))
  2119. AddLocalMaxPoly(ae, maxPair, ae.top);
  2120. DeleteFromAEL(maxPair);
  2121. DeleteFromAEL(ae);
  2122. return (prevE != null ? prevE.nextInAEL : _actives);
  2123. }
  2124. // here ae.nextInAel == ENext == EMaxPair ...
  2125. if (IsHotEdge(ae))
  2126. AddLocalMaxPoly(ae, maxPair, ae.top);
  2127. DeleteFromAEL(ae);
  2128. DeleteFromAEL(maxPair);
  2129. return (prevE != null ? prevE.nextInAEL : _actives);
  2130. }
  2131. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2132. private static bool IsValidPath(OutPt op)
  2133. {
  2134. return (op.next != op);
  2135. }
  2136. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2137. private static bool PtsReallyClose(Point64 pt1, Point64 pt2)
  2138. {
  2139. return (Math.Abs(pt1.X - pt2.X) < 2) && (Math.Abs(pt1.Y - pt2.Y) < 2);
  2140. }
  2141. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2142. private static bool IsVerySmallTriangle(OutPt op)
  2143. {
  2144. return op.next!.next == op.prev &&
  2145. (PtsReallyClose(op.prev.pt, op.next.pt) ||
  2146. PtsReallyClose(op.pt, op.next.pt) ||
  2147. PtsReallyClose(op.pt, op.prev.pt));
  2148. }
  2149. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2150. private static bool IsValidClosedPath(OutPt? op)
  2151. {
  2152. return (op != null && op.next != op &&
  2153. (op.next != op.prev || !IsVerySmallTriangle(op)));
  2154. }
  2155. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2156. private static bool ValueBetween(long val, long end1, long end2)
  2157. {
  2158. // NB accommodates axis aligned between where end1 == end2
  2159. return ((val != end1) == (val != end2)) &&
  2160. ((val > end1) == (val < end2));
  2161. }
  2162. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2163. private static bool ValueEqualOrBetween(long val, long end1, long end2)
  2164. {
  2165. return (val == end1) || (val == end2) || ((val > end1) == (val < end2));
  2166. }
  2167. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2168. private static bool PointEqualOrBetween(Point64 pt, Point64 corner1, Point64 corner2)
  2169. {
  2170. // NB points may not be collinear
  2171. return
  2172. ValueEqualOrBetween(pt.X, corner1.X, corner2.X) &&
  2173. ValueEqualOrBetween(pt.Y, corner1.Y, corner2.Y);
  2174. }
  2175. private static bool PointBetween(Point64 pt, Point64 corner1, Point64 corner2)
  2176. {
  2177. // NB points may not be collinear
  2178. return
  2179. ValueBetween(pt.X, corner1.X, corner2.X) &&
  2180. ValueBetween(pt.Y, corner1.Y, corner2.Y);
  2181. }
  2182. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2183. private static bool CollinearSegsOverlap(Point64 seg1a, Point64 seg1b,
  2184. Point64 seg2a, Point64 seg2b)
  2185. {
  2186. // precondition: seg1 and seg2 are collinear
  2187. if (seg1a.X == seg1b.X)
  2188. {
  2189. if (seg2a.X != seg1a.X || seg2a.X != seg2b.X) return false;
  2190. }
  2191. else if (seg1a.X < seg1b.X)
  2192. {
  2193. if (seg2a.X < seg2b.X)
  2194. {
  2195. if (seg2a.X >= seg1b.X || seg2b.X <= seg1a.X) return false;
  2196. }
  2197. else
  2198. {
  2199. if (seg2b.X >= seg1b.X || seg2a.X <= seg1a.X) return false;
  2200. }
  2201. }
  2202. else
  2203. {
  2204. if (seg2a.X < seg2b.X)
  2205. {
  2206. if (seg2a.X >= seg1a.X || seg2b.X <= seg1b.X) return false;
  2207. }
  2208. else
  2209. {
  2210. if (seg2b.X >= seg1a.X || seg2a.X <= seg1b.X) return false;
  2211. }
  2212. }
  2213. if (seg1a.Y == seg1b.Y)
  2214. {
  2215. if (seg2a.Y != seg1a.Y || seg2a.Y != seg2b.Y) return false;
  2216. }
  2217. else if (seg1a.Y < seg1b.Y)
  2218. {
  2219. if (seg2a.Y < seg2b.Y)
  2220. {
  2221. if (seg2a.Y >= seg1b.Y || seg2b.Y <= seg1a.Y) return false;
  2222. }
  2223. else
  2224. {
  2225. if (seg2b.Y >= seg1b.Y || seg2a.Y <= seg1a.Y) return false;
  2226. }
  2227. }
  2228. else
  2229. {
  2230. if (seg2a.Y < seg2b.Y)
  2231. {
  2232. if (seg2a.Y >= seg1a.Y || seg2b.Y <= seg1b.Y) return false;
  2233. }
  2234. else
  2235. {
  2236. if (seg2b.Y >= seg1a.Y || seg2a.Y <= seg1b.Y) return false;
  2237. }
  2238. }
  2239. return true;
  2240. }
  2241. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2242. private static bool HorzEdgesOverlap(long x1a, long x1b, long x2a, long x2b)
  2243. {
  2244. const long minOverlap = 2;
  2245. if (x1a > x1b + minOverlap)
  2246. {
  2247. if (x2a > x2b + minOverlap)
  2248. return !((x1a <= x2b) || (x2a <= x1b));
  2249. return !((x1a <= x2a) || (x2b <= x1b));
  2250. }
  2251. if (x1b > x1a + minOverlap)
  2252. {
  2253. if (x2a > x2b + minOverlap)
  2254. return !((x1b <= x2b) || (x2a <= x1a));
  2255. return !((x1b <= x2a) || (x2b <= x1a));
  2256. }
  2257. return false;
  2258. }
  2259. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2260. private static Joiner? GetHorzTrialParent(OutPt op)
  2261. {
  2262. Joiner? joiner = op.joiner;
  2263. while (joiner != null)
  2264. {
  2265. if (joiner.op1 == op)
  2266. {
  2267. if (joiner.next1 != null &&
  2268. joiner.next1.idx < 0) return joiner;
  2269. joiner = joiner.next1;
  2270. }
  2271. else
  2272. {
  2273. if (joiner.next2 != null &&
  2274. joiner.next2.idx < 0) return joiner;
  2275. joiner = joiner.next1;
  2276. }
  2277. }
  2278. return joiner;
  2279. }
  2280. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2281. private static bool OutPtInTrialHorzList(OutPt op)
  2282. {
  2283. return op.joiner != null &&
  2284. ((op.joiner.idx < 0) || GetHorzTrialParent(op) != null);
  2285. }
  2286. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2287. private bool ValidateClosedPathEx(ref OutPt? op)
  2288. {
  2289. if (IsValidClosedPath(op)) return true;
  2290. if (op != null)
  2291. SafeDisposeOutPts(ref op);
  2292. return false;
  2293. }
  2294. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2295. private static OutPt InsertOp(Point64 pt, OutPt insertAfter)
  2296. {
  2297. OutPt result = new OutPt(pt, insertAfter.outrec)
  2298. { next = insertAfter.next };
  2299. insertAfter.next!.prev = result;
  2300. insertAfter.next = result;
  2301. result.prev = insertAfter;
  2302. return result;
  2303. }
  2304. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2305. private static OutPt? DisposeOutPt(OutPt op)
  2306. {
  2307. OutPt? result = (op.next == op ? null : op.next);
  2308. op.prev.next = op.next;
  2309. op.next!.prev = op.prev;
  2310. // op == null;
  2311. return result;
  2312. }
  2313. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2314. private void SafeDisposeOutPts(ref OutPt op)
  2315. {
  2316. OutRec? outRec = GetRealOutRec(op.outrec);
  2317. if (outRec!.frontEdge != null)
  2318. outRec.frontEdge.outrec = null;
  2319. if (outRec.backEdge != null)
  2320. outRec.backEdge.outrec = null;
  2321. op.prev.next = null;
  2322. OutPt? op2 = op;
  2323. while (op2 != null)
  2324. {
  2325. SafeDeleteOutPtJoiners(op2);
  2326. op2 = op2.next;
  2327. }
  2328. outRec.pts = null;
  2329. }
  2330. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2331. private void SafeDeleteOutPtJoiners(OutPt op)
  2332. {
  2333. Joiner? joiner = op.joiner;
  2334. if (joiner == null) return;
  2335. while (joiner != null)
  2336. {
  2337. if (joiner.idx < 0)
  2338. DeleteTrialHorzJoin(op);
  2339. else if (_horzJoiners != null)
  2340. {
  2341. if (OutPtInTrialHorzList(joiner.op1))
  2342. DeleteTrialHorzJoin(joiner.op1);
  2343. if (OutPtInTrialHorzList(joiner.op2!))
  2344. DeleteTrialHorzJoin(joiner.op2!);
  2345. DeleteJoin(joiner);
  2346. }
  2347. else
  2348. DeleteJoin(joiner);
  2349. joiner = op.joiner;
  2350. }
  2351. }
  2352. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2353. private void AddTrialHorzJoin(OutPt op)
  2354. {
  2355. // make sure 'op' isn't added more than once
  2356. if (!op.outrec.isOpen && !OutPtInTrialHorzList(op))
  2357. _horzJoiners = new Joiner(op, null, _horzJoiners);
  2358. }
  2359. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2360. private static Joiner? FindTrialJoinParent(ref Joiner joiner, OutPt op)
  2361. {
  2362. Joiner? parent = joiner;
  2363. while (parent != null)
  2364. {
  2365. if (op == parent.op1)
  2366. {
  2367. if (parent.next1 != null && parent.next1.idx < 0)
  2368. {
  2369. joiner = parent.next1;
  2370. return parent;
  2371. }
  2372. parent = parent.next1;
  2373. }
  2374. else
  2375. {
  2376. if (parent.next2 != null && parent.next2.idx < 0)
  2377. {
  2378. joiner = parent.next2;
  2379. return parent;
  2380. }
  2381. parent = parent.next2;
  2382. }
  2383. }
  2384. return null;
  2385. }
  2386. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2387. private void DeleteTrialHorzJoin(OutPt op)
  2388. {
  2389. if (_horzJoiners == null) return;
  2390. Joiner? joiner = op.joiner;
  2391. Joiner? parentH, parentOp = null;
  2392. while (joiner != null)
  2393. {
  2394. if (joiner.idx < 0)
  2395. {
  2396. // first remove joiner from FHorzTrials
  2397. if (joiner == _horzJoiners)
  2398. _horzJoiners = joiner.nextH;
  2399. else
  2400. {
  2401. parentH = _horzJoiners;
  2402. while (parentH!.nextH != joiner)
  2403. parentH = parentH.nextH;
  2404. parentH.nextH = joiner.nextH;
  2405. }
  2406. // now remove joiner from op's joiner list
  2407. if (parentOp == null)
  2408. {
  2409. // joiner must be first one in list
  2410. op.joiner = joiner.next1;
  2411. // joiner == null;
  2412. joiner = op.joiner;
  2413. }
  2414. else
  2415. {
  2416. // the trial joiner isn't first
  2417. if (op == parentOp.op1)
  2418. parentOp.next1 = joiner.next1;
  2419. else
  2420. parentOp.next2 = joiner.next1;
  2421. // joiner = null;
  2422. joiner = parentOp;
  2423. }
  2424. }
  2425. else
  2426. {
  2427. // not a trial join so look further along the linked list
  2428. parentOp = FindTrialJoinParent(ref joiner, op);
  2429. if (parentOp == null) break;
  2430. }
  2431. // loop in case there's more than one trial join
  2432. }
  2433. }
  2434. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2435. private static bool GetHorzExtendedHorzSeg(ref OutPt op, out OutPt op2)
  2436. {
  2437. OutRec outRec = GetRealOutRec(op.outrec)!;
  2438. op2 = op;
  2439. if (outRec.frontEdge != null)
  2440. {
  2441. while (op.prev != outRec.pts &&
  2442. op.prev.pt.Y == op.pt.Y) op = op.prev;
  2443. while (op2 != outRec.pts &&
  2444. op2.next!.pt.Y == op2.pt.Y) op2 = op2.next;
  2445. return op2 != op;
  2446. }
  2447. while (op.prev != op2 && op.prev.pt.Y == op.pt.Y)
  2448. op = op.prev;
  2449. while (op2.next != op && op2.next!.pt.Y == op2.pt.Y)
  2450. op2 = op2.next;
  2451. return op2 != op && op2.next != op;
  2452. }
  2453. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2454. private void ConvertHorzTrialsToJoins()
  2455. {
  2456. while (_horzJoiners != null)
  2457. {
  2458. Joiner? joiner = _horzJoiners;
  2459. _horzJoiners = _horzJoiners.nextH;
  2460. OutPt op1a = joiner.op1;
  2461. if (op1a.joiner == joiner)
  2462. {
  2463. op1a.joiner = joiner.next1;
  2464. }
  2465. else
  2466. {
  2467. Joiner joinerParent = FindJoinParent(joiner, op1a);
  2468. if (joinerParent.op1 == op1a)
  2469. joinerParent.next1 = joiner.next1;
  2470. else
  2471. joinerParent.next2 = joiner.next1;
  2472. }
  2473. // joiner = null;
  2474. if (!GetHorzExtendedHorzSeg(ref op1a, out OutPt op1b))
  2475. {
  2476. if (op1a.outrec.frontEdge == null)
  2477. CleanCollinear(op1a.outrec);
  2478. continue;
  2479. }
  2480. OutPt op2a;
  2481. bool joined = false;
  2482. joiner = _horzJoiners;
  2483. while (joiner != null)
  2484. {
  2485. op2a = joiner.op1;
  2486. if (GetHorzExtendedHorzSeg(ref op2a, out OutPt op2b) &&
  2487. HorzEdgesOverlap(op1a.pt.X, op1b.pt.X, op2a.pt.X, op2b.pt.X))
  2488. {
  2489. // overlap found so promote to a 'real' join
  2490. joined = true;
  2491. if (op1a.pt == op2b.pt)
  2492. AddJoin(op1a, op2b);
  2493. else if (op1b.pt == op2a.pt)
  2494. AddJoin(op1b, op2a);
  2495. else if (op1a.pt == op2a.pt)
  2496. AddJoin(op1a, op2a);
  2497. else if (op1b.pt == op2b.pt)
  2498. AddJoin(op1b, op2b);
  2499. else if (ValueBetween(op1a.pt.X, op2a.pt.X, op2b.pt.X))
  2500. AddJoin(op1a, InsertOp(op1a.pt, op2a));
  2501. else if (ValueBetween(op1b.pt.X, op2a.pt.X, op2b.pt.X))
  2502. AddJoin(op1b, InsertOp(op1b.pt, op2a));
  2503. else if (ValueBetween(op2a.pt.X, op1a.pt.X, op1b.pt.X))
  2504. AddJoin(op2a, InsertOp(op2a.pt, op1a));
  2505. else if (ValueBetween(op2b.pt.X, op1a.pt.X, op1b.pt.X))
  2506. AddJoin(op2b, InsertOp(op2b.pt, op1a));
  2507. break;
  2508. }
  2509. joiner = joiner.nextH;
  2510. }
  2511. if (!joined)
  2512. CleanCollinear(op1a.outrec);
  2513. }
  2514. }
  2515. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2516. private void AddJoin(OutPt op1, OutPt op2)
  2517. {
  2518. if ((op1.outrec == op2.outrec) && ((op1 == op2) ||
  2519. // unless op1.next or op1.prev crosses the start-end divide
  2520. // don't waste time trying to join adjacent vertices
  2521. ((op1.next == op2) && (op1 != op1.outrec.pts)) ||
  2522. ((op2.next == op1) && (op2 != op1.outrec.pts)))) return;
  2523. Joiner joiner = new Joiner(op1, op2, null) { idx = _joinerList.Count };
  2524. _joinerList.Add(joiner);
  2525. }
  2526. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2527. private static Joiner FindJoinParent(Joiner joiner, OutPt op)
  2528. {
  2529. Joiner result = op.joiner!;
  2530. for (; ; )
  2531. {
  2532. if (op == result.op1)
  2533. {
  2534. if (result.next1 == joiner) return result;
  2535. result = result.next1!;
  2536. }
  2537. else
  2538. {
  2539. if (result.next2 == joiner) return result;
  2540. result = result.next2!;
  2541. }
  2542. }
  2543. }
  2544. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2545. private void DeleteJoin(Joiner joiner)
  2546. {
  2547. // This method deletes a single join, and it doesn't check for or
  2548. // delete trial horz. joins. For that, use the following method.
  2549. OutPt op1 = joiner.op1, op2 = joiner.op2!;
  2550. Joiner parentJnr;
  2551. if (op1.joiner != joiner)
  2552. {
  2553. parentJnr = FindJoinParent(joiner, op1);
  2554. if (parentJnr.op1 == op1)
  2555. parentJnr.next1 = joiner.next1;
  2556. else
  2557. parentJnr.next2 = joiner.next1;
  2558. }
  2559. else
  2560. op1.joiner = joiner.next1;
  2561. if (op2.joiner != joiner)
  2562. {
  2563. parentJnr = FindJoinParent(joiner, op2);
  2564. if (parentJnr.op1 == op2)
  2565. parentJnr.next1 = joiner.next2;
  2566. else
  2567. parentJnr.next2 = joiner.next2;
  2568. }
  2569. else
  2570. op2.joiner = joiner.next2;
  2571. _joinerList[joiner.idx] = null;
  2572. }
  2573. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2574. private void ProcessJoinList()
  2575. {
  2576. // NB can't use foreach here because list may
  2577. // contain nulls which can't be enumerated
  2578. for (int i = 0; i < _joinerList.Count; i++)
  2579. {
  2580. Joiner? j = _joinerList[i];
  2581. if (j == null) continue;
  2582. OutRec outrec = ProcessJoin(j);
  2583. CleanCollinear(outrec);
  2584. }
  2585. _joinerList.Clear();
  2586. }
  2587. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2588. private static bool CheckDisposeAdjacent(ref OutPt op, OutPt guard, OutRec outRec)
  2589. {
  2590. bool result = false;
  2591. while (op.prev != op)
  2592. {
  2593. if (op.pt == op.prev.pt && op != guard &&
  2594. op.prev.joiner != null && op.joiner == null)
  2595. {
  2596. if (op == outRec.pts) outRec.pts = op.prev;
  2597. op = DisposeOutPt(op)!;
  2598. op = op.prev;
  2599. }
  2600. else
  2601. break;
  2602. }
  2603. while (op.next != op)
  2604. {
  2605. if (op.pt == op.next!.pt && op != guard &&
  2606. op.next.joiner != null && op.joiner == null)
  2607. {
  2608. if (op == outRec.pts) outRec.pts = op.prev;
  2609. op = DisposeOutPt(op)!;
  2610. op = op.prev;
  2611. }
  2612. else
  2613. break;
  2614. }
  2615. return result;
  2616. }
  2617. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2618. private static double DistanceFromLineSqrd(Point64 pt, Point64 linePt1, Point64 linePt2)
  2619. {
  2620. // perpendicular distance of point (x0,y0) = (a*x0 + b*y0 + C)/Sqrt(a*a + b*b)
  2621. // where ax + by +c = 0 is the equation of the line
  2622. // see https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
  2623. double a = (linePt1.Y - linePt2.Y);
  2624. double b = (linePt2.X - linePt1.X);
  2625. double c = a * linePt1.X + b * linePt1.Y;
  2626. double q = a * pt.X + b * pt.Y - c;
  2627. return (q * q) / (a * a + b * b);
  2628. }
  2629. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2630. private static double DistanceSqr(Point64 pt1, Point64 pt2)
  2631. {
  2632. return (double)(pt1.X - pt2.X) * (pt1.X - pt2.X) +
  2633. (double)(pt1.Y - pt2.Y) * (pt1.Y - pt2.Y);
  2634. }
  2635. private OutRec ProcessJoin(Joiner j)
  2636. {
  2637. OutPt op1 = j.op1, op2 = j.op2!;
  2638. OutRec or1 = GetRealOutRec(op1.outrec)!;
  2639. OutRec or2 = GetRealOutRec(op2.outrec)!;
  2640. DeleteJoin(j);
  2641. if (or2.pts == null) return or1;
  2642. if (!IsValidClosedPath(op2))
  2643. {
  2644. SafeDisposeOutPts(ref op2);
  2645. return or1;
  2646. }
  2647. if ((or1.pts == null) || !IsValidClosedPath(op1))
  2648. {
  2649. SafeDisposeOutPts(ref op1);
  2650. return or2;
  2651. }
  2652. if (or1 == or2 &&
  2653. ((op1 == op2) || (op1.next == op2) || (op1.prev == op2))) return or1;
  2654. CheckDisposeAdjacent(ref op1, op2, or1);
  2655. CheckDisposeAdjacent(ref op2, op1, or2);
  2656. if (op1.next == op2 || op2.next == op1) return or1;
  2657. OutRec result = or1;
  2658. for (; ; )
  2659. {
  2660. if (!IsValidPath(op1) || !IsValidPath(op2) ||
  2661. (or1 == or2 && (op1.prev == op2 || op1.next == op2))) return or1;
  2662. if (op1.prev.pt == op2.next!.pt ||
  2663. ((InternalClipper.CrossProduct(op1.prev.pt, op1.pt, op2.next.pt) == 0) &&
  2664. CollinearSegsOverlap(op1.prev.pt, op1.pt, op2.pt, op2.next.pt)))
  2665. {
  2666. if (or1 == or2)
  2667. {
  2668. // SPLIT REQUIRED
  2669. // make sure op1.prev and op2.next match positions
  2670. // by inserting an extra vertex if needed
  2671. if (op1.prev.pt != op2.next.pt)
  2672. {
  2673. if (PointEqualOrBetween(op1.prev.pt, op2.pt, op2.next.pt))
  2674. op2.next = InsertOp(op1.prev.pt, op2);
  2675. else
  2676. op1.prev = InsertOp(op2.next.pt, op1.prev);
  2677. }
  2678. // current to new
  2679. // op1.p[opA] >>> op1 ... opA \ / op1
  2680. // op2.n[opB] <<< op2 ... opB / \ op2
  2681. OutPt opA = op1.prev, opB = op2.next;
  2682. opA.next = opB;
  2683. opB.prev = opA;
  2684. op1.prev = op2;
  2685. op2.next = op1;
  2686. CompleteSplit(op1, opA, or1);
  2687. }
  2688. else
  2689. {
  2690. // JOIN, NOT SPLIT
  2691. OutPt opA = op1.prev, opB = op2.next;
  2692. opA.next = opB;
  2693. opB.prev = opA;
  2694. op1.prev = op2;
  2695. op2.next = op1;
  2696. //SafeDeleteOutPtJoiners(op2);
  2697. //DisposeOutPt(op2);
  2698. if (or1.idx < or2.idx)
  2699. {
  2700. or1.pts = op1;
  2701. or2.pts = null;
  2702. if (or1.owner != null &&
  2703. (or2.owner == null || or2.owner.idx < or1.owner.idx))
  2704. {
  2705. or1.owner = or2.owner;
  2706. }
  2707. or2.owner = or1;
  2708. }
  2709. else
  2710. {
  2711. result = or2;
  2712. or2.pts = op1;
  2713. or1.pts = null;
  2714. if (or2.owner != null &&
  2715. (or1.owner == null || or1.owner.idx < or2.owner.idx))
  2716. {
  2717. or2.owner = or1.owner;
  2718. }
  2719. or1.owner = or2;
  2720. }
  2721. }
  2722. break;
  2723. }
  2724. if (op1.next!.pt == op2.prev.pt ||
  2725. ((InternalClipper.CrossProduct(op1.next.pt, op2.pt, op2.prev.pt) == 0) &&
  2726. CollinearSegsOverlap(op1.next.pt, op1.pt, op2.pt, op2.prev.pt)))
  2727. {
  2728. if (or1 == or2)
  2729. {
  2730. // SPLIT REQUIRED
  2731. // make sure op2.prev and op1.next match positions
  2732. // by inserting an extra vertex if needed
  2733. if (op2.prev.pt != op1.next.pt)
  2734. {
  2735. if (PointEqualOrBetween(op2.prev.pt, op1.pt, op1.next.pt))
  2736. op1.next = InsertOp(op2.prev.pt, op1);
  2737. else
  2738. op2.prev = InsertOp(op1.next.pt, op2.prev);
  2739. }
  2740. // current to new
  2741. // op2.p[opA] >>> op2 ... opA \ / op2
  2742. // op1.n[opB] <<< op1 ... opB / \ op1
  2743. OutPt opA = op2.prev, opB = op1.next;
  2744. opA.next = opB;
  2745. opB.prev = opA;
  2746. op2.prev = op1;
  2747. op1.next = op2;
  2748. CompleteSplit(op1, opA, or1);
  2749. }
  2750. else
  2751. {
  2752. // JOIN, NOT SPLIT
  2753. OutPt opA = op1.next, opB = op2.prev;
  2754. opA.prev = opB;
  2755. opB.next = opA;
  2756. op1.next = op2;
  2757. op2.prev = op1;
  2758. //SafeDeleteOutPtJoiners(op2);
  2759. //DisposeOutPt(op2);
  2760. if (or1.idx < or2.idx)
  2761. {
  2762. or1.pts = op1;
  2763. or2.pts = null;
  2764. if (or1.owner != null &&
  2765. (or2.owner == null || or2.owner.idx < or1.owner.idx))
  2766. {
  2767. or1.owner = or2.owner;
  2768. }
  2769. or2.owner = or1;
  2770. }
  2771. else
  2772. {
  2773. result = or2;
  2774. or2.pts = op1;
  2775. or1.pts = null;
  2776. if (or2.owner != null &&
  2777. (or1.owner == null || or1.owner.idx < or2.owner.idx))
  2778. {
  2779. or2.owner = or1.owner;
  2780. }
  2781. or1.owner = or2;
  2782. }
  2783. }
  2784. break;
  2785. }
  2786. if (PointBetween(op1.next.pt, op2.pt, op2.prev.pt) &&
  2787. DistanceFromLineSqrd(op1.next.pt, op2.pt, op2.prev.pt) < 2.01)
  2788. {
  2789. InsertOp(op1.next.pt, op2.prev);
  2790. continue;
  2791. }
  2792. if (PointBetween(op2.next.pt, op1.pt, op1.prev.pt) &&
  2793. DistanceFromLineSqrd(op2.next.pt, op1.pt, op1.prev.pt) < 2.01)
  2794. {
  2795. InsertOp(op2.next.pt, op1.prev);
  2796. continue;
  2797. }
  2798. if (PointBetween(op1.prev.pt, op2.pt, op2.next.pt) &&
  2799. DistanceFromLineSqrd(op1.prev.pt, op2.pt, op2.next.pt) < 2.01)
  2800. {
  2801. InsertOp(op1.prev.pt, op2);
  2802. continue;
  2803. }
  2804. if (PointBetween(op2.prev.pt, op1.pt, op1.next.pt) &&
  2805. DistanceFromLineSqrd(op2.prev.pt, op1.pt, op1.next.pt) < 2.01)
  2806. {
  2807. InsertOp(op2.prev.pt, op1);
  2808. continue;
  2809. }
  2810. // something odd needs tidying up
  2811. if (CheckDisposeAdjacent(ref op1, op2, or1)) continue;
  2812. if (CheckDisposeAdjacent(ref op2, op1, or1)) continue;
  2813. if (op1.prev.pt != op2.next!.pt &&
  2814. (DistanceSqr(op1.prev.pt, op2.next.pt) < 2.01))
  2815. {
  2816. op1.prev.pt = op2.next.pt;
  2817. continue;
  2818. }
  2819. if (op1.next!.pt != op2.prev.pt &&
  2820. (DistanceSqr(op1.next.pt, op2.prev.pt) < 2.01))
  2821. {
  2822. op2.prev.pt = op1.next.pt;
  2823. continue;
  2824. }
  2825. // OK, there doesn't seem to be a way to join after all
  2826. // so just tidy up the polygons
  2827. or1.pts = op1;
  2828. if (or2 != or1)
  2829. {
  2830. or2.pts = op2;
  2831. CleanCollinear(or2);
  2832. }
  2833. break;
  2834. }
  2835. return result;
  2836. }
  2837. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2838. private static void UpdateOutrecOwner(OutRec outrec)
  2839. {
  2840. OutPt opCurr = outrec.pts!;
  2841. for (; ; )
  2842. {
  2843. opCurr.outrec = outrec;
  2844. opCurr = opCurr.next!;
  2845. if (opCurr == outrec.pts) return;
  2846. }
  2847. }
  2848. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2849. private void CompleteSplit(OutPt? op1, OutPt? op2, OutRec outrec)
  2850. {
  2851. double area1 = Area(op1!);
  2852. double area2 = Area(op2!);
  2853. bool signs_change = (area1 > 0) == (area2 < 0);
  2854. // delete trivial splits (with zero or almost zero areas)
  2855. if (area1 == 0 || (signs_change && Math.Abs(area1) < 2))
  2856. {
  2857. SafeDisposeOutPts(ref op1!);
  2858. outrec.pts = op2;
  2859. }
  2860. else if (area2 == 0 || (signs_change && Math.Abs(area2) < 2))
  2861. {
  2862. SafeDisposeOutPts(ref op2!);
  2863. outrec.pts = op1;
  2864. }
  2865. else
  2866. {
  2867. OutRec newOr = new OutRec() { idx = _outrecList.Count };
  2868. _outrecList.Add(newOr);
  2869. newOr.polypath = null;
  2870. if (_using_polytree)
  2871. {
  2872. outrec.splits ??= new List<OutRec>();
  2873. outrec.splits.Add(newOr);
  2874. }
  2875. if (Math.Abs(area1) >= Math.Abs(area2))
  2876. {
  2877. outrec.pts = op1;
  2878. newOr.pts = op2;
  2879. }
  2880. else
  2881. {
  2882. outrec.pts = op2;
  2883. newOr.pts = op1;
  2884. }
  2885. if ((area1 > 0) == (area2 > 0))
  2886. newOr.owner = outrec.owner;
  2887. else
  2888. newOr.owner = outrec;
  2889. UpdateOutrecOwner(newOr);
  2890. CleanCollinear(newOr);
  2891. }
  2892. }
  2893. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2894. private void CleanCollinear(OutRec? outrec)
  2895. {
  2896. outrec = GetRealOutRec(outrec);
  2897. if (outrec == null || outrec.isOpen ||
  2898. outrec.frontEdge != null || !ValidateClosedPathEx(ref outrec.pts))
  2899. return;
  2900. OutPt startOp = outrec.pts!;
  2901. OutPt? op2 = startOp;
  2902. for (; ; )
  2903. {
  2904. if (op2!.joiner != null) return;
  2905. // NB if preserveCollinear == true, then only remove 180 deg. spikes
  2906. if ((InternalClipper.CrossProduct(op2.prev.pt, op2.pt, op2.next!.pt) == 0) &&
  2907. ((op2.pt == op2.prev.pt) || (op2.pt == op2.next.pt) || !PreserveCollinear ||
  2908. (InternalClipper.DotProduct(op2.prev.pt, op2.pt, op2.next.pt) < 0)))
  2909. {
  2910. if (op2 == outrec.pts)
  2911. outrec.pts = op2.prev;
  2912. op2 = DisposeOutPt(op2);
  2913. if (!ValidateClosedPathEx(ref op2))
  2914. {
  2915. outrec.pts = null;
  2916. return;
  2917. }
  2918. startOp = op2!;
  2919. continue;
  2920. }
  2921. op2 = op2.next;
  2922. if (op2 == startOp) break;
  2923. }
  2924. FixSelfIntersects(outrec);
  2925. }
  2926. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2927. private void DoSplitOp(OutRec outrec, OutPt splitOp)
  2928. {
  2929. // splitOp.prev -> splitOp &&
  2930. // splitOp.next -> splitOp.next.next are intersecting
  2931. OutPt prevOp = splitOp.prev;
  2932. OutPt nextNextOp = splitOp.next!.next!;
  2933. outrec.pts = prevOp;
  2934. OutPt result = prevOp;
  2935. InternalClipper.GetIntersectPoint(
  2936. prevOp.pt, splitOp.pt, splitOp.next.pt, nextNextOp.pt, out PointD ipD);
  2937. Point64 ip = new Point64(ipD);
  2938. #if USINGZ
  2939. if (_zCallback != null)
  2940. _zCallback(prevOp.pt, splitOp.pt, splitOp.next.pt, nextNextOp.pt, ref ip);
  2941. #endif
  2942. double area1 = Area(prevOp);
  2943. double absArea1 = Math.Abs(area1);
  2944. if (absArea1 < 2)
  2945. {
  2946. SafeDisposeOutPts(ref splitOp);
  2947. return;
  2948. }
  2949. // nb: area1 is the path's area *before* splitting, whereas area2 is
  2950. // the area of the triangle containing splitOp & splitOp.next.
  2951. // So the only way for these areas to have the same sign is if
  2952. // the split triangle is larger than the path containing prevOp or
  2953. // if there's more than one self=intersection.
  2954. double area2 = AreaTriangle(ip, splitOp.pt, splitOp.next.pt);
  2955. double absArea2 = Math.Abs(area2);
  2956. // de-link splitOp and splitOp.next from the path
  2957. // while inserting the intersection point
  2958. if (ip == prevOp.pt || ip == nextNextOp.pt)
  2959. {
  2960. nextNextOp.prev = prevOp;
  2961. prevOp.next = nextNextOp;
  2962. }
  2963. else
  2964. {
  2965. OutPt newOp2 = new OutPt(ip, prevOp.outrec) { prev = prevOp, next = nextNextOp };
  2966. nextNextOp.prev = newOp2;
  2967. prevOp.next = newOp2;
  2968. }
  2969. SafeDeleteOutPtJoiners(splitOp.next);
  2970. SafeDeleteOutPtJoiners(splitOp);
  2971. if (absArea2 > 1 &&
  2972. (absArea2 > absArea1 ||
  2973. ((area2 > 0) == (area1 > 0))))
  2974. {
  2975. OutRec newOutRec = new OutRec();
  2976. newOutRec.idx = _outrecList.Count;
  2977. _outrecList.Add(newOutRec);
  2978. newOutRec.owner = prevOp.outrec.owner;
  2979. newOutRec.polypath = null;
  2980. splitOp.outrec = newOutRec;
  2981. splitOp.next.outrec = newOutRec;
  2982. OutPt newOp = new OutPt(ip, newOutRec) { prev = splitOp.next, next = splitOp };
  2983. newOutRec.pts = newOp;
  2984. splitOp.prev = newOp;
  2985. splitOp.next.next = newOp;
  2986. }
  2987. //else { splitOp = null; splitOp.next = null; }
  2988. }
  2989. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2990. private void FixSelfIntersects(OutRec outrec)
  2991. {
  2992. OutPt op2 = outrec.pts!;
  2993. for (; ; )
  2994. {
  2995. // triangles can't self-intersect
  2996. if (op2.prev == op2.next!.next) break;
  2997. if (InternalClipper.SegsIntersect(op2.prev.pt,
  2998. op2.pt, op2.next.pt, op2.next.next!.pt))
  2999. {
  3000. DoSplitOp(outrec, op2);
  3001. if (outrec.pts == null) return;
  3002. op2 = outrec.pts;
  3003. continue;
  3004. }
  3005. else
  3006. op2 = op2.next;
  3007. if (op2 == outrec.pts) break;
  3008. }
  3009. }
  3010. internal static bool BuildPath(OutPt op, bool reverse, bool isOpen, Path64 path)
  3011. {
  3012. if (op.next == op || (!isOpen && op.next == op.prev)) return false;
  3013. path.Clear();
  3014. Point64 lastPt;
  3015. OutPt op2;
  3016. if (reverse)
  3017. {
  3018. lastPt = op.pt;
  3019. op2 = op.prev;
  3020. }
  3021. else
  3022. {
  3023. op = op.next!;
  3024. lastPt = op.pt;
  3025. op2 = op.next!;
  3026. }
  3027. path.Add(lastPt);
  3028. while (op2 != op)
  3029. {
  3030. if (op2.pt != lastPt)
  3031. {
  3032. lastPt = op2.pt;
  3033. path.Add(lastPt);
  3034. }
  3035. if (reverse)
  3036. op2 = op2.prev;
  3037. else
  3038. op2 = op2.next!;
  3039. }
  3040. if (path.Count == 3 && IsVerySmallTriangle(op2)) return false;
  3041. else return true;
  3042. }
  3043. protected bool BuildPaths(Paths64 solutionClosed, Paths64 solutionOpen)
  3044. {
  3045. solutionClosed.Clear();
  3046. solutionOpen.Clear();
  3047. solutionClosed.Capacity = _outrecList.Count;
  3048. solutionOpen.Capacity = _outrecList.Count;
  3049. foreach (OutRec outrec in _outrecList)
  3050. {
  3051. if (outrec.pts == null) continue;
  3052. Path64 path = new Path64();
  3053. if (outrec.isOpen)
  3054. {
  3055. if (BuildPath(outrec.pts!, ReverseSolution, true, path))
  3056. solutionOpen.Add(path);
  3057. }
  3058. else
  3059. {
  3060. // closed paths should always return a Positive orientation
  3061. // except when ReverseSolution == true
  3062. if (BuildPath(outrec.pts!, ReverseSolution, false, path))
  3063. solutionClosed.Add(path);
  3064. }
  3065. }
  3066. return true;
  3067. }
  3068. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3069. private static bool Path1InsidePath2(OutRec or1, OutRec or2)
  3070. {
  3071. PointInPolygonResult result;
  3072. OutPt op = or1.pts!;
  3073. do
  3074. {
  3075. result = InternalClipper.PointInPolygon(op.pt, or2.path);
  3076. if (result != PointInPolygonResult.IsOn) break;
  3077. op = op.next!;
  3078. } while (op != or1.pts);
  3079. if (result == PointInPolygonResult.IsOn)
  3080. return Area(op) < Area(or2.pts!);
  3081. return result == PointInPolygonResult.IsInside;
  3082. }
  3083. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3084. private static Rect64 GetBounds(Path64 path)
  3085. {
  3086. if (path.Count == 0) return new Rect64();
  3087. Rect64 result = new Rect64(long.MaxValue, long.MaxValue, -long.MaxValue, -long.MaxValue);
  3088. foreach (Point64 pt in path)
  3089. {
  3090. if (pt.X < result.left) result.left = pt.X;
  3091. if (pt.X > result.right) result.right = pt.X;
  3092. if (pt.Y < result.top) result.top = pt.Y;
  3093. if (pt.Y > result.bottom) result.bottom = pt.Y;
  3094. }
  3095. return result;
  3096. }
  3097. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3098. private bool DeepCheckOwner(OutRec outrec, OutRec owner)
  3099. {
  3100. if (owner.bounds.IsEmpty())
  3101. owner.bounds = GetBounds(owner.path);
  3102. bool isInsideOwnerBounds = owner.bounds.Contains(outrec.bounds);
  3103. // while looking for the correct owner, check the owner's
  3104. // splits **before** checking the owner itself because
  3105. // splits can occur internally, and checking the owner
  3106. // first would miss the inner split's true ownership
  3107. if (owner.splits != null)
  3108. foreach (OutRec asplit in owner.splits!)
  3109. {
  3110. OutRec? split = GetRealOutRec(asplit);
  3111. if (split == null || split.idx <= owner.idx || split == outrec) continue;
  3112. if (split.splits != null && DeepCheckOwner(outrec, split)) return true;
  3113. if (split.path.Count == 0)
  3114. BuildPath(split.pts!, ReverseSolution, false, split.path);
  3115. if (split.bounds.IsEmpty()) split.bounds = GetBounds(split.path);
  3116. if (split.bounds.Contains(outrec.bounds) && Path1InsidePath2(outrec, split))
  3117. {
  3118. outrec.owner = split;
  3119. return true;
  3120. }
  3121. }
  3122. // only continue when not inside recursion
  3123. if (owner != outrec.owner) return false;
  3124. for (; ; )
  3125. {
  3126. if (isInsideOwnerBounds && Path1InsidePath2(outrec, outrec.owner!))
  3127. return true;
  3128. outrec.owner = outrec.owner!.owner;
  3129. if (outrec.owner == null) return false;
  3130. isInsideOwnerBounds = outrec.owner.bounds.Contains(outrec.bounds);
  3131. }
  3132. }
  3133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3134. protected bool BuildTree(PolyPathNode polytree, Paths64 solutionOpen)
  3135. {
  3136. polytree.Clear();
  3137. solutionOpen.Clear();
  3138. solutionOpen.Capacity = _outrecList.Count;
  3139. for (int i = 0; i < _outrecList.Count; i++)
  3140. {
  3141. OutRec outrec = _outrecList[i];
  3142. if (outrec.pts == null) continue;
  3143. if (outrec.isOpen)
  3144. {
  3145. Path64 open_path = new Path64();
  3146. if (BuildPath(outrec.pts!, ReverseSolution, true, open_path))
  3147. solutionOpen.Add(open_path);
  3148. continue;
  3149. }
  3150. if (!BuildPath(outrec.pts!, ReverseSolution, false, outrec.path)) continue;
  3151. if (outrec.bounds.IsEmpty()) outrec.bounds = GetBounds(outrec.path);
  3152. outrec.owner = GetRealOutRec(outrec.owner);
  3153. if (outrec.owner != null)
  3154. DeepCheckOwner(outrec, outrec.owner);
  3155. // swap order if outer/owner paths are preceeded by their inner paths
  3156. if (outrec.owner != null && outrec.owner.idx > outrec.idx)
  3157. {
  3158. int j = outrec.owner.idx;
  3159. outrec.owner.idx = i;
  3160. outrec.idx = j;
  3161. _outrecList[i] = _outrecList[j];
  3162. _outrecList[j] = outrec;
  3163. outrec = _outrecList[i];
  3164. outrec.owner = GetRealOutRec(outrec.owner);
  3165. BuildPath(outrec.pts!, ReverseSolution, false, outrec.path);
  3166. if (outrec.bounds.IsEmpty()) outrec.bounds = GetBounds(outrec.path);
  3167. if (outrec.owner != null)
  3168. DeepCheckOwner(outrec, outrec.owner);
  3169. }
  3170. PolyPathNode ownerPP;
  3171. if (outrec.owner != null && outrec.owner.polypath != null)
  3172. ownerPP = outrec.owner.polypath;
  3173. else
  3174. ownerPP = polytree;
  3175. outrec.polypath = ownerPP.AddChild(outrec.path);
  3176. }
  3177. return true;
  3178. }
  3179. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3180. public Rect64 GetBounds()
  3181. {
  3182. Rect64 bounds = Clipper.MaxInvalidRect64;
  3183. foreach (Vertex t in _vertexList)
  3184. {
  3185. Vertex v = t;
  3186. do
  3187. {
  3188. if (v.pt.X < bounds.left) bounds.left = v.pt.X;
  3189. if (v.pt.X > bounds.right) bounds.right = v.pt.X;
  3190. if (v.pt.Y < bounds.top) bounds.top = v.pt.Y;
  3191. if (v.pt.Y > bounds.bottom) bounds.bottom = v.pt.Y;
  3192. v = v.next!;
  3193. } while (v != t);
  3194. }
  3195. return bounds.IsEmpty() ? new Rect64(0, 0, 0, 0) : bounds;
  3196. }
  3197. } // ClipperBase class
  3198. public class Clipper64 : ClipperBase
  3199. {
  3200. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3201. internal new void AddPath(Path64 path, PathType polytype, bool isOpen = false)
  3202. {
  3203. base.AddPath(path, polytype, isOpen);
  3204. }
  3205. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3206. internal new void AddPaths(Paths64 paths, PathType polytype, bool isOpen = false)
  3207. {
  3208. base.AddPaths(paths, polytype, isOpen);
  3209. }
  3210. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3211. public void AddSubject(Paths64 paths)
  3212. {
  3213. AddPaths(paths, PathType.Subject);
  3214. }
  3215. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3216. public void AddOpenSubject(Paths64 paths)
  3217. {
  3218. AddPaths(paths, PathType.Subject, true);
  3219. }
  3220. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3221. public void AddClip(Paths64 paths)
  3222. {
  3223. AddPaths(paths, PathType.Clip);
  3224. }
  3225. public bool Execute(ClipType clipType, FillRule fillRule,
  3226. Paths64 solutionClosed, Paths64 solutionOpen)
  3227. {
  3228. solutionClosed.Clear();
  3229. solutionOpen.Clear();
  3230. try
  3231. {
  3232. ExecuteInternal(clipType, fillRule);
  3233. BuildPaths(solutionClosed, solutionOpen);
  3234. }
  3235. catch
  3236. {
  3237. _succeeded = false;
  3238. }
  3239. ClearSolution();
  3240. return _succeeded;
  3241. }
  3242. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3243. public bool Execute(ClipType clipType, FillRule fillRule, Paths64 solutionClosed)
  3244. {
  3245. return Execute(clipType, fillRule, solutionClosed, new Paths64());
  3246. }
  3247. public bool Execute(ClipType clipType, FillRule fillRule, PolyTree64 polytree, Paths64 openPaths)
  3248. {
  3249. polytree.Clear();
  3250. openPaths.Clear();
  3251. _using_polytree = true;
  3252. try
  3253. {
  3254. ExecuteInternal(clipType, fillRule);
  3255. BuildTree(polytree, openPaths);
  3256. }
  3257. catch
  3258. {
  3259. _succeeded = false;
  3260. }
  3261. ClearSolution();
  3262. return _succeeded;
  3263. }
  3264. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3265. public bool Execute(ClipType clipType, FillRule fillRule, PolyTree64 polytree)
  3266. {
  3267. return Execute(clipType, fillRule, polytree, new Paths64());
  3268. }
  3269. #if USINGZ
  3270. public ZCallback64? ZCallback {
  3271. get { return this._zCallback; }
  3272. set { this._zCallback = value; }
  3273. }
  3274. #endif
  3275. } // Clipper64 class
  3276. public class ClipperD : ClipperBase
  3277. {
  3278. private static string precision_range_error = "Error: Precision is out of range.";
  3279. private readonly double _scale;
  3280. private readonly double _invScale;
  3281. #if USINGZ
  3282. public delegate void ZCallbackD(PointD bot1, PointD top1,
  3283. PointD bot2, PointD top2, ref PointD intersectPt);
  3284. public ZCallbackD? ZCallback { get; set; }
  3285. private void CheckZCallback()
  3286. {
  3287. if (ZCallback != null)
  3288. _zCallback = ZCB;
  3289. else
  3290. _zCallback = null;
  3291. }
  3292. #endif
  3293. public ClipperD(int roundingDecimalPrecision = 2)
  3294. {
  3295. if (roundingDecimalPrecision < -8 || roundingDecimalPrecision > 8)
  3296. throw new ClipperLibException(precision_range_error);
  3297. _scale = Math.Pow(10, roundingDecimalPrecision);
  3298. _invScale = 1 / _scale;
  3299. }
  3300. #if USINGZ
  3301. private void ZCB(Point64 bot1, Point64 top1,
  3302. Point64 bot2, Point64 top2, ref Point64 intersectPt)
  3303. {
  3304. // de-scale (x & y)
  3305. // temporarily convert integers to their initial float values
  3306. // this will slow clipping marginally but will make it much easier
  3307. // to understand the coordinates passed to the callback function
  3308. PointD tmp = new PointD(intersectPt);
  3309. //do the callback
  3310. ZCallback?.Invoke(
  3311. Clipper.ScalePointD(bot1, _invScale),
  3312. Clipper.ScalePointD(top1, _invScale),
  3313. Clipper.ScalePointD(bot2, _invScale),
  3314. Clipper.ScalePointD(top2, _invScale), ref tmp);
  3315. intersectPt = new Point64(intersectPt.X,
  3316. intersectPt.Y, tmp.z);
  3317. }
  3318. #endif
  3319. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3320. public void AddPath(PathD path, PathType polytype, bool isOpen = false)
  3321. {
  3322. base.AddPath(Clipper.ScalePath64(path, _scale), polytype, isOpen);
  3323. }
  3324. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3325. public void AddPaths(PathsD paths, PathType polytype, bool isOpen = false)
  3326. {
  3327. base.AddPaths(Clipper.ScalePaths64(paths, _scale), polytype, isOpen);
  3328. }
  3329. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3330. public void AddSubject(PathD path)
  3331. {
  3332. AddPath(path, PathType.Subject);
  3333. }
  3334. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3335. public void AddOpenSubject(PathD path)
  3336. {
  3337. AddPath(path, PathType.Subject, true);
  3338. }
  3339. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3340. public void AddClip(PathD path)
  3341. {
  3342. AddPath(path, PathType.Clip);
  3343. }
  3344. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3345. public void AddSubject(PathsD paths)
  3346. {
  3347. AddPaths(paths, PathType.Subject);
  3348. }
  3349. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3350. public void AddOpenSubject(PathsD paths)
  3351. {
  3352. AddPaths(paths, PathType.Subject, true);
  3353. }
  3354. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3355. public void AddClip(PathsD paths)
  3356. {
  3357. AddPaths(paths, PathType.Clip);
  3358. }
  3359. public bool Execute(ClipType clipType, FillRule fillRule,
  3360. PathsD solutionClosed, PathsD solutionOpen)
  3361. {
  3362. Paths64 solClosed64 = new Paths64(), solOpen64 = new Paths64();
  3363. #if USINGZ
  3364. CheckZCallback();
  3365. #endif
  3366. bool success = true;
  3367. solutionClosed.Clear();
  3368. solutionOpen.Clear();
  3369. try
  3370. {
  3371. ExecuteInternal(clipType, fillRule);
  3372. BuildPaths(solClosed64, solOpen64);
  3373. }
  3374. catch
  3375. {
  3376. success = false;
  3377. }
  3378. ClearSolution();
  3379. if (!success) return false;
  3380. solutionClosed.Capacity = solClosed64.Count;
  3381. foreach (Path64 path in solClosed64)
  3382. solutionClosed.Add(Clipper.ScalePathD(path, _invScale));
  3383. solutionOpen.Capacity = solOpen64.Count;
  3384. foreach (Path64 path in solOpen64)
  3385. solutionOpen.Add(Clipper.ScalePathD(path, _invScale));
  3386. return true;
  3387. }
  3388. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3389. public bool Execute(ClipType clipType, FillRule fillRule, PathsD solutionClosed)
  3390. {
  3391. return Execute(clipType, fillRule, solutionClosed, new PathsD());
  3392. }
  3393. public bool Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, PathsD openPaths)
  3394. {
  3395. polytree.Clear();
  3396. (polytree as PolyPathD).Scale = _scale;
  3397. #if USINGZ
  3398. CheckZCallback();
  3399. #endif
  3400. openPaths.Clear();
  3401. Paths64 oPaths = new Paths64();
  3402. bool success = true;
  3403. try
  3404. {
  3405. ExecuteInternal(clipType, fillRule);
  3406. BuildTree(polytree, oPaths);
  3407. }
  3408. catch
  3409. {
  3410. success = false;
  3411. }
  3412. ClearSolution();
  3413. if (!success) return false;
  3414. if (oPaths.Count > 0)
  3415. {
  3416. openPaths.Capacity = oPaths.Count;
  3417. foreach (Path64 path in oPaths)
  3418. openPaths.Add(Clipper.ScalePathD(path, _invScale));
  3419. }
  3420. return true;
  3421. }
  3422. public bool Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree)
  3423. {
  3424. return Execute(clipType, fillRule, polytree, new PathsD());
  3425. }
  3426. } // ClipperD class
  3427. public abstract class PolyPathNode : IEnumerable
  3428. {
  3429. internal PolyPathNode? _parent;
  3430. internal List<PolyPathNode> _childs = new List<PolyPathNode>();
  3431. public IEnumerator GetEnumerator()
  3432. {
  3433. return new NodeEnumerator(_childs);
  3434. }
  3435. private class NodeEnumerator : IEnumerator
  3436. {
  3437. private int position = -1;
  3438. private readonly List<PolyPathNode> _nodes;
  3439. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3440. public NodeEnumerator(List<PolyPathNode> nodes)
  3441. {
  3442. _nodes = new List<PolyPathNode>(nodes);
  3443. }
  3444. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3445. private IEnumerator getEnumerator()
  3446. {
  3447. return (IEnumerator)this;
  3448. }
  3449. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3450. public bool MoveNext()
  3451. {
  3452. position++;
  3453. return (position < _nodes.Count);
  3454. }
  3455. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3456. public void Reset()
  3457. {
  3458. position = -1;
  3459. }
  3460. public object Current
  3461. {
  3462. get
  3463. {
  3464. if (position < 0 || position >= _nodes.Count)
  3465. throw new InvalidOperationException();
  3466. return _nodes[position];
  3467. }
  3468. }
  3469. };
  3470. public bool IsHole => GetIsHole();
  3471. public PolyPathNode(PolyPathNode? parent = null) { _parent = parent; }
  3472. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3473. private bool GetIsHole()
  3474. {
  3475. bool result = true;
  3476. PolyPathNode? pp = _parent;
  3477. while (pp != null)
  3478. {
  3479. result = !result;
  3480. pp = pp._parent;
  3481. }
  3482. return result;
  3483. }
  3484. public int Count => _childs.Count;
  3485. internal abstract PolyPathNode AddChild(Path64 p);
  3486. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3487. public void Clear()
  3488. {
  3489. _childs.Clear();
  3490. }
  3491. } // PolyPathBase class
  3492. public class PolyPath64 : PolyPathNode
  3493. {
  3494. public Path64? Polygon { get; private set; } // polytree root's polygon == null
  3495. public PolyPath64(PolyPathNode? parent = null) : base(parent) { }
  3496. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3497. internal override PolyPathNode AddChild(Path64 p)
  3498. {
  3499. PolyPathNode newChild = new PolyPath64(this);
  3500. (newChild as PolyPath64)!.Polygon = p;
  3501. _childs.Add(newChild);
  3502. return newChild;
  3503. }
  3504. [IndexerName("Child")]
  3505. public PolyPath64 this[int index]
  3506. {
  3507. get
  3508. {
  3509. if (index < 0 || index >= _childs.Count)
  3510. throw new InvalidOperationException();
  3511. return (PolyPath64)_childs[index];
  3512. }
  3513. }
  3514. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3515. public double Area()
  3516. {
  3517. double result = Polygon == null ? 0 : Clipper.Area(Polygon);
  3518. foreach (PolyPathNode polyPathBase in _childs)
  3519. {
  3520. PolyPath64 child = (PolyPath64)polyPathBase;
  3521. result += child.Area();
  3522. }
  3523. return result;
  3524. }
  3525. }
  3526. public class PolyPathD : PolyPathNode
  3527. {
  3528. internal double Scale { get; set; }
  3529. public PathD? Polygon { get; private set; }
  3530. public PolyPathD(PolyPathNode? parent = null) : base(parent) { }
  3531. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3532. internal override PolyPathNode AddChild(Path64 p)
  3533. {
  3534. PolyPathNode newChild = new PolyPathD(this);
  3535. (newChild as PolyPathD)!.Scale = Scale;
  3536. (newChild as PolyPathD)!.Polygon = Clipper.ScalePathD(p, 1 / Scale);
  3537. _childs.Add(newChild);
  3538. return newChild;
  3539. }
  3540. [IndexerName("Child")]
  3541. public PolyPathD this[int index]
  3542. {
  3543. get
  3544. {
  3545. if (index < 0 || index >= _childs.Count)
  3546. throw new InvalidOperationException();
  3547. return (PolyPathD)_childs[index];
  3548. }
  3549. }
  3550. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  3551. public double Area()
  3552. {
  3553. double result = Polygon == null ? 0 : Clipper.Area(Polygon);
  3554. foreach (PolyPathNode polyPathBase in _childs)
  3555. {
  3556. PolyPathD child = (PolyPathD)polyPathBase;
  3557. result += child.Area();
  3558. }
  3559. return result;
  3560. }
  3561. }
  3562. public class PolyTree64 : PolyPath64 { }
  3563. public class PolyTreeD : PolyPathD
  3564. {
  3565. public new double Scale => base.Scale;
  3566. }
  3567. public class ClipperLibException : Exception
  3568. {
  3569. public ClipperLibException(string description) : base(description) { }
  3570. }
  3571. } // namespace