Clipper.Engine.pas 123 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259
  1. unit Clipper.Engine;
  2. (*******************************************************************************
  3. * Author : Angus Johnson *
  4. * Date : 22 November 2024 *
  5. * Website : http://www.angusj.com *
  6. * Copyright : Angus Johnson 2010-2024 *
  7. * Purpose : This is the main polygon clipping module *
  8. * License : http://www.boost.org/LICENSE_1_0.txt *
  9. *******************************************************************************)
  10. interface
  11. {$I Clipper.inc}
  12. uses
  13. Classes, Math, Clipper.Core;
  14. type
  15. //PathType:
  16. // 1. only subject paths may be open
  17. // 2. for closed paths, all boolean clipping operations except for
  18. // Difference are commutative. (In other words, subjects and clips
  19. // could be swapped and the same solution will be returned.)
  20. TPathType = (ptSubject, ptClip);
  21. // Vertex: a pre-clipping data structure. It is used to separate polygons
  22. // into ascending and descending 'bounds' (or sides) that start at local
  23. // minima and ascend to a local maxima, before descending again.
  24. TVertexFlag = (vfOpenStart, vfOpenEnd, vfLocMax, vfLocMin);
  25. TVertexFlags = set of TVertexFlag;
  26. PVertex = ^TVertex;
  27. TVertex = record
  28. pt : TPoint64;
  29. next : PVertex;
  30. prev : PVertex;
  31. flags : TVertexFlags;
  32. end;
  33. PPLocalMinima = ^PLocalMinima;
  34. PLocalMinima = ^TLocalMinima;
  35. TLocalMinima = record
  36. vertex : PVertex;
  37. polytype : TPathType;
  38. isOpen : Boolean;
  39. end;
  40. TLocMinList = class(TListEx)
  41. public
  42. function Add: PLocalMinima;
  43. procedure Clear; override;
  44. end;
  45. TReuseableDataContainer64 = class
  46. private
  47. FLocMinList : TLocMinList;
  48. FVertexArrayList : TList;
  49. public
  50. constructor Create;
  51. destructor Destroy; override;
  52. procedure Clear;
  53. procedure AddPaths(const paths: TPaths64;
  54. pathType: TPathType; isOpen: Boolean);
  55. end;
  56. // forward declarations
  57. POutRec = ^TOutRec;
  58. PHorzSegment = ^THorzSegment;
  59. PHorzJoin = ^THorzJoin;
  60. PActive = ^TActive;
  61. TPolyPathBase = class;
  62. TPolyTree64 = class;
  63. TPolyTreeD = class;
  64. // OutPt: vertex data structure for clipping solutions
  65. POutPt = ^TOutPt;
  66. TOutPt = record
  67. pt : TPoint64;
  68. next : POutPt;
  69. prev : POutPt;
  70. outrec : POutRec;
  71. horz : PHorzSegment;
  72. end;
  73. TOutRecArray = array of POutRec;
  74. THorzPosition = (hpBottom, hpMiddle, hpTop);
  75. // OutRec: path data structure for clipping solutions
  76. TOutRec = record
  77. idx : Integer;
  78. owner : POutRec;
  79. frontE : PActive;
  80. backE : PActive;
  81. pts : POutPt;
  82. polypath : TPolyPathBase;
  83. splits : TOutRecArray;
  84. recursiveCheck : POutRec;
  85. bounds : TRect64;
  86. path : TPath64;
  87. isOpen : Boolean;
  88. end;
  89. TOutRecList = class(TListEx)
  90. public
  91. function Add: POutRec;
  92. procedure Clear; override;
  93. end;
  94. THorzSegment = record
  95. leftOp : POutPt;
  96. rightOp : POutPt;
  97. leftToRight : Boolean;
  98. end;
  99. THorzSegList = class(TListEx)
  100. public
  101. procedure Clear; override;
  102. procedure Add(op: POutPt);
  103. end;
  104. THorzJoin = record
  105. op1: POutPt;
  106. op2: POutPt;
  107. end;
  108. THorzJoinList = class(TListEx)
  109. public
  110. procedure Clear; override;
  111. function Add(op1, op2: POutPt): PHorzJoin;
  112. end;
  113. ///////////////////////////////////////////////////////////////////
  114. // Important: UP and DOWN here are premised on Y-axis positive down
  115. // displays, which is the orientation used in Clipper's development.
  116. ///////////////////////////////////////////////////////////////////
  117. TJoinWith = (jwNone, jwLeft, jwRight);
  118. // Active: represents an edge in the Active Edge Table (Vatti's AET)
  119. TActive = record
  120. bot : TPoint64;
  121. top : TPoint64;
  122. currX : Int64; // x relative to *top* of current scanbeam
  123. dx : Double; // inverse of edge slope (zero = vertical)
  124. windDx : Integer; // wind direction (ascending: +1; descending: -1)
  125. windCnt : Integer; // current wind count
  126. windCnt2 : Integer; // current wind count of the opposite TPolyType
  127. outrec : POutRec;
  128. // AEL: 'active edge list' (Vatti's AET - active edge table)
  129. // a linked list of all edges (from left to right) that are present
  130. // (or 'active') within the current scanbeam (a horizontal 'beam' that
  131. // sweeps from bottom to top over the paths in the clipping operation).
  132. prevInAEL : PActive;
  133. nextInAEL : PActive;
  134. // SEL: 'sorted edge list' (Vatti's ST - sorted table)
  135. // linked list used when sorting edges into their new positions at the
  136. // top of scanbeams, but also (re)used to process horizontals.
  137. prevInSEL : PActive;
  138. nextInSEL : PActive;
  139. jump : PActive; // fast merge sorting (see BuildIntersectList())
  140. vertTop : PVertex;
  141. locMin : PLocalMinima; // the bottom of a 'bound' (also Vatti)
  142. isLeftB : Boolean;
  143. joinedWith : TJoinWith;
  144. end;
  145. // IntersectNode: a structure representing 2 intersecting edges.
  146. // Intersections must be sorted so they are processed from the largest
  147. // Y coordinates to the smallest while keeping edges adjacent.
  148. PPIntersectNode = ^PIntersectNode;
  149. PIntersectNode = ^TIntersectNode;
  150. TIntersectNode = record
  151. active1 : PActive;
  152. active2 : PActive;
  153. pt : TPoint64;
  154. end;
  155. // Scanline: a virtual line representing current position
  156. // while processing edges using a "sweep line" algorithm.
  157. PScanLine = ^TScanLine;
  158. TScanLine = record
  159. y : Int64;
  160. next : PScanLine;
  161. end;
  162. {$IFDEF USINGZ}
  163. TZCallback64 = procedure (const bot1, top1, bot2, top2: TPoint64;
  164. var intersectPt: TPoint64) of object;
  165. TZCallbackD = procedure (const bot1, top1, bot2, top2: TPointD;
  166. var intersectPt: TPointD) of object;
  167. {$ENDIF}
  168. // ClipperBase: abstract base
  169. TClipperBase = class
  170. {$IFDEF STRICT}strict{$ENDIF} private
  171. FBotY : Int64;
  172. FScanLine : PScanLine;
  173. FCurrentLocMinIdx : Integer;
  174. FClipType : TClipType;
  175. FFillRule : TFillRule;
  176. FPreserveCollinear : Boolean;
  177. FIntersectList : TList;
  178. FOutRecList : TOutRecList;
  179. FLocMinList : TLocMinList;
  180. FHorzSegList : THorzSegList;
  181. FHorzJoinList : THorzJoinList;
  182. FVertexArrayList : TList;
  183. // FActives: see AEL above
  184. FActives : PActive;
  185. // FSel: see SEL above.
  186. // BUT also used to store horz. edges for later processing
  187. FSel : PActive;
  188. FHasOpenPaths : Boolean;
  189. FLocMinListSorted : Boolean;
  190. FSucceeded : Boolean;
  191. FReverseSolution : Boolean;
  192. {$IFDEF USINGZ}
  193. fDefaultZ : Ztype;
  194. fZCallback : TZCallback64;
  195. {$ENDIF}
  196. procedure Reset;
  197. procedure InsertScanLine(const Y: Int64);
  198. function PopScanLine(out Y: Int64): Boolean;
  199. function PopLocalMinima(Y: Int64;
  200. out localMinima: PLocalMinima): Boolean;
  201. procedure DisposeScanLineList;
  202. procedure DisposeVerticesAndLocalMinima;
  203. function IsContributingClosed(e: PActive): Boolean;
  204. function IsContributingOpen(e: PActive): Boolean;
  205. procedure SetWindCountForClosedPathEdge(e: PActive);
  206. procedure SetWindCountForOpenPathEdge(e: PActive);
  207. procedure InsertLocalMinimaIntoAEL(const botY: Int64);
  208. procedure InsertLeftEdge(e: PActive);
  209. procedure PushHorz(e: PActive); {$IFDEF INLINING} inline; {$ENDIF}
  210. function PopHorz(out e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  211. function StartOpenPath(e: PActive; const pt: TPoint64): POutPt;
  212. procedure UpdateEdgeIntoAEL(var e: PActive);
  213. procedure IntersectEdges(e1, e2: PActive; pt: TPoint64);
  214. procedure DeleteEdges(var e: PActive);
  215. procedure DeleteFromAEL(e: PActive);
  216. procedure AdjustCurrXAndCopyToSEL(topY: Int64);
  217. procedure ConvertHorzSegsToJoins;
  218. procedure ProcessHorzJoins;
  219. procedure DoIntersections(const topY: Int64);
  220. procedure DisposeIntersectNodes;
  221. procedure AddNewIntersectNode(e1, e2: PActive; topY: Int64);
  222. function BuildIntersectList(const topY: Int64): Boolean;
  223. procedure ProcessIntersectList;
  224. procedure SwapPositionsInAEL(e1, e2: PActive);
  225. function AddOutPt(e: PActive; const pt: TPoint64): POutPt;
  226. procedure UndoJoin(e: PActive; const currPt: TPoint64);
  227. procedure CheckJoinLeft(e: PActive;
  228. const pt: TPoint64; checkCurrX: Boolean = false);
  229. {$IFDEF INLINING} inline; {$ENDIF}
  230. procedure CheckJoinRight(e: PActive;
  231. const pt: TPoint64; checkCurrX: Boolean = false);
  232. {$IFDEF INLINING} inline; {$ENDIF}
  233. function AddLocalMinPoly(e1, e2: PActive;
  234. const pt: TPoint64; IsNew: Boolean = false): POutPt;
  235. function AddLocalMaxPoly(e1, e2: PActive; const pt: TPoint64): POutPt;
  236. procedure JoinOutrecPaths(e1, e2: PActive);
  237. function DoMaxima(e: PActive): PActive;
  238. procedure DoHorizontal(horzEdge: PActive);
  239. procedure DoTopOfScanbeam(Y: Int64);
  240. procedure CleanCollinear(outRec: POutRec);
  241. procedure DoSplitOp(outrec: POutRec; splitOp: POutPt);
  242. procedure FixSelfIntersects(outrec: POutRec);
  243. function CheckBounds(outrec: POutRec): Boolean;
  244. function CheckSplitOwner(outrec: POutRec; const splits: TOutRecArray): Boolean;
  245. procedure RecursiveCheckOwners(outrec: POutRec; polytree: TPolyPathBase);
  246. protected
  247. FUsingPolytree : Boolean;
  248. procedure AddPath(const path: TPath64;
  249. pathType: TPathType; isOpen: Boolean);
  250. procedure AddPaths(const paths: TPaths64;
  251. pathType: TPathType; isOpen: Boolean);
  252. procedure AddReuseableData(const reuseableData: TReuseableDataContainer64);
  253. function ClearSolutionOnly: Boolean;
  254. procedure ExecuteInternal(clipType: TClipType;
  255. fillRule: TFillRule; usingPolytree: Boolean);
  256. function BuildPaths(var closedPaths, openPaths: TPaths64): Boolean;
  257. function BuildTree(polytree: TPolyPathBase; out openPaths: TPaths64): Boolean;
  258. {$IFDEF USINGZ}
  259. procedure SetZ( e1, e2: PActive; var intersectPt: TPoint64);
  260. property ZCallback : TZCallback64 read fZCallback write fZCallback;
  261. property DefaultZ : Ztype read fDefaultZ write fDefaultZ;
  262. {$ENDIF}
  263. property Succeeded : Boolean read FSucceeded;
  264. public
  265. constructor Create; virtual;
  266. destructor Destroy; override;
  267. procedure Clear;
  268. function GetBounds: TRect64;
  269. property PreserveCollinear: Boolean read
  270. FPreserveCollinear write FPreserveCollinear;
  271. property ReverseSolution: Boolean read
  272. FReverseSolution write FReverseSolution;
  273. end;
  274. TClipper64 = class(TClipperBase) // for integer coordinates
  275. public
  276. procedure AddReuseableData(const reuseableData: TReuseableDataContainer64);
  277. procedure AddSubject(const subject: TPath64); overload;
  278. procedure AddSubject(const subjects: TPaths64); overload;
  279. procedure AddOpenSubject(const subject: TPath64); overload;
  280. procedure AddOpenSubject(const subjects: TPaths64); overload;
  281. procedure AddClip(const clip: TPath64); overload;
  282. procedure AddClip(const clips: TPaths64); overload;
  283. function Execute(clipType: TClipType; fillRule: TFillRule;
  284. out closedSolutions: TPaths64): Boolean; overload; virtual;
  285. function Execute(clipType: TClipType; fillRule: TFillRule;
  286. out closedSolutions, openSolutions: TPaths64): Boolean; overload; virtual;
  287. function Execute(clipType: TClipType; fillRule: TFillRule;
  288. var solutionTree: TPolyTree64; out openSolutions: TPaths64): Boolean; overload; virtual;
  289. {$IFDEF USINGZ}
  290. property ZCallback;
  291. {$ENDIF}
  292. end;
  293. // PolyPathBase: ancestor of TPolyPath and TPolyPathD
  294. TPolyPathBase = class
  295. {$IFDEF STRICT}strict{$ENDIF} private
  296. FParent : TPolyPathBase;
  297. FChildList : TList;
  298. function GetChildCnt: Integer;
  299. function GetIsHole: Boolean;
  300. function GetLevel: Integer;
  301. protected
  302. function GetChild(index: Integer): TPolyPathBase;
  303. function AddChild(const path: TPath64): TPolyPathBase; virtual; abstract;
  304. property ChildList: TList read FChildList;
  305. property Parent: TPolyPathBase read FParent write FParent;
  306. public
  307. constructor Create; virtual;
  308. destructor Destroy; override;
  309. procedure Clear; virtual;
  310. property IsHole: Boolean read GetIsHole;
  311. property Count: Integer read GetChildCnt;
  312. property Child[index: Integer]: TPolyPathBase read GetChild; default;
  313. property Level: Integer read GetLevel;
  314. end;
  315. TPolyPath64 = class(TPolyPathBase)
  316. {$IFDEF STRICT}strict{$ENDIF} private
  317. FPath : TPath64;
  318. function GetChild64(index: Integer): TPolyPath64;
  319. public
  320. function AddChild(const path: TPath64): TPolyPathBase; override;
  321. property Child[index: Integer]: TPolyPath64 read GetChild64; default;
  322. property Polygon: TPath64 read FPath;
  323. end;
  324. // PolyTree: is intended as a READ-ONLY data structure to receive closed path
  325. // solutions to clipping operations. While this structure is more complex than
  326. // the alternative TPaths structure, it does model path ownership (ie paths
  327. // that are contained by other paths). This will be useful to some users.
  328. TPolyTree64 = class(TPolyPath64);
  329. // FLOATING POINT POLYGON COORDINATES (D suffix to indicate double precision)
  330. // To preserve numerical robustness, clipping must be done using integer
  331. // coordinates. Consequently, polygons that are defined with floating point
  332. // coordinates will need these converted into integer values together with
  333. // scaling to achieve the desired floating point precision.
  334. TClipperD = class(TClipperBase) // for floating point coordinates
  335. {$IFDEF STRICT}strict{$ENDIF} private
  336. FScale: double;
  337. FInvScale: double;
  338. {$IFDEF USINGZ}
  339. fZCallback : TZCallbackD;
  340. procedure ZCB(const bot1, top1, bot2, top2: TPoint64; var intersectPt: TPoint64);
  341. procedure CheckCallback;
  342. {$ENDIF}
  343. public
  344. procedure AddSubject(const pathD: TPathD); overload;
  345. procedure AddSubject(const pathsD: TPathsD); overload;
  346. procedure AddOpenSubject(const pathD: TPathD); overload;
  347. procedure AddOpenSubject(const pathsD: TPathsD); overload;
  348. procedure AddClip(const pathD: TPathD); overload;
  349. procedure AddClip(const pathsD: TPathsD); overload;
  350. constructor Create(precision: integer = 2);
  351. reintroduce; overload;
  352. function Execute(clipType: TClipType; fillRule: TFillRule;
  353. out closedSolutions: TPathsD): Boolean; overload;
  354. function Execute(clipType: TClipType; fillRule: TFillRule;
  355. out closedSolutions, openSolutions: TPathsD): Boolean; overload;
  356. function Execute(clipType: TClipType; fillRule: TFillRule;
  357. var solutionsTree: TPolyTreeD; out openSolutions: TPathsD): Boolean; overload;
  358. {$IFDEF USINGZ}
  359. property ZCallback : TZCallbackD read fZCallback write fZCallback;
  360. {$ENDIF}
  361. end;
  362. TPolyPathD = class(TPolyPathBase)
  363. {$IFDEF STRICT}strict{$ENDIF} private
  364. FPath : TPathD;
  365. function GetChildD(index: Integer): TPolyPathD;
  366. protected
  367. FScale : double;
  368. public
  369. function AddChild(const path: TPath64): TPolyPathBase; overload; override;
  370. function AddChild(const path: TPathD): TPolyPathBase; reintroduce; overload;
  371. property Polygon: TPathD read FPath;
  372. property Child[index: Integer]: TPolyPathD read GetChildD; default;
  373. end;
  374. TPolyTreeD = class(TPolyPathD)
  375. protected
  376. procedure SetScale(value: double); // alternative to friend class
  377. public
  378. property Scale: double read FScale;
  379. end;
  380. resourcestring
  381. rsClipper_PolyTreeErr = 'The TPolyTree parameter must be assigned.';
  382. rsClipper_ClippingErr = 'Undefined clipping error';
  383. implementation
  384. //OVERFLOWCHECKS OFF is a necessary workaround for a compiler bug that very
  385. //occasionally reports incorrect overflow errors in Delphi versions before 10.2.
  386. //see https://forums.embarcadero.com/message.jspa?messageID=871444
  387. {$OVERFLOWCHECKS OFF}
  388. const
  389. DefaultClipperDScale = 100;
  390. //------------------------------------------------------------------------------
  391. // TLocMinList class
  392. //------------------------------------------------------------------------------
  393. function TLocMinList.Add: PLocalMinima;
  394. begin
  395. new(Result);
  396. inherited Add(Result);
  397. end;
  398. //------------------------------------------------------------------------------
  399. procedure TLocMinList.Clear;
  400. var
  401. i: integer;
  402. begin
  403. for i := 0 to Count -1 do
  404. Dispose(PLocalMinima(UnsafeGet(i)));
  405. inherited;
  406. end;
  407. //------------------------------------------------------------------------------
  408. // TOutRecList class
  409. //------------------------------------------------------------------------------
  410. function TOutRecList.Add: POutRec;
  411. begin
  412. new(Result);
  413. FillChar(Result^, SizeOf(TOutRec), 0);
  414. Result.idx := inherited Add(Result);
  415. end;
  416. //------------------------------------------------------------------------------
  417. procedure TOutRecList.Clear;
  418. var
  419. i: integer;
  420. por: POutRec;
  421. op, tmpPp: POutPt;
  422. begin
  423. for i := 0 to Count -1 do
  424. begin
  425. por := UnsafeGet(i);
  426. if Assigned(por.pts) then
  427. begin
  428. op := por.pts;
  429. op.prev.next := nil;
  430. while Assigned(op) do
  431. begin
  432. tmpPp := op;
  433. op := op.next;
  434. Dispose(tmpPp);
  435. end;
  436. end;
  437. Dispose(por);
  438. end;
  439. inherited;
  440. end;
  441. //------------------------------------------------------------------------------
  442. // THorzSegList
  443. //------------------------------------------------------------------------------
  444. procedure THorzSegList.Clear;
  445. var
  446. i: integer;
  447. begin
  448. for i := 0 to Count -1 do
  449. Dispose(PHorzSegment(UnsafeGet(i)));
  450. inherited;
  451. end;
  452. //------------------------------------------------------------------------------
  453. procedure THorzSegList.Add(op: POutPt);
  454. var
  455. hs: PHorzSegment;
  456. begin
  457. if (op.outrec.isOpen) then Exit;
  458. new(hs);
  459. hs.leftOp := op;
  460. op.horz := nil;
  461. inherited Add(hs);
  462. end;
  463. //------------------------------------------------------------------------------
  464. // THorzJoinList
  465. //------------------------------------------------------------------------------
  466. procedure THorzJoinList.Clear;
  467. var
  468. i: integer;
  469. begin
  470. for i := 0 to Count -1 do
  471. Dispose(PHorzJoin(UnsafeGet(i)));
  472. inherited;
  473. end;
  474. //------------------------------------------------------------------------------
  475. function THorzJoinList.Add(op1, op2: POutPt): PHorzJoin;
  476. begin
  477. new(Result);
  478. Result.op1 := op1;
  479. Result.op2 := op2;
  480. inherited Add(Result);
  481. end;
  482. //------------------------------------------------------------------------------
  483. // Miscellaneous Functions ...
  484. //------------------------------------------------------------------------------
  485. function UnsafeGet(List: TList; Index: Integer): Pointer;
  486. {$IFDEF INLINING} inline; {$ENDIF}
  487. begin
  488. Result := List.List[Index];
  489. end;
  490. //------------------------------------------------------------------------------
  491. function IsOpen(e: PActive): Boolean; overload; {$IFDEF INLINING} inline; {$ENDIF}
  492. begin
  493. Result := e.locMin.isOpen;
  494. end;
  495. //------------------------------------------------------------------------------
  496. function IsOpenEnd(v: PVertex): Boolean; overload; {$IFDEF INLINING} inline; {$ENDIF}
  497. begin
  498. Result := (v.flags * [vfOpenStart, vfOpenEnd] <> []);
  499. end;
  500. //------------------------------------------------------------------------------
  501. function IsHotEdge(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  502. begin
  503. Result := assigned(e.outrec);
  504. end;
  505. //------------------------------------------------------------------------------
  506. function GetPrevHotEdge(e: PActive): PActive; {$IFDEF INLINING} inline; {$ENDIF}
  507. begin
  508. Result := e.prevInAEL;
  509. while assigned(Result) and (IsOpen(Result) or not IsHotEdge(Result)) do
  510. Result := Result.prevInAEL;
  511. end;
  512. //------------------------------------------------------------------------------
  513. function IsFront(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  514. begin
  515. Result := (e = e.outrec.frontE);
  516. end;
  517. //------------------------------------------------------------------------------
  518. function NewOutPt(const pt: TPoint64;
  519. outrec: POutRec; prev, next: POutPt): POutPt; overload;
  520. {$IFDEF INLINING} inline; {$ENDIF}
  521. begin
  522. new(Result);
  523. Result.pt := pt;
  524. Result.next := next;
  525. Result.prev := prev;
  526. Result.outrec := outrec;
  527. Result.horz := nil;
  528. end;
  529. //------------------------------------------------------------------------------
  530. function NewOutPt(const pt: TPoint64; outrec: POutRec): POutPt; overload;
  531. {$IFDEF INLINING} inline; {$ENDIF}
  532. begin
  533. new(Result);
  534. Result.pt := pt;
  535. Result.next := Result;
  536. Result.prev := Result;
  537. Result.outrec := outrec;
  538. Result.horz := nil;
  539. end;
  540. //------------------------------------------------------------------------------
  541. function DuplicateOp(op:POutPt; insertNext: Boolean): POutPt;
  542. {$IFDEF INLINING} inline; {$ENDIF}
  543. begin
  544. new(Result);
  545. Result.pt := op.pt;
  546. Result.outrec := op.outrec;
  547. Result.horz := nil;
  548. if insertNext then
  549. begin
  550. Result.next := op.next;
  551. Result.next.prev := Result;
  552. Result.prev := op;
  553. op.next := Result;
  554. end else
  555. begin
  556. Result.prev := op.prev;
  557. Result.prev.next := Result;
  558. Result.next := op;
  559. op.prev := Result;
  560. end;
  561. end;
  562. //------------------------------------------------------------------------------
  563. function GetRealOutRec(outRec: POutRec): POutRec;
  564. {$IFDEF INLINING} inline; {$ENDIF}
  565. begin
  566. Result := outRec;
  567. while Assigned(Result) and not Assigned(Result.pts) do
  568. Result := Result.owner;
  569. end;
  570. //------------------------------------------------------------------------------
  571. function IsValidOwner(outRec, TestOwner: POutRec): Boolean;
  572. {$IFDEF INLINING} inline; {$ENDIF}
  573. begin
  574. while Assigned(TestOwner) and (outrec <> TestOwner) do
  575. TestOwner := TestOwner.owner;
  576. Result := not Assigned(TestOwner);
  577. end;
  578. //------------------------------------------------------------------------------
  579. function PtsReallyClose(const pt1, pt2: TPoint64): Boolean;
  580. {$IFDEF INLINING} inline; {$ENDIF}
  581. begin
  582. Result := (abs(pt1.X - pt2.X) < 2) and (abs(pt1.Y - pt2.Y) < 2);
  583. end;
  584. //------------------------------------------------------------------------------
  585. function IsVerySmallTriangle(op: POutPt): Boolean;
  586. {$IFDEF INLINING} inline; {$ENDIF}
  587. begin
  588. //also treat inconsequential polygons as invalid
  589. Result := (op.next.next = op.prev) and
  590. (PtsReallyClose(op.prev.pt, op.next.pt) or
  591. PtsReallyClose(op.pt, op.next.pt) or
  592. PtsReallyClose(op.pt, op.prev.pt));
  593. end;
  594. //------------------------------------------------------------------------------
  595. function IsValidClosedPath(op: POutPt): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  596. begin
  597. result := assigned(op) and (op.next <> op) and
  598. (op.next <> op.prev) and not IsVerySmallTriangle(op);
  599. end;
  600. //------------------------------------------------------------------------------
  601. (*******************************************************************************
  602. * Dx: 0(90deg) *
  603. * | *
  604. * +inf (180deg) <--- o ---> -inf (0deg) *
  605. *******************************************************************************)
  606. function GetDx(const pt1, pt2: TPoint64): double;
  607. {$IFDEF INLINING} inline; {$ENDIF}
  608. var
  609. dy: Int64;
  610. begin
  611. dy := (pt2.Y - pt1.Y);
  612. if dy <> 0 then result := (pt2.X - pt1.X) / dy
  613. else if (pt2.X > pt1.X) then result := NegInfinity
  614. else result := Infinity;
  615. end;
  616. //------------------------------------------------------------------------------
  617. function TopX(e: PActive; const currentY: Int64): Int64; overload;
  618. {$IFDEF INLINING} inline; {$ENDIF}
  619. begin
  620. if (currentY = e.top.Y) or (e.top.X = e.bot.X) then Result := e.top.X
  621. else if (currentY = e.bot.Y) then Result := e.bot.X
  622. else Result := e.bot.X + Round(e.dx*(currentY - e.bot.Y));
  623. end;
  624. //------------------------------------------------------------------------------
  625. function IsHorizontal(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  626. begin
  627. Result := (e.top.Y = e.bot.Y);
  628. end;
  629. //------------------------------------------------------------------------------
  630. function IsHeadingRightHorz(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  631. begin
  632. Result := (e.dx = NegInfinity);
  633. end;
  634. //------------------------------------------------------------------------------
  635. function IsHeadingLeftHorz(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  636. begin
  637. Result := (e.dx = Infinity);
  638. end;
  639. //------------------------------------------------------------------------------
  640. function IsJoined(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  641. begin
  642. Result := e.joinedWith <> jwNone;
  643. end;
  644. //------------------------------------------------------------------------------
  645. procedure SwapActives(var e1, e2: PActive); {$IFDEF INLINING} inline; {$ENDIF}
  646. var
  647. e: PActive;
  648. begin
  649. e := e1; e1 := e2; e2 := e;
  650. end;
  651. //------------------------------------------------------------------------------
  652. function GetPolyType(const e: PActive): TPathType;
  653. {$IFDEF INLINING} inline; {$ENDIF}
  654. begin
  655. Result := e.locMin.polytype;
  656. end;
  657. //------------------------------------------------------------------------------
  658. function IsSamePolyType(const e1, e2: PActive): Boolean;
  659. {$IFDEF INLINING} inline; {$ENDIF}
  660. begin
  661. Result := e1.locMin.polytype = e2.locMin.polytype;
  662. end;
  663. //------------------------------------------------------------------------------
  664. procedure SetDx(e: PActive); {$IFDEF INLINING} inline; {$ENDIF}
  665. begin
  666. e.dx := GetDx(e.bot, e.top);
  667. end;
  668. //------------------------------------------------------------------------------
  669. function IsLeftBound(e: PActive): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  670. begin
  671. Result := e.isLeftB;
  672. end;
  673. //------------------------------------------------------------------------------
  674. function NextVertex(e: PActive): PVertex; // ie heading (inverted Y-axis) "up"
  675. {$IFDEF INLINING} inline; {$ENDIF}
  676. begin
  677. if e.windDx > 0 then
  678. Result := e.vertTop.next else
  679. Result := e.vertTop.prev;
  680. end;
  681. //------------------------------------------------------------------------------
  682. //PrevPrevVertex: useful to get the (inverted Y-axis) top of the
  683. //alternate edge (ie left or right bound) during edge insertion.
  684. function PrevPrevVertex(e: PActive): PVertex;
  685. {$IFDEF INLINING} inline; {$ENDIF}
  686. begin
  687. if e.windDx > 0 then
  688. Result := e.vertTop.prev.prev else
  689. Result := e.vertTop.next.next;
  690. end;
  691. //------------------------------------------------------------------------------
  692. function IsMaxima(vertex: PVertex): Boolean; overload;
  693. {$IFDEF INLINING} inline; {$ENDIF}
  694. begin
  695. Result := vfLocMax in vertex.flags;
  696. end;
  697. //------------------------------------------------------------------------------
  698. function IsMaxima(e: PActive): Boolean; overload;
  699. {$IFDEF INLINING} inline; {$ENDIF}
  700. begin
  701. Result := vfLocMax in e.vertTop.flags;
  702. end;
  703. //------------------------------------------------------------------------------
  704. function GetCurrYMaximaVertexOpen(e: PActive): PVertex;
  705. begin
  706. Result := e.vertTop;
  707. if e.windDx > 0 then
  708. while (Result.next.pt.Y = Result.pt.Y) and
  709. (Result.flags * [vfOpenEnd, vfLocMax] = []) do
  710. Result := Result.next
  711. else
  712. while (Result.prev.pt.Y = Result.pt.Y) and
  713. (Result.flags * [vfOpenEnd, vfLocMax] = []) do
  714. Result := Result.prev;
  715. if not IsMaxima(Result) then Result := nil; // not a maxima
  716. end;
  717. //------------------------------------------------------------------------------
  718. function GetCurrYMaximaVertex(e: PActive): PVertex;
  719. begin
  720. // nb: function not safe with open paths
  721. Result := e.vertTop;
  722. if e.windDx > 0 then
  723. while Result.next.pt.Y = Result.pt.Y do Result := Result.next
  724. else
  725. while Result.prev.pt.Y = Result.pt.Y do Result := Result.prev;
  726. if not IsMaxima(Result) then Result := nil; // not a maxima
  727. end;
  728. //------------------------------------------------------------------------------
  729. function GetMaximaPair(e: PActive): PActive;
  730. begin
  731. Result := e.nextInAEL;
  732. while assigned(Result) do
  733. begin
  734. if Result.vertTop = e.vertTop then Exit; // Found!
  735. Result := Result.nextInAEL;
  736. end;
  737. Result := nil;
  738. end;
  739. //------------------------------------------------------------------------------
  740. function PointCount(pts: POutPt): Integer; {$IFDEF INLINING} inline; {$ENDIF}
  741. var
  742. p: POutPt;
  743. begin
  744. Result := 0;
  745. if not Assigned(pts) then Exit;
  746. p := pts;
  747. repeat
  748. Inc(Result);
  749. p := p.next;
  750. until p = pts;
  751. end;
  752. //------------------------------------------------------------------------------
  753. function GetCleanPath(op: POutPt): TPath64;
  754. var
  755. cnt: integer;
  756. op2, prevOp: POutPt;
  757. begin
  758. cnt := 0;
  759. SetLength(Result, PointCount(op));
  760. op2 := op;
  761. while ((op2.next <> op) and
  762. (((op2.pt.X = op2.next.pt.X) and (op2.pt.X = op2.prev.pt.X)) or
  763. ((op2.pt.Y = op2.next.pt.Y) and (op2.pt.Y = op2.prev.pt.Y)))) do
  764. op2 := op2.next;
  765. result[cnt] := op2.pt;
  766. inc(cnt);
  767. prevOp := op2;
  768. op2 := op2.next;
  769. while (op2 <> op) do
  770. begin
  771. if (((op2.pt.X <> op2.next.pt.X) or (op2.pt.X <> prevOp.pt.X)) and
  772. ((op2.pt.Y <> op2.next.pt.Y) or (op2.pt.Y <> prevOp.pt.Y))) then
  773. begin
  774. result[cnt] := op2.pt;
  775. inc(cnt);
  776. prevOp := op2;
  777. end;
  778. op2 := op2.next;
  779. end;
  780. SetLength(Result, cnt);
  781. end;
  782. function PointInOpPolygon(const pt: TPoint64; op: POutPt): TPointInPolygonResult;
  783. var
  784. val: Integer;
  785. op2: POutPt;
  786. isAbove, startingAbove: Boolean;
  787. d: double; // avoids integer overflow
  788. begin
  789. result := pipOutside;
  790. if (op = op.next) or (op.prev = op.next) then Exit;
  791. op2 := op;
  792. repeat
  793. if (op.pt.Y <> pt.Y) then break;
  794. op := op.next;
  795. until op = op2;
  796. if (op.pt.Y = pt.Y) then Exit; // not a proper polygon
  797. isAbove := op.pt.Y < pt.Y;
  798. startingAbove := isAbove;
  799. Result := pipOn;
  800. val := 0;
  801. op2 := op.next;
  802. while (op2 <> op) do
  803. begin
  804. if isAbove then
  805. while (op2 <> op) and (op2.pt.Y < pt.Y) do op2 := op2.next
  806. else
  807. while (op2 <> op) and (op2.pt.Y > pt.Y) do op2 := op2.next;
  808. if (op2 = op) then break;
  809. // must have touched or crossed the pt.Y horizonal
  810. // and this must happen an even number of times
  811. if (op2.pt.Y = pt.Y) then // touching the horizontal
  812. begin
  813. if (op2.pt.X = pt.X) or ((op2.pt.Y = op2.prev.pt.Y) and
  814. ((pt.X < op2.prev.pt.X) <> (pt.X < op2.pt.X))) then Exit;
  815. op2 := op2.next;
  816. if (op2 = op) then break;
  817. Continue;
  818. end;
  819. if (pt.X < op2.pt.X) and (pt.X < op2.prev.pt.X) then
  820. // do nothing because
  821. // we're only interested in edges crossing on the left
  822. else if((pt.X > op2.prev.pt.X) and (pt.X > op2.pt.X)) then
  823. val := 1 - val // toggle val
  824. else
  825. begin
  826. d := CrossProduct(op2.prev.pt, op2.pt, pt);
  827. if d = 0 then Exit; // ie point on path
  828. if (d < 0) = isAbove then val := 1 - val;
  829. end;
  830. isAbove := not isAbove;
  831. op2 := op2.next;
  832. end;
  833. if (isAbove <> startingAbove) then
  834. begin
  835. d := CrossProduct(op2.prev.pt, op2.pt, pt);
  836. if d = 0 then Exit; // ie point on path
  837. if (d < 0) = isAbove then val := 1 - val;
  838. end;
  839. if val = 0 then
  840. result := pipOutside else
  841. result := pipInside;
  842. end;
  843. //------------------------------------------------------------------------------
  844. function Path1InsidePath2(const op1, op2: POutPt): Boolean;
  845. var
  846. op: POutPt;
  847. mp: TPoint64;
  848. path: TPath64;
  849. pipResult: TPointInPolygonResult;
  850. outsideCnt: integer;
  851. begin
  852. // precondition - the twi paths or1 & pr2 don't intersect
  853. // we need to make some accommodation for rounding errors
  854. // so we won't jump if the first vertex is found outside
  855. outsideCnt := 0;
  856. op := op1;
  857. repeat
  858. pipResult := PointInOpPolygon(op.pt, op2);
  859. if pipResult = pipOutside then inc(outsideCnt)
  860. else if pipResult = pipInside then dec(outsideCnt);
  861. op := op.next;
  862. until (op = op1) or (Abs(outsideCnt) = 2);
  863. if (Abs(outsideCnt) < 2) then
  864. begin
  865. // if path1's location is still equivocal then check its midpoint
  866. path := GetCleanPath(op1);
  867. mp := Clipper.Core.GetBounds(path).MidPoint;
  868. path := GetCleanPath(op2);
  869. Result := PointInPolygon(mp, path) <> pipOutside;
  870. end
  871. else
  872. Result := (outsideCnt < 0);
  873. end;
  874. //------------------------------------------------------------------------------
  875. procedure UncoupleOutRec(e: PActive);
  876. var
  877. outRec: POutRec;
  878. begin
  879. if not Assigned(e.outrec) then Exit;
  880. outRec := e.outrec;
  881. outRec.frontE.outrec := nil;
  882. outRec.backE.outrec := nil;
  883. outRec.frontE := nil;
  884. outRec.backE := nil;
  885. end;
  886. //------------------------------------------------------------------------------
  887. procedure AddPathsToVertexList(const paths: TPaths64;
  888. polyType: TPathType; isOpen: Boolean;
  889. vertexList: TList; LocMinList: TLocMinList);
  890. var
  891. i, j, len, totalVerts: integer;
  892. p: PPoint64;
  893. v, va0, vaCurr, vaPrev: PVertex;
  894. ascending, ascending0: Boolean;
  895. procedure AddLocMin(vert: PVertex);
  896. var
  897. lm: PLocalMinima;
  898. begin
  899. if vfLocMin in vert.flags then Exit; // ie already added
  900. Include(vert.flags, vfLocMin);
  901. lm := LocMinList.Add;
  902. lm.vertex := vert;
  903. lm.polytype := polyType;
  904. lm.isOpen := isOpen;
  905. end;
  906. //---------------------------------------------------------
  907. begin
  908. // count the total (maximum) number of vertices required
  909. totalVerts := 0;
  910. for i := 0 to High(paths) do
  911. totalVerts := totalVerts + Length(paths[i]);
  912. if (totalVerts = 0) then Exit;
  913. // allocate memory
  914. GetMem(v, sizeof(TVertex) * totalVerts);
  915. vertexList.Add(v);
  916. {$IF not defined(FPC) and (CompilerVersion <= 26.0)}
  917. // Delphi 7-XE5 have a problem with "continue" and the
  918. // code analysis, marking "ascending" as "not initialized"
  919. ascending := False;
  920. {$IFEND}
  921. for i := 0 to High(paths) do
  922. begin
  923. len := Length(paths[i]);
  924. if (len < 3) and (not isOpen or (len < 2)) then Continue;
  925. p := @paths[i][0];
  926. va0 := v; vaCurr := v;
  927. vaCurr.pt := p^;
  928. vaCurr.prev := nil;
  929. inc(p);
  930. vaCurr.flags := [];
  931. vaPrev := vaCurr;
  932. inc(vaCurr);
  933. for j := 1 to len -1 do
  934. begin
  935. if PointsEqual(vaPrev.pt, p^) then
  936. begin
  937. inc(p);
  938. Continue; // skips duplicates
  939. end;
  940. vaPrev.next := vaCurr;
  941. vaCurr.prev := vaPrev;
  942. vaCurr.pt := p^;
  943. vaCurr.flags := [];
  944. vaPrev := vaCurr;
  945. inc(vaCurr);
  946. inc(p);
  947. end;
  948. if not Assigned(vaPrev.prev) then Continue;
  949. if not isOpen and PointsEqual(vaPrev.pt, va0.pt) then
  950. vaPrev := vaPrev.prev;
  951. vaPrev.next := va0;
  952. va0.prev := vaPrev;
  953. v := vaCurr; // ie get ready for next path
  954. if isOpen and (va0.next = va0) then Continue;
  955. // now find and assign local minima
  956. if (isOpen) then
  957. begin
  958. vaCurr := va0.next;
  959. while (vaCurr <> va0) and (vaCurr.pt.Y = va0.pt.Y) do
  960. vaCurr := vaCurr.next;
  961. ascending := vaCurr.pt.Y <= va0.pt.Y;
  962. if (ascending) then
  963. begin
  964. va0.flags := [vfOpenStart];
  965. AddLocMin(va0);
  966. end
  967. else
  968. va0.flags := [vfOpenStart, vfLocMax];
  969. end else
  970. begin
  971. // closed path
  972. vaPrev := va0.prev;
  973. while (vaPrev <> va0) and (vaPrev.pt.Y = va0.pt.Y) do
  974. vaPrev := vaPrev.prev;
  975. if (vaPrev = va0) then
  976. Continue; // only open paths can be completely flat
  977. ascending := vaPrev.pt.Y > va0.pt.Y;
  978. end;
  979. ascending0 := ascending;
  980. vaPrev := va0;
  981. vaCurr := va0.next;
  982. while (vaCurr <> va0) do
  983. begin
  984. if (vaCurr.pt.Y > vaPrev.pt.Y) and ascending then
  985. begin
  986. Include(vaPrev.flags, vfLocMax);
  987. ascending := false;
  988. end
  989. else if (vaCurr.pt.Y < vaPrev.pt.Y) and not ascending then
  990. begin
  991. ascending := true;
  992. AddLocMin(vaPrev);
  993. end;
  994. vaPrev := vaCurr;
  995. vaCurr := vaCurr.next;
  996. end;
  997. if (isOpen) then
  998. begin
  999. Include(vaPrev.flags, vfOpenEnd);
  1000. if ascending then
  1001. Include(vaPrev.flags, vfLocMax) else
  1002. AddLocMin(vaPrev);
  1003. end
  1004. else if (ascending <> ascending0) then
  1005. begin
  1006. if (ascending0) then AddLocMin(vaPrev)
  1007. else Include(vaPrev.flags, vfLocMax);
  1008. end;
  1009. end;
  1010. end;
  1011. //------------------------------------------------------------------------------
  1012. function BuildPath(op: POutPt; reverse, isOpen: Boolean;
  1013. out path: TPath64): Boolean;
  1014. var
  1015. i,j, cnt: integer;
  1016. begin
  1017. cnt := PointCount(op);
  1018. if (cnt < 3) and (not isOpen or (Cnt < 2)) then
  1019. begin
  1020. Result := false;
  1021. Exit;
  1022. end;
  1023. if (cnt = 3) and not IsOpen and IsVerySmallTriangle(op) then
  1024. begin
  1025. Result := false;
  1026. Exit;
  1027. end;
  1028. setLength(path, cnt);
  1029. if reverse then
  1030. begin
  1031. path[0] := op.pt;
  1032. op := op.prev;
  1033. end else
  1034. begin
  1035. op := op.next;
  1036. path[0] := op.pt;
  1037. op := op.next;
  1038. end;
  1039. j := 0;
  1040. for i := 0 to cnt -2 do
  1041. begin
  1042. if not PointsEqual(path[j], op.pt) then
  1043. begin
  1044. inc(j);
  1045. path[j] := op.pt;
  1046. end;
  1047. if reverse then op := op.prev else op := op.next;
  1048. end;
  1049. setLength(path, j+1);
  1050. if isOpen then
  1051. Result := (j > 0) else
  1052. Result := (j > 1);
  1053. end;
  1054. //------------------------------------------------------------------------------
  1055. function DisposeOutPt(op: POutPt): POutPt;
  1056. begin
  1057. if op.next = op then
  1058. Result := nil else
  1059. Result := op.next;
  1060. op.prev.next := op.next;
  1061. op.next.prev := op.prev;
  1062. Dispose(Op);
  1063. end;
  1064. //------------------------------------------------------------------------------
  1065. function LocMinListSort(item1, item2: Pointer): Integer;
  1066. var
  1067. q: Int64;
  1068. lm1: PLocalMinima absolute item1;
  1069. lm2: PLocalMinima absolute item2;
  1070. begin
  1071. q := lm2.vertex.pt.Y - lm1.vertex.pt.Y;
  1072. if q < 0 then
  1073. Result := -1
  1074. else if q > 0 then
  1075. Result := 1
  1076. else
  1077. begin
  1078. q := lm2.vertex.pt.X - lm1.vertex.pt.X;
  1079. if q < 0 then Result := 1
  1080. else if q > 0 then Result := -1
  1081. else Result := 0;
  1082. end;
  1083. end;
  1084. //------------------------------------------------------------------------------
  1085. function HorzSegListSort(item1, item2: Pointer): Integer;
  1086. var
  1087. q: Int64;
  1088. h1: PHorzSegment absolute item1;
  1089. h2: PHorzSegment absolute item2;
  1090. begin
  1091. q := h2.leftOp.pt.X - h1.leftOp.pt.X;
  1092. if q > 0 then Result := -1
  1093. else if q < 0 then Result := 1
  1094. else Result := h1.leftOp.outrec.idx - h2.leftOp.outrec.idx;
  1095. end;
  1096. //------------------------------------------------------------------------------
  1097. procedure SetSides(outRec: POutRec; startEdge, endEdge: PActive);
  1098. {$IFDEF INLINING} inline; {$ENDIF}
  1099. begin
  1100. outRec.frontE := startEdge;
  1101. outRec.backE := endEdge;
  1102. end;
  1103. //------------------------------------------------------------------------------
  1104. procedure SwapOutRecs(e1, e2: PActive);
  1105. var
  1106. or1, or2: POutRec;
  1107. e: PActive;
  1108. begin
  1109. or1 := e1.outrec;
  1110. or2 := e2.outrec;
  1111. if (or1 = or2) then
  1112. begin
  1113. // nb: at least one edge is 'hot'
  1114. e := or1.frontE;
  1115. or1.frontE := or1.backE;
  1116. or1.backE := e;
  1117. Exit;
  1118. end;
  1119. if assigned(or1) then
  1120. begin
  1121. if e1 = or1.frontE then
  1122. or1.frontE := e2 else
  1123. or1.backE := e2;
  1124. end;
  1125. if assigned(or2) then
  1126. begin
  1127. if e2 = or2.frontE then
  1128. or2.frontE := e1 else
  1129. or2.backE := e1;
  1130. end;
  1131. e1.outrec := or2;
  1132. e2.outrec := or1;
  1133. end;
  1134. //------------------------------------------------------------------------------
  1135. procedure Swap(p1, p2: PPointer); overload; {$IFDEF INLINING} inline; {$ENDIF}
  1136. var
  1137. p: Pointer;
  1138. begin
  1139. if p1^ = p2^ then Exit;
  1140. p := p1^;
  1141. p1^ := p2^;
  1142. p2^ := p;
  1143. end;
  1144. //------------------------------------------------------------------------------
  1145. function Area(op: POutPt): Double;
  1146. var
  1147. op2: POutPt;
  1148. d: double;
  1149. begin
  1150. // https://en.wikipedia.org/wiki/Shoelace_formula
  1151. Result := 0;
  1152. if not Assigned(op) then Exit;
  1153. op2 := op;
  1154. repeat
  1155. d := (op2.prev.pt.Y + op2.pt.Y);
  1156. Result := Result + d * (op2.prev.pt.X - op2.pt.X);
  1157. op2 := op2.next;
  1158. until op2 = op;
  1159. Result := Result * 0.5;
  1160. end;
  1161. //------------------------------------------------------------------------------
  1162. function AreaTriangle(const pt1, pt2, pt3: TPoint64): double;
  1163. var
  1164. d1,d2,d3,d4,d5,d6: double;
  1165. begin
  1166. d1 := (pt3.y + pt1.y);
  1167. d2 := (pt3.x - pt1.x);
  1168. d3 := (pt1.y + pt2.y);
  1169. d4 := (pt1.x - pt2.x);
  1170. d5 := (pt2.y + pt3.y);
  1171. d6 := (pt2.x - pt3.x);
  1172. result := d1 * d2 + d3 *d4 + d5 *d6;
  1173. end;
  1174. //------------------------------------------------------------------------------
  1175. function OutrecIsAscending(hotEdge: PActive): Boolean;
  1176. {$IFDEF INLINING} inline; {$ENDIF}
  1177. begin
  1178. Result := (hotEdge = hotEdge.outrec.frontE);
  1179. end;
  1180. //------------------------------------------------------------------------------
  1181. procedure SwapFrontBackSides(outRec: POutRec); {$IFDEF INLINING} inline; {$ENDIF}
  1182. var
  1183. e2: PActive;
  1184. begin
  1185. // while this proc. is needed for open paths
  1186. // it's almost never needed for closed paths
  1187. e2 := outRec.frontE;
  1188. outRec.frontE := outRec.backE;
  1189. outRec.backE := e2;
  1190. outRec.pts := outRec.pts.next;
  1191. end;
  1192. //------------------------------------------------------------------------------
  1193. function EdgesAdjacentInAEL(node: PIntersectNode): Boolean;
  1194. {$IFDEF INLINING} inline; {$ENDIF}
  1195. var
  1196. active1, active2: PActive;
  1197. begin
  1198. active1 := node.active1;
  1199. active2 := node.active2;
  1200. Result := (active1.nextInAEL = active2) or (active1.prevInAEL = active2);
  1201. end;
  1202. //------------------------------------------------------------------------------
  1203. procedure SetOwner(outrec, newOwner: POutRec);
  1204. var
  1205. tmp: POutRec;
  1206. begin
  1207. //precondition1: new_owner is never null
  1208. while Assigned(newOwner.owner) and
  1209. not Assigned(newOwner.owner.pts) do
  1210. newOwner.owner := newOwner.owner.owner;
  1211. //make sure that outrec isn't an owner of newOwner
  1212. tmp := newOwner;
  1213. while Assigned(tmp) and (tmp <> outrec) do
  1214. tmp := tmp.owner;
  1215. if Assigned(tmp) then
  1216. newOwner.owner := outrec.owner;
  1217. outrec.owner := newOwner;
  1218. end;
  1219. //------------------------------------------------------------------------------
  1220. // TReuseableDataContainer64 methods ...
  1221. //------------------------------------------------------------------------------
  1222. constructor TReuseableDataContainer64.Create;
  1223. begin
  1224. FLocMinList := TLocMinList.Create;
  1225. FVertexArrayList := TList.Create;
  1226. end;
  1227. //------------------------------------------------------------------------------
  1228. destructor TReuseableDataContainer64.Destroy;
  1229. begin
  1230. Clear;
  1231. FLocMinList.Free;
  1232. FVertexArrayList.Free;
  1233. inherited;
  1234. end;
  1235. //------------------------------------------------------------------------------
  1236. procedure TReuseableDataContainer64.Clear;
  1237. var
  1238. i: integer;
  1239. begin
  1240. FLocMinList.Clear;
  1241. for i := 0 to FVertexArrayList.Count -1 do
  1242. FreeMem(UnsafeGet(FVertexArrayList, i));
  1243. FVertexArrayList.Clear;
  1244. end;
  1245. //------------------------------------------------------------------------------
  1246. procedure TReuseableDataContainer64.AddPaths(const paths: TPaths64;
  1247. pathType: TPathType; isOpen: Boolean);
  1248. begin
  1249. AddPathsToVertexList(paths, pathType, isOpen,
  1250. FVertexArrayList, FLocMinList);
  1251. end;
  1252. //------------------------------------------------------------------------------
  1253. // TClipperBase methods ...
  1254. //------------------------------------------------------------------------------
  1255. constructor TClipperBase.Create;
  1256. begin
  1257. FLocMinList := TLocMinList.Create(4);
  1258. FOutRecList := TOutRecList.Create(4);
  1259. FIntersectList := TList.Create;
  1260. FVertexArrayList := TList.Create;
  1261. FHorzSegList := THorzSegList.Create;
  1262. FHorzJoinList := THorzJoinList.Create;
  1263. FPreserveCollinear := true;
  1264. FReverseSolution := false;
  1265. end;
  1266. //------------------------------------------------------------------------------
  1267. destructor TClipperBase.Destroy;
  1268. begin
  1269. Clear;
  1270. FLocMinList.Free;
  1271. FOutRecList.Free;
  1272. FIntersectList.Free;
  1273. FHorzSegList.Free;
  1274. FHorzJoinList.Free;
  1275. FVertexArrayList.Free;
  1276. inherited;
  1277. end;
  1278. //------------------------------------------------------------------------------
  1279. function TClipperBase.ClearSolutionOnly: Boolean;
  1280. var
  1281. dummy: Int64;
  1282. begin
  1283. try
  1284. // in case of exceptions ...
  1285. DeleteEdges(FActives);
  1286. while assigned(FScanLine) do PopScanLine(dummy);
  1287. DisposeIntersectNodes;
  1288. DisposeScanLineList;
  1289. FOutRecList.Clear;
  1290. FHorzSegList.Clear;
  1291. FHorzJoinList.Clear;
  1292. Result := true;
  1293. except
  1294. Result := false;
  1295. end;
  1296. end;
  1297. //------------------------------------------------------------------------------
  1298. procedure TClipperBase.Clear;
  1299. begin
  1300. ClearSolutionOnly;
  1301. DisposeVerticesAndLocalMinima;
  1302. FCurrentLocMinIdx := 0;
  1303. FLocMinListSorted := false;
  1304. FHasOpenPaths := False;
  1305. end;
  1306. //------------------------------------------------------------------------------
  1307. procedure TClipperBase.Reset;
  1308. var
  1309. i: Integer;
  1310. begin
  1311. if not FLocMinListSorted then
  1312. begin
  1313. FLocMinList.Sort(LocMinListSort);
  1314. FLocMinListSorted := true;
  1315. end;
  1316. for i := FLocMinList.Count -1 downto 0 do
  1317. InsertScanLine(PLocalMinima(FLocMinList.UnsafeGet(i)).vertex.pt.Y);
  1318. FCurrentLocMinIdx := 0;
  1319. FActives := nil;
  1320. FSel := nil;
  1321. FSucceeded := true;
  1322. end;
  1323. //------------------------------------------------------------------------------
  1324. {$IFDEF USINGZ}
  1325. function XYCoordsEqual(const pt1, pt2: TPoint64): Boolean;
  1326. begin
  1327. Result := (pt1.X = pt2.X) and (pt1.Y = pt2.Y);
  1328. end;
  1329. //------------------------------------------------------------------------------
  1330. procedure TClipperBase.SetZ(e1, e2: PActive; var intersectPt: TPoint64);
  1331. begin
  1332. if not Assigned(fZCallback) then
  1333. begin
  1334. intersectPt.Z := 0;
  1335. Exit;
  1336. end;
  1337. // prioritize subject vertices over clip vertices
  1338. // and pass the subject vertices before clip vertices in the callback
  1339. if (GetPolyType(e1) = ptSubject) then
  1340. begin
  1341. if (XYCoordsEqual(intersectPt, e1.bot)) then intersectPt.Z := e1.bot.Z
  1342. else if (XYCoordsEqual(intersectPt, e1.top)) then intersectPt.Z := e1.top.Z
  1343. else if (XYCoordsEqual(intersectPt, e2.bot)) then intersectPt.Z := e2.bot.Z
  1344. else if (XYCoordsEqual(intersectPt, e2.top)) then intersectPt.Z := e2.top.Z
  1345. else intersectPt.Z := fDefaultZ;
  1346. fZCallback(e1.bot, e1.top, e2.bot, e2.top, intersectPt);
  1347. end else
  1348. begin
  1349. if (XYCoordsEqual(intersectPt, e2.bot)) then intersectPt.Z := e2.bot.Z
  1350. else if (XYCoordsEqual(intersectPt, e2.top)) then intersectPt.Z := e2.top.Z
  1351. else if (XYCoordsEqual(intersectPt, e1.bot)) then intersectPt.Z := e1.bot.Z
  1352. else if (XYCoordsEqual(intersectPt, e1.top)) then intersectPt.Z := e1.top.Z
  1353. else intersectPt.Z := fDefaultZ;
  1354. fZCallback(e2.bot, e2.top, e1.bot, e1.top, intersectPt);
  1355. end;
  1356. end;
  1357. //------------------------------------------------------------------------------
  1358. {$ENDIF}
  1359. procedure TClipperBase.InsertScanLine(const Y: Int64);
  1360. var
  1361. newSl, sl: PScanLine;
  1362. begin
  1363. // The scanline list is a single-linked list of all the Y coordinates of
  1364. // subject and clip vertices in the clipping operation (sorted descending).
  1365. // However, only scanline Y's at Local Minima are inserted before clipping
  1366. // starts. While scanlines are removed sequentially during the sweep operation,
  1367. // new scanlines are only inserted whenever edge bounds are updated. This keeps
  1368. // the scanline list relatively short, optimising performance.
  1369. if not Assigned(FScanLine) then
  1370. begin
  1371. new(newSl);
  1372. newSl.y := Y;
  1373. FScanLine := newSl;
  1374. newSl.next := nil;
  1375. end else if Y > FScanLine.y then
  1376. begin
  1377. new(newSl);
  1378. newSl.y := Y;
  1379. newSl.next := FScanLine;
  1380. FScanLine := newSl;
  1381. end else
  1382. begin
  1383. sl := FScanLine;
  1384. while Assigned(sl.next) and (Y <= sl.next.y) do
  1385. sl := sl.next;
  1386. if Y = sl.y then Exit; // skip duplicates
  1387. new(newSl);
  1388. newSl.y := Y;
  1389. newSl.next := sl.next;
  1390. sl.next := newSl;
  1391. end;
  1392. end;
  1393. //------------------------------------------------------------------------------
  1394. function TClipperBase.PopScanLine(out Y: Int64): Boolean;
  1395. var
  1396. sl: PScanLine;
  1397. begin
  1398. Result := assigned(FScanLine);
  1399. if not Result then Exit;
  1400. Y := FScanLine.y;
  1401. sl := FScanLine;
  1402. FScanLine := FScanLine.next;
  1403. dispose(sl);
  1404. end;
  1405. //------------------------------------------------------------------------------
  1406. function TClipperBase.PopLocalMinima(Y: Int64;
  1407. out localMinima: PLocalMinima): Boolean;
  1408. begin
  1409. Result := false;
  1410. if FCurrentLocMinIdx = FLocMinList.Count then Exit;
  1411. localMinima := PLocalMinima(FLocMinList.UnsafeGet(FCurrentLocMinIdx));
  1412. if (localMinima.vertex.pt.Y = Y) then
  1413. begin
  1414. inc(FCurrentLocMinIdx);
  1415. Result := true;
  1416. end;
  1417. end;
  1418. //------------------------------------------------------------------------------
  1419. procedure TClipperBase.DisposeScanLineList;
  1420. var
  1421. sl: PScanLine;
  1422. begin
  1423. while Assigned(FScanLine) do
  1424. begin
  1425. sl := FScanLine.next;
  1426. Dispose(FScanLine);
  1427. FScanLine := sl;
  1428. end;
  1429. end;
  1430. //------------------------------------------------------------------------------
  1431. procedure TClipperBase.DisposeVerticesAndLocalMinima;
  1432. var
  1433. i: Integer;
  1434. begin
  1435. FLocMinList.Clear;
  1436. for i := 0 to FVertexArrayList.Count -1 do
  1437. FreeMem(UnsafeGet(FVertexArrayList, i));
  1438. FVertexArrayList.Clear;
  1439. end;
  1440. //------------------------------------------------------------------------------
  1441. procedure TClipperBase.AddPath(const path: TPath64;
  1442. pathType: TPathType; isOpen: Boolean);
  1443. var
  1444. pp: TPaths64;
  1445. begin
  1446. SetLength(pp, 1);
  1447. pp[0] := path;
  1448. AddPaths(pp, pathType, isOpen);
  1449. end;
  1450. //------------------------------------------------------------------------------
  1451. procedure TClipperBase.AddPaths(const paths: TPaths64;
  1452. pathType: TPathType; isOpen: Boolean);
  1453. begin
  1454. if isOpen then FHasOpenPaths := true;
  1455. FLocMinListSorted := false;
  1456. AddPathsToVertexList(paths, pathType, isOpen,
  1457. FVertexArrayList, FLocMinList);
  1458. end;
  1459. //------------------------------------------------------------------------------
  1460. procedure TClipperBase.AddReuseableData(const reuseableData: TReuseableDataContainer64);
  1461. var
  1462. i: integer;
  1463. lm: PLocalMinima;
  1464. begin
  1465. if reuseableData.FLocMinList.Count = 0 then Exit;
  1466. // nb: reuseableData will continue to own the vertices
  1467. // and will remain responsible for their clean up.
  1468. // Consequently, it's important that the reuseableData object isn't
  1469. // destroyed before the Clipper object that's using the data.
  1470. FLocMinListSorted := false;
  1471. for i := 0 to reuseableData.FLocMinList.Count -1 do
  1472. with PLocalMinima(reuseableData.FLocMinList[i])^ do
  1473. begin
  1474. lm := self.FLocMinList.Add;
  1475. lm.vertex := vertex;
  1476. lm.polytype := polytype;
  1477. lm.isOpen := isOpen;
  1478. if isOpen then FHasOpenPaths := true;
  1479. end;
  1480. end;
  1481. //------------------------------------------------------------------------------
  1482. function TClipperBase.IsContributingClosed(e: PActive): Boolean;
  1483. begin
  1484. Result := false;
  1485. case FFillRule of
  1486. frNonZero: if abs(e.windCnt) <> 1 then Exit;
  1487. frPositive: if (e.windCnt <> 1) then Exit;
  1488. frNegative: if (e.windCnt <> -1) then Exit;
  1489. end;
  1490. case FClipType of
  1491. ctIntersection:
  1492. case FFillRule of
  1493. frPositive: Result := (e.windCnt2 > 0);
  1494. frNegative: Result := (e.windCnt2 < 0);
  1495. else Result := (e.windCnt2 <> 0);
  1496. end;
  1497. ctUnion:
  1498. case FFillRule of
  1499. frPositive: Result := (e.windCnt2 <= 0);
  1500. frNegative: Result := (e.windCnt2 >= 0);
  1501. else Result := (e.windCnt2 = 0);
  1502. end;
  1503. ctDifference:
  1504. begin
  1505. case FFillRule of
  1506. frPositive: Result := (e.windCnt2 <= 0);
  1507. frNegative: Result := (e.windCnt2 >= 0);
  1508. else Result := (e.windCnt2 = 0);
  1509. end;
  1510. if GetPolyType(e) <> ptSubject then Result := not Result;
  1511. end;
  1512. ctXor:
  1513. Result := true;
  1514. end;
  1515. end;
  1516. //------------------------------------------------------------------------------
  1517. function TClipperBase.IsContributingOpen(e: PActive): Boolean;
  1518. var
  1519. isInSubj, isInClip: Boolean;
  1520. begin
  1521. case FFillRule of
  1522. frPositive:
  1523. begin
  1524. isInSubj := e.windCnt > 0;
  1525. isInClip := e.windCnt2 > 0;
  1526. end;
  1527. frNegative:
  1528. begin
  1529. isInSubj := e.windCnt < 0;
  1530. isInClip := e.windCnt2 < 0;
  1531. end;
  1532. else
  1533. begin
  1534. isInSubj := e.windCnt <> 0;
  1535. isInClip := e.windCnt2 <> 0;
  1536. end;
  1537. end;
  1538. case FClipType of
  1539. ctIntersection: Result := isInClip;
  1540. ctUnion: Result := not isInSubj and not isInClip;
  1541. else Result := not isInClip;
  1542. end;
  1543. end;
  1544. //------------------------------------------------------------------------------
  1545. procedure TClipperBase.SetWindCountForClosedPathEdge(e: PActive);
  1546. var
  1547. e2: PActive;
  1548. begin
  1549. // Wind counts refer to polygon regions not edges, so here an edge's WindCnt
  1550. // indicates the higher of the wind counts for the two regions touching the
  1551. // edge. (nb: Adjacent regions can only ever have their wind counts differ by
  1552. // one. Also, open paths have no meaningful wind directions or counts.)
  1553. e2 := e.prevInAEL;
  1554. // find the nearest closed path edge of the same PolyType in AEL (heading left)
  1555. while Assigned(e2) and (not IsSamePolyType(e2, e) or IsOpen(e2)) do
  1556. e2 := e2.prevInAEL;
  1557. if not Assigned(e2) then
  1558. begin
  1559. e.windCnt := e.windDx;
  1560. e2 := FActives;
  1561. end
  1562. else if (FFillRule = frEvenOdd) then
  1563. begin
  1564. e.windCnt := e.windDx;
  1565. e.windCnt2 := e2.windCnt2;
  1566. e2 := e2.nextInAEL;
  1567. end else
  1568. begin
  1569. // NonZero, positive, or negative filling here ...
  1570. // when e2's WindCnt is in the SAME direction as its WindDx,
  1571. // then polygon will fill on the right of 'e2' (and 'e' will be inside)
  1572. // nb: neither e2.WindCnt nor e2.WindDx should ever be 0.
  1573. if (e2.windCnt * e2.windDx < 0) then
  1574. begin
  1575. // opposite directions so 'e' is outside 'e2' ...
  1576. if (Abs(e2.windCnt) > 1) then
  1577. begin
  1578. // outside prev poly but still inside another.
  1579. e.windCnt := Iif(e2.windDx * e.windDx < 0,
  1580. e2.windCnt, // reversing direction so use the same WC
  1581. e2.windCnt + e.windDx);
  1582. end
  1583. // now outside all polys of same polytype so set own WC ...
  1584. else e.windCnt := e.windDx;
  1585. end else
  1586. begin
  1587. //'e' must be inside 'e2'
  1588. e.windCnt := Iif(e2.windDx * e.windDx < 0,
  1589. e2.windCnt, // reversing direction so use the same WC
  1590. e2.windCnt + e.windDx); // else keep 'increasing' the WC
  1591. end;
  1592. e.windCnt2 := e2.windCnt2;
  1593. e2 := e2.nextInAEL;
  1594. end;
  1595. // update WindCnt2 ...
  1596. if FFillRule = frEvenOdd then
  1597. while (e2 <> e) do
  1598. begin
  1599. if IsSamePolyType(e2, e) or IsOpen(e2) then // do nothing
  1600. else if e.windCnt2 = 0 then e.windCnt2 := 1
  1601. else e.windCnt2 := 0;
  1602. e2 := e2.nextInAEL;
  1603. end
  1604. else
  1605. while (e2 <> e) do
  1606. begin
  1607. if not IsSamePolyType(e2, e) and not IsOpen(e2) then
  1608. Inc(e.windCnt2, e2.windDx);
  1609. e2 := e2.nextInAEL;
  1610. end;
  1611. end;
  1612. //------------------------------------------------------------------------------
  1613. procedure TClipperBase.SetWindCountForOpenPathEdge(e: PActive);
  1614. var
  1615. e2: PActive;
  1616. cnt1, cnt2: Integer;
  1617. begin
  1618. e2 := FActives;
  1619. if FFillRule = frEvenOdd then
  1620. begin
  1621. cnt1 := 0;
  1622. cnt2 := 0;
  1623. while (e2 <> e) do
  1624. begin
  1625. if (GetPolyType(e2) = ptClip) then inc(cnt2)
  1626. else if not IsOpen(e2) then inc(cnt1);
  1627. e2 := e2.nextInAEL;
  1628. end;
  1629. e.windCnt := Iif(Odd(cnt1), 1, 0);
  1630. e.windCnt2 := Iif(Odd(cnt2), 1, 0);
  1631. end else
  1632. begin
  1633. // if FClipType in [ctUnion, ctDifference] then e.WindCnt := e.WindDx;
  1634. while (e2 <> e) do
  1635. begin
  1636. if (GetPolyType(e2) = ptClip) then inc(e.windCnt2, e2.windDx)
  1637. else if not IsOpen(e2) then inc(e.windCnt, e2.windDx);
  1638. e2 := e2.nextInAEL;
  1639. end;
  1640. end;
  1641. end;
  1642. //------------------------------------------------------------------------------
  1643. function IsValidAelOrder(resident, newcomer: PActive): Boolean;
  1644. var
  1645. botY: Int64;
  1646. newcomerIsLeft: Boolean;
  1647. d: double;
  1648. begin
  1649. if (newcomer.currX <> resident.currX) then
  1650. begin
  1651. Result := newcomer.currX > resident.currX;
  1652. Exit;
  1653. end;
  1654. // get the turning direction a1.top, a2.bot, a2.top
  1655. d := CrossProduct(resident.top, newcomer.bot, newcomer.top);
  1656. if d <> 0 then
  1657. begin
  1658. Result := d < 0;
  1659. Exit;
  1660. end;
  1661. // edges must be collinear to get here
  1662. if not IsMaxima(resident) and
  1663. (resident.top.Y > newcomer.top.Y) then
  1664. begin
  1665. Result := CrossProduct(newcomer.bot,
  1666. resident.top, NextVertex(resident).pt) <= 0;
  1667. Exit;
  1668. end
  1669. else if not IsMaxima(newcomer) and
  1670. (newcomer.top.Y > resident.top.Y) then
  1671. begin
  1672. Result := CrossProduct(newcomer.bot,
  1673. newcomer.top, NextVertex(newcomer).pt) >= 0;
  1674. Exit;
  1675. end;
  1676. botY := newcomer.bot.Y;
  1677. newcomerIsLeft := IsLeftBound(newcomer);
  1678. if (resident.bot.Y <> botY) or
  1679. (resident.locMin.vertex.pt.Y <> botY) then
  1680. Result := newcomerIsLeft
  1681. // resident must also have just been inserted
  1682. else if IsLeftBound(resident) <> newcomerIsLeft then
  1683. Result := newcomerIsLeft
  1684. else if IsCollinear(PrevPrevVertex(resident).pt,
  1685. resident.bot, resident.top) then
  1686. Result := true
  1687. else
  1688. // otherwise compare turning direction of the alternate bound
  1689. Result := (CrossProduct(PrevPrevVertex(resident).pt,
  1690. newcomer.bot, PrevPrevVertex(newcomer).pt) > 0) = newcomerIsLeft;
  1691. end;
  1692. //------------------------------------------------------------------------------
  1693. procedure TClipperBase.InsertLeftEdge(e: PActive);
  1694. var
  1695. e2: PActive;
  1696. begin
  1697. if not Assigned(FActives) then
  1698. begin
  1699. e.prevInAEL := nil;
  1700. e.nextInAEL := nil;
  1701. FActives := e;
  1702. end
  1703. else if not IsValidAelOrder(FActives, e) then
  1704. begin
  1705. e.prevInAEL := nil;
  1706. e.nextInAEL := FActives;
  1707. FActives.prevInAEL := e;
  1708. FActives := e;
  1709. end else
  1710. begin
  1711. e2 := FActives;
  1712. while Assigned(e2.nextInAEL) and IsValidAelOrder(e2.nextInAEL, e) do
  1713. e2 := e2.nextInAEL;
  1714. //don't separate joined edges
  1715. if e2.joinedWith = jwRight then e2 := e2.nextInAEL;
  1716. e.nextInAEL := e2.nextInAEL;
  1717. if Assigned(e2.nextInAEL) then e2.nextInAEL.prevInAEL := e;
  1718. e.prevInAEL := e2;
  1719. e2.nextInAEL := e;
  1720. end;
  1721. end;
  1722. //----------------------------------------------------------------------
  1723. procedure InsertRightEdge(e, e2: PActive);
  1724. begin
  1725. e2.nextInAEL := e.nextInAEL;
  1726. if Assigned(e.nextInAEL) then e.nextInAEL.prevInAEL := e2;
  1727. e2.prevInAEL := e;
  1728. e.nextInAEL := e2;
  1729. end;
  1730. //----------------------------------------------------------------------
  1731. procedure TClipperBase.InsertLocalMinimaIntoAEL(const botY: Int64);
  1732. var
  1733. leftB, rightB, rbn: PActive;
  1734. locMin: PLocalMinima;
  1735. contributing: Boolean;
  1736. begin
  1737. // Add local minima (if any) at BotY ...
  1738. // nb: horizontal local minima edges should contain locMin.Vertex.prev
  1739. while PopLocalMinima(botY, locMin) do
  1740. begin
  1741. if (vfOpenStart in locMin.vertex.flags) then
  1742. begin
  1743. leftB := nil;
  1744. end else
  1745. begin
  1746. new(leftB);
  1747. FillChar(leftB^, sizeof(TActive), 0);
  1748. leftB.locMin := locMin;
  1749. leftB.outrec := nil;
  1750. leftB.joinedWith := jwNone;
  1751. leftB.bot := locMin.vertex.pt;
  1752. leftB.windDx := -1;
  1753. leftB.vertTop := locMin.vertex.prev;
  1754. leftB.top := leftB.vertTop.pt;
  1755. leftB.currX := leftB.bot.X;
  1756. SetDx(leftB);
  1757. end;
  1758. if (vfOpenEnd in locMin.vertex.flags) then
  1759. begin
  1760. rightB := nil;
  1761. end else
  1762. begin
  1763. new(rightB);
  1764. FillChar(rightB^, sizeof(TActive), 0);
  1765. rightB.locMin := locMin;
  1766. rightB.outrec := nil;
  1767. rightB.joinedWith := jwNone;
  1768. rightB.bot := locMin.vertex.pt;
  1769. rightB.windDx := 1;
  1770. rightB.vertTop := locMin.vertex.next;
  1771. rightB.top := rightB.vertTop.pt;
  1772. rightB.currX := rightB.bot.X;
  1773. SetDx(rightB);
  1774. end;
  1775. // Currently LeftB is just descending and RightB is ascending,
  1776. // so now we swap them if LeftB isn't actually on the left.
  1777. if assigned(leftB) and assigned(rightB) then
  1778. begin
  1779. if IsHorizontal(leftB) then
  1780. begin
  1781. if IsHeadingRightHorz(leftB) then SwapActives(leftB, rightB);
  1782. end
  1783. else if IsHorizontal(rightB) then
  1784. begin
  1785. if IsHeadingLeftHorz(rightB) then SwapActives(leftB, rightB);
  1786. end
  1787. else if (leftB.dx < rightB.dx) then SwapActives(leftB, rightB);
  1788. //so when leftB has windDx == 1, the polygon will be oriented
  1789. //counter-clockwise in Cartesian coords (clockwise with inverted Y).
  1790. end
  1791. else if not assigned(leftB) then
  1792. begin
  1793. leftB := rightB;
  1794. rightB := nil;
  1795. end;
  1796. LeftB.isLeftB := true; // nb: we can't use winddx instead
  1797. InsertLeftEdge(leftB); ////////////////
  1798. if IsOpen(leftB) then
  1799. begin
  1800. SetWindCountForOpenPathEdge(leftB);
  1801. contributing := IsContributingOpen(leftB);
  1802. end else
  1803. begin
  1804. SetWindCountForClosedPathEdge(leftB);
  1805. contributing := IsContributingClosed(leftB);
  1806. end;
  1807. if assigned(rightB) then
  1808. begin
  1809. rightB.windCnt := leftB.windCnt;
  1810. rightB.windCnt2 := leftB.windCnt2;
  1811. InsertRightEdge(leftB, rightB); ////////////////
  1812. if contributing then
  1813. begin
  1814. AddLocalMinPoly(leftB, rightB, leftB.bot, true);
  1815. if not IsHorizontal(leftB) then
  1816. CheckJoinLeft(leftB, leftB.bot);
  1817. end;
  1818. while Assigned(rightB.nextInAEL) and
  1819. IsValidAelOrder(rightB.nextInAEL, rightB) do
  1820. begin
  1821. rbn := rightB.nextInAEL;
  1822. IntersectEdges(rightB, rbn, rightB.bot);
  1823. SwapPositionsInAEL(rightB, rightB.nextInAEL);
  1824. end;
  1825. if IsHorizontal(rightB) then
  1826. PushHorz(rightB)
  1827. else
  1828. begin
  1829. if IsHotEdge(rightB) then
  1830. CheckJoinRight(rightB, rightB.bot);
  1831. InsertScanLine(rightB.top.Y);
  1832. end;
  1833. end
  1834. else if contributing then
  1835. StartOpenPath(leftB, leftB.bot);
  1836. if IsHorizontal(leftB) then
  1837. PushHorz(leftB) else
  1838. InsertScanLine(leftB.top.Y);
  1839. end;
  1840. end;
  1841. //------------------------------------------------------------------------------
  1842. procedure TClipperBase.PushHorz(e: PActive);
  1843. begin
  1844. if assigned(FSel) then
  1845. e.nextInSEL := FSel else
  1846. e.nextInSEL := nil;
  1847. FSel := e;
  1848. end;
  1849. //------------------------------------------------------------------------------
  1850. function TClipperBase.PopHorz(out e: PActive): Boolean;
  1851. begin
  1852. Result := assigned(FSel);
  1853. if not Result then Exit;
  1854. e := FSel;
  1855. FSel := FSel.nextInSEL;
  1856. end;
  1857. //------------------------------------------------------------------------------
  1858. function TClipperBase.AddLocalMinPoly(e1, e2: PActive;
  1859. const pt: TPoint64; IsNew: Boolean = false): POutPt;
  1860. var
  1861. newOr: POutRec;
  1862. prevHotEdge: PActive;
  1863. begin
  1864. newOr := FOutRecList.Add;
  1865. newOr.owner := nil;
  1866. e1.outrec := newOr;
  1867. e2.outrec := newOr;
  1868. if IsOpen(e1) then
  1869. begin
  1870. newOr.isOpen := true;
  1871. if e1.windDx > 0 then
  1872. SetSides(newOr, e1, e2) else
  1873. SetSides(newOr, e2, e1);
  1874. end else
  1875. begin
  1876. prevHotEdge := GetPrevHotEdge(e1);
  1877. newOr.isOpen := false;
  1878. // e.windDx is the winding direction of the **input** paths
  1879. // and unrelated to the winding direction of output polygons.
  1880. // Output orientation is determined by e.outrec.frontE which is
  1881. // the ascending edge (see AddLocalMinPoly).
  1882. if Assigned(prevHotEdge) then
  1883. begin
  1884. if FUsingPolytree then
  1885. SetOwner(newOr, prevHotEdge.outrec);
  1886. if OutrecIsAscending(prevHotEdge) = isNew then
  1887. SetSides(newOr, e2, e1) else
  1888. SetSides(newOr, e1, e2);
  1889. end else
  1890. begin
  1891. if isNew then
  1892. SetSides(newOr, e1, e2) else
  1893. SetSides(newOr, e2, e1);
  1894. end;
  1895. end;
  1896. Result := NewOutPt(pt, newOr);
  1897. newOr.pts := Result;
  1898. end;
  1899. //------------------------------------------------------------------------------
  1900. procedure DisposeOutPts(outrec: POutRec); {$IFDEF INLINING} inline; {$ENDIF}
  1901. var
  1902. op, tmpOp: POutPt;
  1903. begin
  1904. op := outrec.pts;
  1905. op.prev.next := nil;
  1906. while Assigned(op) do
  1907. begin
  1908. tmpOp := op;
  1909. op := op.next;
  1910. Dispose(tmpOp);
  1911. end;
  1912. outrec.pts := nil;
  1913. end;
  1914. //------------------------------------------------------------------------------
  1915. procedure TClipperBase.CleanCollinear(outRec: POutRec);
  1916. var
  1917. op2, startOp: POutPt;
  1918. begin
  1919. outRec := GetRealOutRec(outRec);
  1920. if not Assigned(outRec) or outRec.isOpen then Exit;
  1921. if not IsValidClosedPath(outRec.pts) then
  1922. begin
  1923. DisposeOutPts(outRec);
  1924. Exit;
  1925. end;
  1926. startOp := outRec.pts;
  1927. op2 := startOp;
  1928. while true do
  1929. begin
  1930. // trim if collinear AND one of
  1931. // a duplicate point OR
  1932. // not preserving collinear points OR
  1933. // is a 180 degree 'spike'
  1934. if IsCollinear(op2.prev.pt, op2.pt, op2.next.pt) and
  1935. (PointsEqual(op2.pt,op2.prev.pt) or
  1936. PointsEqual(op2.pt,op2.next.pt) or
  1937. not FPreserveCollinear or
  1938. (DotProduct(op2.prev.pt, op2.pt, op2.next.pt) < 0)) then
  1939. begin
  1940. if op2 = outRec.pts then outRec.pts := op2.prev;
  1941. op2 := DisposeOutPt(op2);
  1942. if not IsValidClosedPath(op2) then
  1943. begin
  1944. DisposeOutPts(outRec);
  1945. Exit;
  1946. end;
  1947. startOp := op2;
  1948. Continue;
  1949. end;
  1950. op2 := op2.next;
  1951. if op2 = startOp then Break;
  1952. end;
  1953. FixSelfIntersects(outRec);
  1954. end;
  1955. //------------------------------------------------------------------------------
  1956. procedure AddSplit(oldOr, newOr: POutRec);
  1957. var
  1958. i: integer;
  1959. begin
  1960. i := Length(oldOr.splits);
  1961. SetLength(oldOr.splits, i +1);
  1962. oldOr.splits[i] := newOr;
  1963. end;
  1964. //------------------------------------------------------------------------------
  1965. procedure TClipperBase.DoSplitOp(outrec: POutRec; splitOp: POutPt);
  1966. var
  1967. newOp, newOp2, prevOp, nextNextOp: POutPt;
  1968. ip: TPoint64;
  1969. area1, area2, absArea1, absArea2: double;
  1970. newOutRec: POutRec;
  1971. begin
  1972. // splitOp.prev <=> splitOp &&
  1973. // splitOp.next <=> splitOp.next.next are intersecting
  1974. prevOp := splitOp.prev;
  1975. nextNextOp := splitOp.next.next;
  1976. outrec.pts := prevOp;
  1977. GetSegmentIntersectPt(
  1978. prevOp.pt, splitOp.pt, splitOp.next.pt, nextNextOp.pt, ip);
  1979. {$IFDEF USINGZ}
  1980. if Assigned(fZCallback) then
  1981. fZCallback(prevOp.Pt, splitOp.Pt, splitOp.Next.Pt, nextNextOp.Pt, ip);
  1982. {$ENDIF}
  1983. area1 := Area(outrec.pts);
  1984. absArea1 := abs(area1);
  1985. if absArea1 < 2 then
  1986. begin
  1987. DisposeOutPts(outrec);
  1988. Exit;
  1989. end;
  1990. area2 := AreaTriangle(ip, splitOp.pt, splitOp.next.pt);
  1991. absArea2 := abs(area2);
  1992. // de-link splitOp and splitOp.next from the path
  1993. // while inserting the intersection point
  1994. if PointsEqual(ip, prevOp.pt) or
  1995. PointsEqual(ip, nextNextOp.pt) then
  1996. begin
  1997. nextNextOp.prev := prevOp;
  1998. prevOp.next := nextNextOp;
  1999. end else
  2000. begin
  2001. newOp2 := NewOutPt(ip, outrec, prevOp, nextNextOp);
  2002. nextNextOp.prev := newOp2;
  2003. prevOp.next := newOp2;
  2004. end;
  2005. // nb: area1 is the path's area *before* splitting, whereas area2 is
  2006. // the area of the triangle containing splitOp & splitOp.next.
  2007. // So the only way for these areas to have the same sign is if
  2008. // the split triangle is larger than the path containing prevOp or
  2009. // if there's more than one self-intersection.
  2010. if (absArea2 > 1) and
  2011. ((absArea2 > absArea1) or
  2012. ((area2 > 0) = (area1 > 0))) then
  2013. begin
  2014. newOutRec := FOutRecList.Add;
  2015. newOutRec.owner := outrec.owner;
  2016. splitOp.outrec := newOutRec;
  2017. splitOp.next.outrec := newOutRec;
  2018. newOp := NewOutPt(ip, newOutRec, splitOp.next, splitOp);
  2019. splitOp.prev := newOp;
  2020. splitOp.next.next := newOp;
  2021. newOutRec.pts := newOp;
  2022. if FUsingPolytree then
  2023. begin
  2024. if (Path1InsidePath2(prevOp, newOp)) then
  2025. AddSplit(newOutRec, outrec) else
  2026. AddSplit(outrec, newOutRec);
  2027. end;
  2028. end else
  2029. begin
  2030. Dispose(splitOp.next);
  2031. Dispose(splitOp);
  2032. end;
  2033. end;
  2034. //------------------------------------------------------------------------------
  2035. procedure TClipperBase.FixSelfIntersects(outrec: POutRec);
  2036. var
  2037. op2: POutPt;
  2038. begin
  2039. op2 := outrec.pts;
  2040. while true do
  2041. begin
  2042. // triangles can't self-intersect
  2043. if (op2.prev = op2.next.next) then
  2044. Break
  2045. else if SegmentsIntersect(op2.prev.pt, op2.pt,
  2046. op2.next.pt, op2.next.next.pt) then
  2047. begin
  2048. DoSplitOp(outrec, op2);
  2049. if not assigned(outrec.pts) then Break;
  2050. op2 := outrec.pts;
  2051. Continue;
  2052. end else
  2053. op2 := op2.next;
  2054. if (op2 = outrec.pts) then Break;
  2055. end;
  2056. end;
  2057. //------------------------------------------------------------------------------
  2058. function TClipperBase.AddLocalMaxPoly(e1, e2: PActive; const pt: TPoint64): POutPt;
  2059. var
  2060. e: PActive;
  2061. outRec: POutRec;
  2062. begin
  2063. if IsJoined(e1) then UndoJoin(e1, pt);
  2064. if IsJoined(e2) then UndoJoin(e2, pt);
  2065. if (IsFront(e1) = IsFront(e2)) then
  2066. begin
  2067. if IsOpenEnd(e1.vertTop) then
  2068. SwapFrontBackSides(e1.outrec)
  2069. else if IsOpenEnd(e2.vertTop) then
  2070. SwapFrontBackSides(e2.outrec)
  2071. else
  2072. begin
  2073. FSucceeded := false;
  2074. Result := nil;
  2075. Exit;
  2076. end;
  2077. end;
  2078. Result := AddOutPt(e1, pt);
  2079. if (e1.outrec = e2.outrec) then
  2080. begin
  2081. outRec := e1.outrec;
  2082. outRec.pts := Result;
  2083. if FUsingPolytree then
  2084. begin
  2085. e := GetPrevHotEdge(e1);
  2086. if not Assigned(e) then
  2087. outRec.owner := nil else
  2088. SetOwner(outRec, e.outrec);
  2089. // nb: outRec.owner here is likely NOT the real
  2090. // owner but this will be checked in DeepCheckOwner()
  2091. end;
  2092. UncoupleOutRec(e1);
  2093. end
  2094. else if IsOpen(e1) then
  2095. begin
  2096. // preserve the winding orientation of Outrec
  2097. if e1.windDx < 0 then
  2098. JoinOutrecPaths(e1, e2) else
  2099. JoinOutrecPaths(e2, e1);
  2100. end
  2101. else if e1.outrec.idx < e2.outrec.idx then
  2102. JoinOutrecPaths(e1, e2)
  2103. else
  2104. JoinOutrecPaths(e2, e1);
  2105. end;
  2106. //------------------------------------------------------------------------------
  2107. procedure TClipperBase.JoinOutrecPaths(e1, e2: PActive);
  2108. var
  2109. p1_start, p1_end, p2_start, p2_end: POutPt;
  2110. begin
  2111. // join e2 outrec path onto e1 outrec path and then delete e2 outrec path
  2112. // pointers. (see joining_outpt.svg)
  2113. p1_start := e1.outrec.pts;
  2114. p2_start := e2.outrec.pts;
  2115. p1_end := p1_start.next;
  2116. p2_end := p2_start.next;
  2117. if IsFront(e1) then
  2118. begin
  2119. p2_end.prev := p1_start;
  2120. p1_start.next := p2_end;
  2121. p2_start.next := p1_end;
  2122. p1_end.prev := p2_start;
  2123. e1.outrec.pts := p2_start;
  2124. // nb: if IsOpen(e1) then e1 & e2 must be a 'maximaPair'
  2125. e1.outrec.frontE := e2.outrec.frontE;
  2126. if Assigned(e1.outrec.frontE) then
  2127. e1.outrec.frontE.outrec := e1.outrec;
  2128. end else
  2129. begin
  2130. p1_end.prev := p2_start;
  2131. p2_start.next := p1_end;
  2132. p1_start.next := p2_end;
  2133. p2_end.prev := p1_start;
  2134. e1.outrec.backE := e2.outrec.backE;
  2135. if Assigned(e1.outrec.backE) then
  2136. e1.outrec.backE.outrec := e1.outrec;
  2137. end;
  2138. // after joining, the e2.OutRec mustn't contains vertices
  2139. e2.outrec.frontE := nil;
  2140. e2.outrec.backE := nil;
  2141. e2.outrec.pts := nil;
  2142. if IsOpenEnd(e1.vertTop) then
  2143. begin
  2144. e2.outrec.pts := e1.outrec.pts;
  2145. e1.outrec.pts := nil;
  2146. end
  2147. else
  2148. SetOwner(e2.outrec, e1.outrec);
  2149. // and e1 and e2 are maxima and are about to be dropped from the Actives list.
  2150. e1.outrec := nil;
  2151. e2.outrec := nil;
  2152. end;
  2153. //------------------------------------------------------------------------------
  2154. procedure TClipperBase.UndoJoin(e: PActive; const currPt: TPoint64);
  2155. begin
  2156. if e.joinedWith = jwRight then
  2157. begin
  2158. e.nextInAEL.joinedWith := jwNone;
  2159. e.joinedWith := jwNone;
  2160. AddLocalMinPoly(e, e.nextInAEL, currPt, true);
  2161. end else
  2162. begin
  2163. e.prevInAEL.joinedWith := jwNone;
  2164. e.joinedWith := jwNone;
  2165. AddLocalMinPoly(e.prevInAEL, e, currPt, true);
  2166. end;
  2167. end;
  2168. //------------------------------------------------------------------------------
  2169. procedure TClipperBase.CheckJoinLeft(e: PActive;
  2170. const pt: TPoint64; checkCurrX: Boolean);
  2171. var
  2172. prev: PActive;
  2173. begin
  2174. prev := e.prevInAEL;
  2175. if not Assigned(prev) or
  2176. not IsHotEdge(e) or not IsHotEdge(prev) or
  2177. IsHorizontal(e) or IsHorizontal(prev) or
  2178. IsOpen(e) or IsOpen(prev) then Exit;
  2179. if ((pt.Y < e.top.Y +2) or (pt.Y < prev.top.Y +2)) and
  2180. ((e.bot.Y > pt.Y) or (prev.bot.Y > pt.Y)) then Exit; // (#490)
  2181. if checkCurrX then
  2182. begin
  2183. if PerpendicDistFromLineSqrd(pt, prev.bot, prev.top) > 0.25 then Exit
  2184. end else if (e.currX <> prev.currX) then Exit;
  2185. if not IsCollinear(e.top, pt, prev.top) then Exit;
  2186. if (e.outrec.idx = prev.outrec.idx) then
  2187. AddLocalMaxPoly(prev, e, pt)
  2188. else if e.outrec.idx < prev.outrec.idx then
  2189. JoinOutrecPaths(e, prev)
  2190. else
  2191. JoinOutrecPaths(prev, e);
  2192. prev.joinedWith := jwRight;
  2193. e.joinedWith := jwLeft;
  2194. end;
  2195. //------------------------------------------------------------------------------
  2196. procedure TClipperBase.CheckJoinRight(e: PActive;
  2197. const pt: TPoint64; checkCurrX: Boolean);
  2198. var
  2199. next: PActive;
  2200. begin
  2201. next := e.nextInAEL;
  2202. if not Assigned(next) or
  2203. not IsHotEdge(e) or not IsHotEdge(next) or
  2204. IsHorizontal(e) or IsHorizontal(next) or
  2205. IsOpen(e) or IsOpen(next) then Exit;
  2206. if ((pt.Y < e.top.Y +2) or (pt.Y < next.top.Y +2)) and
  2207. ((e.bot.Y > pt.Y) or (next.bot.Y > pt.Y)) then Exit; // (#490)
  2208. if (checkCurrX) then
  2209. begin
  2210. if PerpendicDistFromLineSqrd(pt, next.bot, next.top) > 0.25 then Exit
  2211. end
  2212. else if (e.currX <> next.currX) then Exit;
  2213. if not IsCollinear(e.top, pt, next.top) then Exit;
  2214. if e.outrec.idx = next.outrec.idx then
  2215. AddLocalMaxPoly(e, next, pt)
  2216. else if e.outrec.idx < next.outrec.idx then
  2217. JoinOutrecPaths(e, next)
  2218. else
  2219. JoinOutrecPaths(next, e);
  2220. e.joinedWith := jwRight;
  2221. next.joinedWith := jwLeft;
  2222. end;
  2223. //------------------------------------------------------------------------------
  2224. function TClipperBase.AddOutPt(e: PActive; const pt: TPoint64): POutPt;
  2225. var
  2226. opFront, opBack: POutPt;
  2227. toFront: Boolean;
  2228. outrec: POutRec;
  2229. begin
  2230. // Outrec.OutPts: a circular doubly-linked-list of POutPt where ...
  2231. // opFront[.Prev]* ~~~> opBack & opBack == opFront.Next
  2232. outrec := e.outrec;
  2233. toFront := IsFront(e);
  2234. opFront := outrec.pts;
  2235. opBack := opFront.next;
  2236. if toFront and PointsEqual(pt, opFront.pt) then
  2237. begin
  2238. result := opFront;
  2239. end
  2240. else if not toFront and PointsEqual(pt, opBack.pt) then
  2241. begin
  2242. result := opBack;
  2243. end else
  2244. begin
  2245. Result := NewOutPt(pt, outrec, opFront, opBack);
  2246. opBack.prev := Result;
  2247. opFront.next := Result;
  2248. if toFront then outrec.pts := Result;
  2249. end;
  2250. end;
  2251. //------------------------------------------------------------------------------
  2252. function TClipperBase.StartOpenPath(e: PActive; const pt: TPoint64): POutPt;
  2253. var
  2254. newOr: POutRec;
  2255. begin
  2256. newOr := FOutRecList.Add;
  2257. newOr.isOpen := true;
  2258. if e.windDx > 0 then
  2259. begin
  2260. newOr.frontE := e;
  2261. newOr.backE := nil;
  2262. end else
  2263. begin
  2264. newOr.frontE := nil;
  2265. newOr.backE := e;
  2266. end;
  2267. e.outrec := newOr;
  2268. Result := NewOutPt(pt, newOr);
  2269. newOr.pts := Result;
  2270. end;
  2271. //------------------------------------------------------------------------------
  2272. procedure TrimHorz(horzEdge: PActive; preserveCollinear: Boolean);
  2273. var
  2274. pt: TPoint64;
  2275. wasTrimmed: Boolean;
  2276. begin
  2277. wasTrimmed := false;
  2278. pt := NextVertex(horzEdge).pt;
  2279. while (pt.Y = horzEdge.top.Y) do
  2280. begin
  2281. // always trim 180 deg. spikes (in closed paths)
  2282. // but otherwise break if preserveCollinear = true
  2283. if preserveCollinear and
  2284. ((pt.X < horzEdge.top.X) <> (horzEdge.bot.X < horzEdge.top.X)) then
  2285. break;
  2286. horzEdge.vertTop := NextVertex(horzEdge);
  2287. horzEdge.top := pt;
  2288. wasTrimmed := true;
  2289. if IsMaxima(horzEdge) then Break;
  2290. pt := NextVertex(horzEdge).pt;
  2291. end;
  2292. if wasTrimmed then SetDx(horzEdge); // +/-infinity
  2293. end;
  2294. //------------------------------------------------------------------------------
  2295. procedure TClipperBase.UpdateEdgeIntoAEL(var e: PActive);
  2296. begin
  2297. e.bot := e.top;
  2298. e.vertTop := NextVertex(e);
  2299. e.top := e.vertTop.pt;
  2300. e.currX := e.bot.X;
  2301. SetDx(e);
  2302. if IsJoined(e) then UndoJoin(e, e.bot);
  2303. if IsHorizontal(e) then
  2304. begin
  2305. if not IsOpen(e) then TrimHorz(e, PreserveCollinear);
  2306. Exit;
  2307. end;
  2308. InsertScanLine(e.top.Y);
  2309. CheckJoinLeft(e, e.bot);
  2310. CheckJoinRight(e, e.bot, true); // (#500)
  2311. end;
  2312. //------------------------------------------------------------------------------
  2313. function FindEdgeWithMatchingLocMin(e: PActive): PActive;
  2314. begin
  2315. Result := e.nextInAEL;
  2316. while Assigned(Result) do
  2317. begin
  2318. if (Result.locMin = e.locMin) then Exit;
  2319. if not IsHorizontal(Result) and
  2320. not PointsEqual(e.bot, Result.bot) then Result := nil
  2321. else Result := Result.nextInAEL;
  2322. end;
  2323. Result := e.prevInAEL;
  2324. while Assigned(Result) do
  2325. begin
  2326. if (Result.locMin = e.locMin) then Exit;
  2327. if not IsHorizontal(Result) and
  2328. not PointsEqual(e.bot, Result.bot) then Result := nil
  2329. else
  2330. Result := Result.prevInAEL;
  2331. end;
  2332. end;
  2333. //------------------------------------------------------------------------------
  2334. {$IFNDEF USINGZ}
  2335. {$HINTS OFF}
  2336. {$ENDIF}
  2337. procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64);
  2338. var
  2339. e1WindCnt, e2WindCnt, e1WindCnt2, e2WindCnt2: Integer;
  2340. e3: PActive;
  2341. op, op2: POutPt;
  2342. begin
  2343. // MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
  2344. if FHasOpenPaths and (IsOpen(e1) or IsOpen(e2)) then
  2345. begin
  2346. if IsOpen(e1) and IsOpen(e2) then Exit;
  2347. // the following line avoids duplicating quite a bit of code
  2348. if IsOpen(e2) then SwapActives(e1, e2);
  2349. // e1 is open and e2 is closed
  2350. if IsJoined(e2) then UndoJoin(e2, pt); // needed for safety
  2351. case FClipType of
  2352. ctUnion: if not IsHotEdge(e2) then Exit;
  2353. else if e2.locMin.polytype = ptSubject then Exit;
  2354. end;
  2355. case FFillRule of
  2356. frPositive: if e2.windCnt <> 1 then Exit;
  2357. frNegative: if e2.windCnt <> -1 then Exit;
  2358. else if (abs(e2.windCnt) <> 1) then Exit;
  2359. end;
  2360. // toggle contribution ...
  2361. if IsHotEdge(e1) then
  2362. begin
  2363. op := AddOutPt(e1, pt);
  2364. if IsFront(e1) then
  2365. e1.outrec.frontE := nil else
  2366. e1.outrec.backE := nil;
  2367. e1.outrec := nil;
  2368. // e1 is no longer 'hot'
  2369. end
  2370. // horizontal edges can pass under open paths at a LocMins
  2371. else if PointsEqual(pt, e1.locMin.vertex.pt) and
  2372. (e1.locMin.vertex.flags * [vfOpenStart, vfOpenEnd] = []) then
  2373. begin
  2374. //todo: recheck if this code block is still needed
  2375. // find the other side of the LocMin and
  2376. // if it's 'hot' join up with it ...
  2377. e3 := FindEdgeWithMatchingLocMin(e1);
  2378. if assigned(e3) and IsHotEdge(e3) then
  2379. begin
  2380. e1.outrec := e3.outrec;
  2381. if e1.windDx > 0 then
  2382. SetSides(e3.outrec, e1, e3) else
  2383. SetSides(e3.outrec, e3, e1);
  2384. Exit;
  2385. end else
  2386. op := StartOpenPath(e1, pt);
  2387. end else
  2388. op := StartOpenPath(e1, pt);
  2389. {$IFDEF USINGZ}
  2390. SetZ(e1, e2, op.pt);
  2391. {$ENDIF}
  2392. Exit;
  2393. end;
  2394. // MANAGING CLOSED PATHS FROM HERE ON
  2395. if IsJoined(e1) then UndoJoin(e1, pt);
  2396. if IsJoined(e2) then UndoJoin(e2, pt);
  2397. // FIRST, UPDATE WINDING COUNTS
  2398. if IsSamePolyType(e1, e2) then
  2399. begin
  2400. if FFillRule = frEvenOdd then
  2401. begin
  2402. e1WindCnt := e1.windCnt;
  2403. e1.windCnt := e2.windCnt;
  2404. e2.windCnt := e1WindCnt;
  2405. end else
  2406. begin
  2407. e1.windCnt := Iif(e1.windCnt + e2.windDx = 0,
  2408. -e1.windCnt, e1.windCnt + e2.windDx);
  2409. e2.windCnt := Iif(e2.windCnt - e1.windDx = 0,
  2410. -e2.windCnt, e2.windCnt - e1.windDx);
  2411. end;
  2412. end else
  2413. begin
  2414. if FFillRule <> frEvenOdd then Inc(e1.windCnt2, e2.windDx)
  2415. else if e1.windCnt2 = 0 then e1.windCnt2 := 1
  2416. else e1.windCnt2 := 0;
  2417. if FFillRule <> frEvenOdd then Dec(e2.windCnt2, e1.windDx)
  2418. else if e2.windCnt2 = 0 then e2.windCnt2 := 1
  2419. else e2.windCnt2 := 0;
  2420. end;
  2421. case FFillRule of
  2422. frPositive:
  2423. begin
  2424. e1WindCnt := e1.windCnt;
  2425. e2WindCnt := e2.windCnt;
  2426. end;
  2427. frNegative:
  2428. begin
  2429. e1WindCnt := -e1.windCnt;
  2430. e2WindCnt := -e2.windCnt;
  2431. end;
  2432. else
  2433. begin
  2434. e1WindCnt := abs(e1.windCnt);
  2435. e2WindCnt := abs(e2.windCnt);
  2436. end;
  2437. end;
  2438. if (not IsHotEdge(e1) and not (e1WindCnt in [0,1])) or
  2439. (not IsHotEdge(e2) and not (e2WindCnt in [0,1])) then Exit;
  2440. // NOW PROCESS THE INTERSECTION
  2441. // if both edges are 'hot' ...
  2442. if IsHotEdge(e1) and IsHotEdge(e2) then
  2443. begin
  2444. if not (e1WindCnt in [0,1]) or not (e2WindCnt in [0,1]) or
  2445. (not IsSamePolyType(e1, e2) and (fClipType <> ctXor)) then
  2446. begin
  2447. op := AddLocalMaxPoly(e1, e2, pt);
  2448. {$IFDEF USINGZ}
  2449. if Assigned(op) then SetZ(e1, e2, op.pt);
  2450. {$ENDIF}
  2451. end else if IsFront(e1) or (e1.outrec = e2.outrec) then
  2452. begin
  2453. // this 'else if' condition isn't strictly needed but
  2454. // it's sensible to split polygons that ony touch at
  2455. // a common vertex (not at common edges).
  2456. op := AddLocalMaxPoly(e1, e2, pt);
  2457. {$IFDEF USINGZ}
  2458. op2 := AddLocalMinPoly(e1, e2, pt);
  2459. if Assigned(op) then SetZ(e1, e2, op.pt);
  2460. SetZ(e1, e2, op2.pt);
  2461. {$ELSE}
  2462. AddLocalMinPoly(e1, e2, pt);
  2463. {$ENDIF}
  2464. end else
  2465. begin
  2466. // can't treat as maxima & minima
  2467. op := AddOutPt(e1, pt);
  2468. {$IFDEF USINGZ}
  2469. op2 := AddOutPt(e2, pt);
  2470. SetZ(e1, e2, op.pt);
  2471. SetZ(e1, e2, op2.pt);
  2472. {$ELSE}
  2473. AddOutPt(e2, pt);
  2474. {$ENDIF}
  2475. SwapOutRecs(e1, e2);
  2476. end;
  2477. end
  2478. // if one or other edge is 'hot' ...
  2479. else if IsHotEdge(e1) then
  2480. begin
  2481. op := AddOutPt(e1, pt);
  2482. {$IFDEF USINGZ}
  2483. SetZ(e1, e2, op.pt);
  2484. {$ENDIF}
  2485. SwapOutRecs(e1, e2);
  2486. end
  2487. else if IsHotEdge(e2) then
  2488. begin
  2489. op := AddOutPt(e2, pt);
  2490. {$IFDEF USINGZ}
  2491. SetZ(e1, e2, op.pt);
  2492. {$ENDIF}
  2493. SwapOutRecs(e1, e2);
  2494. end
  2495. // else neither edge is 'hot'
  2496. else
  2497. begin
  2498. case FFillRule of
  2499. frPositive:
  2500. begin
  2501. e1WindCnt2 := e1.windCnt2;
  2502. e2WindCnt2 := e2.windCnt2;
  2503. end;
  2504. frNegative:
  2505. begin
  2506. e1WindCnt2 := -e1.windCnt2;
  2507. e2WindCnt2 := -e2.windCnt2;
  2508. end;
  2509. else
  2510. begin
  2511. e1WindCnt2 := abs(e1.windCnt2);
  2512. e2WindCnt2 := abs(e2.windCnt2);
  2513. end;
  2514. end;
  2515. if not IsSamePolyType(e1, e2) then
  2516. begin
  2517. op := AddLocalMinPoly(e1, e2, pt, false);
  2518. {$IFDEF USINGZ}
  2519. SetZ(e1, e2, op.pt);
  2520. {$ENDIF}
  2521. end
  2522. else if (e1WindCnt = 1) and (e2WindCnt = 1) then
  2523. begin
  2524. op := nil;
  2525. case FClipType of
  2526. ctIntersection:
  2527. if (e1WindCnt2 <= 0) or (e2WindCnt2 <= 0) then Exit
  2528. else op := AddLocalMinPoly(e1, e2, pt, false);
  2529. ctUnion:
  2530. if (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0) then
  2531. op := AddLocalMinPoly(e1, e2, pt, false);
  2532. ctDifference:
  2533. if ((GetPolyType(e1) = ptClip) and
  2534. (e1WindCnt2 > 0) and (e2WindCnt2 > 0)) or
  2535. ((GetPolyType(e1) = ptSubject) and
  2536. (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0)) then
  2537. op := AddLocalMinPoly(e1, e2, pt, false);
  2538. else // xOr
  2539. op := AddLocalMinPoly(e1, e2, pt, false);
  2540. end;
  2541. {$IFDEF USINGZ}
  2542. if assigned(op) then SetZ(e1, e2, op.pt);
  2543. {$ENDIF}
  2544. end;
  2545. end;
  2546. end;
  2547. //------------------------------------------------------------------------------
  2548. {$IFNDEF USINGZ}
  2549. {$HINTS ON}
  2550. {$ENDIF}
  2551. procedure TClipperBase.DeleteEdges(var e: PActive);
  2552. var
  2553. e2: PActive;
  2554. begin
  2555. while Assigned(e) do
  2556. begin
  2557. e2 := e;
  2558. e := e.nextInAEL;
  2559. Dispose(e2);
  2560. end;
  2561. end;
  2562. //------------------------------------------------------------------------------
  2563. procedure TClipperBase.DeleteFromAEL(e: PActive);
  2564. var
  2565. aelPrev, aelNext: PActive;
  2566. begin
  2567. aelPrev := e.prevInAEL;
  2568. aelNext := e.nextInAEL;
  2569. if not Assigned(aelPrev) and not Assigned(aelNext) and
  2570. (e <> FActives) then Exit; // already deleted
  2571. if Assigned(aelPrev) then aelPrev.nextInAEL := aelNext
  2572. else FActives := aelNext;
  2573. if Assigned(aelNext) then aelNext.prevInAEL := aelPrev;
  2574. Dispose(e);
  2575. end;
  2576. //------------------------------------------------------------------------------
  2577. procedure TClipperBase.AdjustCurrXAndCopyToSEL(topY: Int64);
  2578. var
  2579. e: PActive;
  2580. begin
  2581. FSel := FActives;
  2582. e := FActives;
  2583. while Assigned(e) do
  2584. begin
  2585. e.prevInSEL := e.prevInAEL;
  2586. e.nextInSEL := e.nextInAEL;
  2587. e.jump := e.nextInSEL;
  2588. if (e.joinedWith = jwLeft) then
  2589. e.currX := e.prevInAEL.currX // this also avoids complications
  2590. else
  2591. e.currX := TopX(e, topY);
  2592. e := e.nextInAEL;
  2593. end;
  2594. end;
  2595. //------------------------------------------------------------------------------
  2596. procedure TClipperBase.ExecuteInternal(clipType: TClipType;
  2597. fillRule: TFillRule; usingPolytree: Boolean);
  2598. var
  2599. Y: Int64;
  2600. e: PActive;
  2601. begin
  2602. if clipType = ctNoClip then Exit;
  2603. FFillRule := fillRule;
  2604. FClipType := clipType;
  2605. Reset;
  2606. if not PopScanLine(Y) then Exit;
  2607. while FSucceeded do
  2608. begin
  2609. InsertLocalMinimaIntoAEL(Y);
  2610. while PopHorz(e) do DoHorizontal(e);
  2611. if FHorzSegList.Count > 0 then
  2612. begin
  2613. if FHorzSegList.Count > 1 then ConvertHorzSegsToJoins;
  2614. FHorzSegList.Clear;
  2615. end;
  2616. FBotY := Y; // FBotY == bottom of current scanbeam
  2617. if not PopScanLine(Y) then Break; // Y == top of current scanbeam
  2618. DoIntersections(Y);
  2619. DoTopOfScanbeam(Y);
  2620. while PopHorz(e) do DoHorizontal(e);
  2621. end;
  2622. if Succeeded then ProcessHorzJoins;
  2623. end;
  2624. //------------------------------------------------------------------------------
  2625. procedure FixOutRecPts(outrec: POutrec); overload;
  2626. {$IFDEF INLINING} inline; {$ENDIF}
  2627. var
  2628. op: POutPt;
  2629. begin
  2630. op := outrec.pts;
  2631. repeat
  2632. op.outrec := outrec;
  2633. op := op.next;
  2634. until op = outrec.pts;
  2635. end;
  2636. //------------------------------------------------------------------------------
  2637. procedure SetOutRecPts(op: POutPt; newOR: POutrec); overload;
  2638. {$IFDEF INLINING} inline; {$ENDIF}
  2639. var
  2640. op2: POutPt;
  2641. begin
  2642. op2 := op;
  2643. repeat
  2644. op2.outrec := newOR;
  2645. op2 := op2.next;
  2646. until op2 = op;
  2647. end;
  2648. //------------------------------------------------------------------------------
  2649. function HorzOverlapWithLRSet(const left1, right1, left2, right2: TPoint64): boolean;
  2650. {$IFDEF INLINING} inline; {$ENDIF}
  2651. begin
  2652. Result := (left1.X < right2.X) and (right1.X > left2.X);
  2653. end;
  2654. //------------------------------------------------------------------------------
  2655. function HorzontalsOverlap(const horz1a, horz1b, horz2a, horz2b: TPoint64): boolean;
  2656. {$IFDEF INLINING} inline; {$ENDIF}
  2657. begin
  2658. if horz1a.X < horz1b.X then
  2659. begin
  2660. Result := Iif(horz2a.X < horz2b.X,
  2661. HorzOverlapWithLRSet(horz1a, horz1b, horz2a, horz2b),
  2662. HorzOverlapWithLRSet(horz1a, horz1b, horz2b, horz2a));
  2663. end else
  2664. begin
  2665. Result := Iif(horz2a.X < horz2b.X,
  2666. HorzOverlapWithLRSet(horz1b, horz1a, horz2a, horz2b),
  2667. HorzOverlapWithLRSet(horz1b, horz1a, horz2b, horz2a));
  2668. end;
  2669. end;
  2670. //------------------------------------------------------------------------------
  2671. procedure SetRealOutRec(op: POutPt); {$IFDEF INLINING} inline; {$ENDIF}
  2672. begin
  2673. op.outrec := GetRealOutRec(op.outrec);
  2674. end;
  2675. //------------------------------------------------------------------------------
  2676. function SetHorzSegHeadingForward(hs: PHorzSegment; opP, opN: POutPt): Boolean;
  2677. {$IFDEF INLINING} inline; {$ENDIF}
  2678. begin
  2679. Result := opP.pt.X <> opN.pt.X;
  2680. if not Result then Exit;
  2681. if opP.pt.X < opN.pt.X then
  2682. begin
  2683. hs.leftOp := opP;
  2684. hs.rightOp := opN;
  2685. hs.leftToRight := true;
  2686. end else
  2687. begin
  2688. hs.leftOp := opN;
  2689. hs.rightOp := opP;
  2690. hs.leftToRight := false;
  2691. end;
  2692. end;
  2693. //------------------------------------------------------------------------------
  2694. function UpdateHorzSegment(hs: PHorzSegment): Boolean;
  2695. {$IFDEF INLINING} inline; {$ENDIF}
  2696. var
  2697. op, opP, opN, opA, opZ: POutPt;
  2698. outrec: POutrec;
  2699. currY: Int64;
  2700. outrecHasEdges: Boolean;
  2701. begin
  2702. op := hs.leftOp;
  2703. outrec := GetRealOutRec(op.outrec);
  2704. outrecHasEdges := Assigned(outrec.frontE);
  2705. // nb: it's possible that both opA and opZ are below op
  2706. // (eg when there's been an intermediate maxima horz. join)
  2707. currY := op.pt.Y;
  2708. opP := op; opN := op;
  2709. if outrecHasEdges then
  2710. begin
  2711. opA := outrec.pts;
  2712. opZ := opA.next;
  2713. while (opP <> opZ) and (opP.prev.pt.Y = currY) do
  2714. opP := opP.prev;
  2715. while (opN <> opA) and (opN.next.pt.Y = currY) do
  2716. opN := opN.next;
  2717. end else
  2718. begin
  2719. while (opP.prev <> opN) and (opP.prev.pt.Y = currY) do
  2720. opP := opP.prev;
  2721. while (opN.next <> opP) and (opN.next.pt.Y = currY) do
  2722. opN := opN.next;
  2723. end;
  2724. Result := SetHorzSegHeadingForward(hs, opP, opN) and
  2725. not Assigned(hs.leftOp.horz);
  2726. if Result then hs.leftOp.horz := hs;
  2727. end;
  2728. //------------------------------------------------------------------------------
  2729. procedure TClipperBase.ConvertHorzSegsToJoins;
  2730. var
  2731. i, j: integer;
  2732. currY: Int64;
  2733. hs1, hs2: PHorzSegment;
  2734. begin
  2735. j := 0;
  2736. for i := 0 to FHorzSegList.Count -1 do
  2737. begin
  2738. hs1 := FHorzSegList.UnsafeGet(i);
  2739. if UpdateHorzSegment(hs1) then
  2740. begin
  2741. if (j < i) then
  2742. FHorzSegList.UnsafeSet(j, hs1);
  2743. inc(j);
  2744. end else
  2745. Dispose(hs1);
  2746. end;
  2747. FHorzSegList.Resize(j);
  2748. if j < 2 then Exit;
  2749. FHorzSegList.Sort(HorzSegListSort);
  2750. // find overlaps
  2751. for i := 0 to FHorzSegList.Count -2 do
  2752. begin
  2753. hs1 := FHorzSegList.UnsafeGet(i);
  2754. for j := i+1 to FHorzSegList.Count -1 do
  2755. begin
  2756. hs2 := FHorzSegList.UnsafeGet(j);
  2757. if (hs2.leftOp.pt.X >= hs1.rightOp.pt.X) or
  2758. (hs2.leftToRight = hs1.leftToRight) or
  2759. (hs2.rightOp.pt.X <= hs1.leftOp.pt.X) then Continue;
  2760. currY := hs1.leftOp.pt.Y;
  2761. if hs1.leftToRight then
  2762. begin
  2763. while (hs1.leftOp.next.pt.Y = currY) and
  2764. (hs1.leftOp.next.pt.X <= hs2.leftOp.pt.X) do
  2765. hs1.leftOp := hs1.leftOp.next;
  2766. while (hs2.leftOp.prev.pt.Y = currY) and
  2767. (hs2.leftOp.prev.pt.X <= hs1.leftOp.pt.X) do
  2768. hs2.leftOp := hs2.leftOp.prev;
  2769. FHorzJoinList.Add(
  2770. DuplicateOp(hs1.leftOp, true),
  2771. DuplicateOp(hs2.leftOp, false));
  2772. end else
  2773. begin
  2774. while (hs1.leftOp.prev.pt.Y = currY) and
  2775. (hs1.leftOp.prev.pt.X <= hs2.leftOp.pt.X) do
  2776. hs1.leftOp := hs1.leftOp.prev;
  2777. while (hs2.leftOp.next.pt.Y = currY) and
  2778. (hs2.leftOp.next.pt.X <= hs1.leftOp.pt.X) do
  2779. hs2.leftOp := hs2.leftOp.next;
  2780. FHorzJoinList.Add(
  2781. DuplicateOp(hs2.leftOp, true),
  2782. DuplicateOp(hs1.leftOp, false));
  2783. end;
  2784. end;
  2785. end;
  2786. end;
  2787. //------------------------------------------------------------------------------
  2788. procedure MoveSplits(fromOr, toOr: POutRec);
  2789. var
  2790. i: integer;
  2791. begin
  2792. if not assigned(fromOr.splits) then Exit;
  2793. for i := 0 to High(fromOr.splits) do
  2794. AddSplit(toOr, fromOr.splits[i]);
  2795. fromOr.splits := nil;
  2796. end;
  2797. //------------------------------------------------------------------------------
  2798. procedure TClipperBase.ProcessHorzJoins;
  2799. var
  2800. i: integer;
  2801. or1, or2: POutRec;
  2802. op1b, op2b, tmp: POutPt;
  2803. begin
  2804. for i := 0 to FHorzJoinList.Count -1 do
  2805. with PHorzJoin(FHorzJoinList[i])^ do
  2806. begin
  2807. or1 := GetRealOutRec(op1.outrec);
  2808. or2 := GetRealOutRec(op2.outrec);
  2809. // op1 >>> op1b
  2810. // op2 <<< op2b
  2811. op1b := op1.next;
  2812. op2b := op2.prev;
  2813. op1.next := op2;
  2814. op2.prev := op1;
  2815. op1b.prev := op2b;
  2816. op2b.next := op1b;
  2817. if or1 = or2 then // 'join' is really a split
  2818. begin
  2819. or2 := FOutRecList.Add;
  2820. or2.pts := op1b;
  2821. FixOutRecPts(or2);
  2822. //if or1->pts has moved to or2 then update or1->pts!!
  2823. if or1.pts.outrec = or2 then
  2824. begin
  2825. or1.pts := op1;
  2826. or1.pts.outrec := or1;
  2827. end;
  2828. if FUsingPolytree then //#498, #520, #584, D#576, #618
  2829. begin
  2830. if Path1InsidePath2(or1.pts, or2.pts) then
  2831. begin
  2832. //swap or1's & or2's pts
  2833. tmp := or1.pts;
  2834. or1.pts := or2.pts;
  2835. or2.pts := tmp;
  2836. FixOutRecPts(or1);
  2837. FixOutRecPts(or2);
  2838. //or2 is now inside or1
  2839. or2.owner := or1;
  2840. end
  2841. else if Path1InsidePath2(or2.pts, or1.pts) then
  2842. begin
  2843. or2.owner := or1;
  2844. end
  2845. else
  2846. or2.owner := or1.owner;
  2847. AddSplit(or1, or2);
  2848. end
  2849. else
  2850. or2.owner := or1;
  2851. end else
  2852. begin
  2853. or2.pts := nil;
  2854. if FUsingPolytree then
  2855. begin
  2856. SetOwner(or2, or1);
  2857. MoveSplits(or2, or1); //#618
  2858. end else
  2859. or2.owner := or1;
  2860. end;
  2861. end;
  2862. end;
  2863. //------------------------------------------------------------------------------
  2864. procedure TClipperBase.DoIntersections(const topY: Int64);
  2865. begin
  2866. if BuildIntersectList(topY) then
  2867. try
  2868. ProcessIntersectList;
  2869. finally
  2870. DisposeIntersectNodes;
  2871. end;
  2872. end;
  2873. //------------------------------------------------------------------------------
  2874. procedure TClipperBase.DisposeIntersectNodes;
  2875. var
  2876. i: Integer;
  2877. begin
  2878. for i := 0 to FIntersectList.Count - 1 do
  2879. Dispose(PIntersectNode(UnsafeGet(FIntersectList,i)));
  2880. FIntersectList.Clear;
  2881. end;
  2882. //------------------------------------------------------------------------------
  2883. procedure TClipperBase.AddNewIntersectNode(e1, e2: PActive; topY: Int64);
  2884. var
  2885. ip: TPoint64;
  2886. absDx1, absDx2: double;
  2887. node: PIntersectNode;
  2888. begin
  2889. if not GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip) then
  2890. ip := Point64(e1.currX, topY);
  2891. // Rounding errors can occasionally place the calculated intersection
  2892. // point either below or above the scanbeam, so check and correct ...
  2893. if (ip.Y > FBotY) or (ip.Y < topY) then
  2894. begin
  2895. absDx1 := Abs(e1.dx);
  2896. absDx2 := Abs(e2.dx);
  2897. if (absDx1 > 100) and (absDx2 > 100) then
  2898. begin
  2899. if (absDx1 > absDx2) then
  2900. ip := GetClosestPointOnSegment(ip, e1.bot, e1.top) else
  2901. ip := GetClosestPointOnSegment(ip, e2.bot, e2.top);
  2902. end
  2903. else if (absDx1 > 100) then
  2904. ip := GetClosestPointOnSegment(ip, e1.bot, e1.top)
  2905. else if (absDx2 > 100) then
  2906. ip := GetClosestPointOnSegment(ip, e2.bot, e2.top)
  2907. else
  2908. begin
  2909. ip.Y := Iif(ip.Y < topY, topY , fBotY);
  2910. ip.X := Iif(absDx1 < absDx2, TopX(e1, ip.Y), TopX(e2, ip.Y));
  2911. end;
  2912. end;
  2913. new(node);
  2914. node.active1 := e1;
  2915. node.active2 := e2;
  2916. node.pt := ip;
  2917. FIntersectList.Add(node);
  2918. end;
  2919. //------------------------------------------------------------------------------
  2920. function ExtractFromSEL(edge: PActive): PActive;
  2921. begin
  2922. // nb: edge.PrevInSEL is always assigned
  2923. Result := edge.nextInSEL;
  2924. if Assigned(Result) then
  2925. Result.prevInSEL := edge.prevInSEL;
  2926. edge.prevInSEL.nextInSEL := Result;
  2927. end;
  2928. //------------------------------------------------------------------------------
  2929. procedure Insert1Before2InSEL(edge1, edge2: PActive);
  2930. begin
  2931. edge1.prevInSEL := edge2.prevInSEL;
  2932. if Assigned(edge1.prevInSEL) then
  2933. edge1.prevInSEL.nextInSEL := edge1;
  2934. edge1.nextInSEL := edge2;
  2935. edge2.prevInSEL := edge1;
  2936. end;
  2937. //------------------------------------------------------------------------------
  2938. function TClipperBase.BuildIntersectList(const topY: Int64): Boolean;
  2939. var
  2940. e, base,prevBase,left,right, lend, rend: PActive;
  2941. begin
  2942. result := false;
  2943. if not Assigned(FActives) or not Assigned(FActives.nextInAEL) then Exit;
  2944. // Calculate edge positions at the top of the current scanbeam, and from this
  2945. // we will determine the intersections required to reach these new positions.
  2946. AdjustCurrXAndCopyToSEL(topY);
  2947. // Find all edge intersections in the current scanbeam using a stable merge
  2948. // sort that ensures only adjacent edges are intersecting. Intersect info is
  2949. // stored in FIntersectList ready to be processed in ProcessIntersectList.
  2950. left := FSel;
  2951. while Assigned(left.jump) do
  2952. begin
  2953. prevBase := nil;
  2954. while Assigned(left) and Assigned(left.jump) do
  2955. begin
  2956. base := left;
  2957. right := left.jump;
  2958. rend := right.jump;
  2959. left.jump := rend;
  2960. lend := right; rend := right.jump;
  2961. while (left <> lend) and (right <> rend) do
  2962. begin
  2963. if right.currX < left.currX then
  2964. begin
  2965. // save edge intersections
  2966. e := right.prevInSEL;
  2967. while true do
  2968. begin
  2969. AddNewIntersectNode(e, right, topY);
  2970. if e = left then Break;
  2971. e := e.prevInSEL;
  2972. end;
  2973. // now move the out of place edge on the right
  2974. // to its new ordered place on the left.
  2975. e := right;
  2976. right := ExtractFromSEL(e); // ie returns the new right
  2977. lend := right;
  2978. Insert1Before2InSEL(e, left);
  2979. if left = base then
  2980. begin
  2981. base := e;
  2982. base.jump := rend;
  2983. if Assigned(prevBase) then
  2984. prevBase.jump := base else
  2985. FSel := base;
  2986. end;
  2987. end else
  2988. left := left.nextInSEL;
  2989. end;
  2990. prevBase := base;
  2991. left := rend;
  2992. end;
  2993. left := FSel;
  2994. end;
  2995. result := FIntersectList.Count > 0;
  2996. end;
  2997. //------------------------------------------------------------------------------
  2998. function IntersectListSort(node1, node2: Pointer): Integer;
  2999. var
  3000. pt1, pt2: PPoint64;
  3001. i: Int64;
  3002. begin
  3003. if node1 = node2 then
  3004. begin
  3005. Result := 0;
  3006. Exit;
  3007. end;
  3008. pt1 := @PIntersectNode(node1).pt;
  3009. pt2 := @PIntersectNode(node2).pt;
  3010. i := pt2.Y - pt1.Y;
  3011. // note to self - can't return int64 values :)
  3012. if i > 0 then Result := 1
  3013. else if i < 0 then Result := -1
  3014. else if (pt1 = pt2) then Result := 0
  3015. else
  3016. begin
  3017. // Sort by X too. Not essential, but it significantly
  3018. // speeds up the secondary sort in ProcessIntersectList .
  3019. i := pt1.X - pt2.X;
  3020. if i > 0 then Result := 1
  3021. else if i < 0 then Result := -1
  3022. else Result := 0;
  3023. end;
  3024. end;
  3025. //------------------------------------------------------------------------------
  3026. procedure TClipperBase.ProcessIntersectList;
  3027. var
  3028. i: Integer;
  3029. nodeI, nodeJ: PPIntersectNode;
  3030. begin
  3031. // The list of required intersections now needs to be processed in a
  3032. // specific order such that intersection points with the largest Y coords
  3033. // are processed before those with the smallest Y coords. However,
  3034. // it's critical that edges are adjacent at the time of intersection, but
  3035. // that can only be checked during processing (when edge positions change).
  3036. // First we do a quicksort so that intersections will be processed
  3037. // mostly from largest Y to smallest
  3038. FIntersectList.Sort(IntersectListSort);
  3039. nodeI := @FIntersectList.List[0];
  3040. for i := 0 to FIntersectList.Count - 1 do
  3041. begin
  3042. // during processing, make sure edges are adjacent before
  3043. // proceeding, and swapping the order if they aren't adjacent.
  3044. if not EdgesAdjacentInAEL(nodeI^) then
  3045. begin
  3046. nodeJ := nodeI;
  3047. repeat
  3048. inc(nodeJ);
  3049. until EdgesAdjacentInAEL(nodeJ^);
  3050. // now swap intersection order
  3051. Swap(PPointer(nodeI), PPointer(nodeJ));
  3052. end;
  3053. // now process the intersection
  3054. with nodeI^^ do
  3055. begin
  3056. IntersectEdges(active1, active2, pt);
  3057. SwapPositionsInAEL(active1, active2);
  3058. active1.currX := pt.X;
  3059. active2.currX := pt.X;
  3060. CheckJoinLeft(active2, pt, true);
  3061. CheckJoinRight(active1, pt, true);
  3062. end;
  3063. inc(nodeI);
  3064. end;
  3065. // Edges should once again be correctly ordered (left to right) in the AEL.
  3066. end;
  3067. //------------------------------------------------------------------------------
  3068. procedure TClipperBase.SwapPositionsInAEL(e1, e2: PActive);
  3069. var
  3070. prev, next: PActive;
  3071. begin
  3072. // preconditon: e1 must be immediately prior to e2
  3073. next := e2.nextInAEL;
  3074. if Assigned(next) then next.prevInAEL := e1;
  3075. prev := e1.prevInAEL;
  3076. if Assigned(prev) then prev.nextInAEL := e2;
  3077. e2.prevInAEL := prev;
  3078. e2.nextInAEL := e1;
  3079. e1.prevInAEL := e2;
  3080. e1.nextInAEL := next;
  3081. if not Assigned(e2.prevInAEL) then FActives := e2;
  3082. end;
  3083. //------------------------------------------------------------------------------
  3084. function GetLastOp(hotEdge: PActive): POutPt;
  3085. {$IFDEF INLINING} inline; {$ENDIF}
  3086. var
  3087. outrec: POutRec;
  3088. begin
  3089. outrec := hotEdge.outrec;
  3090. Result := outrec.pts;
  3091. if hotEdge <> outrec.frontE then
  3092. Result := Result.next;
  3093. end;
  3094. //------------------------------------------------------------------------------
  3095. procedure TClipperBase.DoHorizontal(horzEdge: PActive);
  3096. var
  3097. maxVertex: PVertex;
  3098. horzLeft, horzRight: Int64;
  3099. function ResetHorzDirection: Boolean;
  3100. var
  3101. e: PActive;
  3102. begin
  3103. if (horzEdge.bot.X = horzEdge.top.X) then
  3104. begin
  3105. // the horizontal edge is going nowhere ...
  3106. horzLeft := horzEdge.currX;
  3107. horzRight := horzEdge.currX;
  3108. e := horzEdge.nextInAEL;
  3109. while assigned(e) and (e.vertTop <> maxVertex) do
  3110. e := e.nextInAEL;
  3111. Result := assigned(e);
  3112. // nb: this block isn't yet redundant
  3113. end
  3114. else if horzEdge.currX < horzEdge.top.X then
  3115. begin
  3116. horzLeft := horzEdge.currX;
  3117. horzRight := horzEdge.top.X;
  3118. Result := true;
  3119. end else
  3120. begin
  3121. horzLeft := horzEdge.top.X;
  3122. horzRight := horzEdge.currX;
  3123. Result := false;
  3124. end;
  3125. end;
  3126. //------------------------------------------------------------------------
  3127. var
  3128. Y: Int64;
  3129. e: PActive;
  3130. pt: TPoint64;
  3131. op: POutPt;
  3132. isLeftToRight, horzIsOpen: Boolean;
  3133. begin
  3134. (*******************************************************************************
  3135. * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or *
  3136. * bottom of a scanbeam) are processed as if layered. The order in which HEs *
  3137. * are processed doesn't matter. HEs intersect with the bottom vertices of *
  3138. * other HEs [#] and with non-horizontal edges [*]. Once these intersections *
  3139. * are completed, intermediate HEs are 'promoted' to the next edge in their *
  3140. * bounds, and they in turn may be intersected [%] by other HEs. *
  3141. * *
  3142. * eg: 3 horizontals at a scanline: / | / / *
  3143. * | / | (HE3) o=========%==========o *
  3144. * o=======o (HE2) / | / / *
  3145. * o============#=========*======*========#=========o (HE1) *
  3146. * / | / | / *
  3147. *******************************************************************************)
  3148. horzIsOpen := IsOpen(horzEdge);
  3149. Y := horzEdge.bot.Y;
  3150. maxVertex := nil;
  3151. if horzIsOpen then
  3152. maxVertex := GetCurrYMaximaVertexOpen(horzEdge) else
  3153. maxVertex := GetCurrYMaximaVertex(horzEdge);
  3154. isLeftToRight := ResetHorzDirection;
  3155. // nb: TrimHorz above hence not using Bot.X here
  3156. if IsHotEdge(horzEdge) then
  3157. begin
  3158. {$IFDEF USINGZ}
  3159. op := AddOutPt(horzEdge, Point64(horzEdge.currX, Y, horzEdge.bot.Z));
  3160. {$ELSE}
  3161. op := AddOutPt(horzEdge, Point64(horzEdge.currX, Y));
  3162. {$ENDIF}
  3163. FHorzSegList.Add(op);
  3164. end;
  3165. while true do // loop through consec. horizontal edges
  3166. begin
  3167. if isLeftToRight then
  3168. e := horzEdge.nextInAEL else
  3169. e := horzEdge.prevInAEL;
  3170. while assigned(e) do
  3171. begin
  3172. if (e.vertTop = maxVertex) then
  3173. begin
  3174. if IsHotEdge(horzEdge) and IsJoined(e) then
  3175. UndoJoin(e, e.top);
  3176. if IsHotEdge(horzEdge) then
  3177. begin
  3178. while (horzEdge.vertTop <> maxVertex) do
  3179. begin
  3180. AddOutPt(horzEdge, horzEdge.top);
  3181. UpdateEdgeIntoAEL(horzEdge);
  3182. end;
  3183. if isLeftToRight then
  3184. AddLocalMaxPoly(horzEdge, e, horzEdge.top) else
  3185. AddLocalMaxPoly(e, horzEdge, horzEdge.top);
  3186. end;
  3187. // remove horzEdge's maxPair from AEL
  3188. DeleteFromAEL(e);
  3189. DeleteFromAEL(horzEdge);
  3190. Exit;
  3191. end;
  3192. // if horzEdge is a maxima, keep going until we reach
  3193. // its maxima pair, otherwise check for Break conditions
  3194. if (maxVertex <> horzEdge.vertTop) or IsOpenEnd(horzEdge.vertTop) then
  3195. begin
  3196. // otherwise stop when 'e' is beyond the end of the horizontal line
  3197. if (isLeftToRight and (e.currX > horzRight)) or
  3198. (not isLeftToRight and (e.currX < horzLeft)) then Break;
  3199. if (e.currX = horzEdge.top.X) and not IsHorizontal(e) then
  3200. begin
  3201. pt := NextVertex(horzEdge).pt;
  3202. // to maximize the possibility of putting open edges into
  3203. // solutions, we'll only break if it's past HorzEdge's end
  3204. if IsOpen(E) and not IsSamePolyType(E, horzEdge) and
  3205. not IsHotEdge(e) then
  3206. begin
  3207. if (isLeftToRight and (TopX(E, pt.Y) > pt.X)) or
  3208. (not isLeftToRight and (TopX(E, pt.Y) < pt.X)) then Break;
  3209. end
  3210. // otherwise for edges at horzEdge's end, only stop when horzEdge's
  3211. // outslope is greater than e's slope when heading right or when
  3212. // horzEdge's outslope is less than e's slope when heading left.
  3213. else if (isLeftToRight and (TopX(E, pt.Y) >= pt.X)) or
  3214. (not isLeftToRight and (TopX(E, pt.Y) <= pt.X)) then Break;
  3215. end;
  3216. end;
  3217. pt := Point64(e.currX, Y);
  3218. if (isLeftToRight) then
  3219. begin
  3220. IntersectEdges(horzEdge, e, pt);
  3221. SwapPositionsInAEL(horzEdge, e);
  3222. CheckJoinLeft(e, pt);
  3223. horzEdge.currX := e.currX;
  3224. e := horzEdge.nextInAEL;
  3225. end else
  3226. begin
  3227. IntersectEdges(e, horzEdge, pt);
  3228. SwapPositionsInAEL(e, horzEdge);
  3229. CheckJoinRight(e, pt);
  3230. horzEdge.currX := e.currX;
  3231. e := horzEdge.prevInAEL;
  3232. end;
  3233. if IsHotEdge(horzEdge) then
  3234. begin
  3235. //nb: The outrec containining the op returned by IntersectEdges
  3236. //above may no longer be associated with horzEdge.
  3237. FHorzSegList.Add(GetLastOp(horzEdge));
  3238. end;
  3239. end; // we've reached the end of this horizontal
  3240. // check if we've finished looping through consecutive horizontals
  3241. if horzIsOpen and IsOpenEnd(horzEdge.vertTop) then
  3242. begin
  3243. if IsHotEdge(horzEdge) then
  3244. begin
  3245. AddOutPt(horzEdge, horzEdge.top);
  3246. if IsFront(horzEdge) then
  3247. horzEdge.outrec.frontE := nil else
  3248. horzEdge.outrec.backE := nil;
  3249. horzEdge.outrec := nil;
  3250. end;
  3251. DeleteFromAEL(horzEdge); // ie open at top
  3252. Exit;
  3253. end;
  3254. if (NextVertex(horzEdge).pt.Y <> horzEdge.top.Y) then
  3255. Break; // end of an intermediate horizontal
  3256. // there must be a following (consecutive) horizontal
  3257. if IsHotEdge(horzEdge) then
  3258. AddOutPt(horzEdge, horzEdge.top);
  3259. UpdateEdgeIntoAEL(horzEdge);
  3260. isLeftToRight := ResetHorzDirection;
  3261. end; // end while horizontal
  3262. if IsHotEdge(horzEdge) then
  3263. begin
  3264. op := AddOutPt(horzEdge, horzEdge.top);
  3265. FHorzSegList.Add(op); // Disc.#546
  3266. end;
  3267. UpdateEdgeIntoAEL(horzEdge); // this is the end of an intermediate horiz.
  3268. end;
  3269. //------------------------------------------------------------------------------
  3270. procedure TClipperBase.DoTopOfScanbeam(Y: Int64);
  3271. var
  3272. e: PActive;
  3273. begin
  3274. // FSel is reused to flag horizontals (see PushHorz below)
  3275. FSel := nil;
  3276. e := FActives;
  3277. while Assigned(e) do
  3278. begin
  3279. // nb: 'e' will never be horizontal here
  3280. if (e.top.Y = Y) then
  3281. begin
  3282. e.currX := e.top.X;
  3283. if IsMaxima(e) then
  3284. begin
  3285. e := DoMaxima(e); // TOP OF BOUND (MAXIMA)
  3286. Continue;
  3287. end else
  3288. begin
  3289. // INTERMEDIATE VERTEX ...
  3290. if IsHotEdge(e) then
  3291. AddOutPt(e, e.top);
  3292. UpdateEdgeIntoAEL(e);
  3293. if IsHorizontal(e) then
  3294. PushHorz(e);
  3295. end;
  3296. end else
  3297. e.currX := TopX(e, Y);
  3298. e := e.nextInAEL;
  3299. end;
  3300. end;
  3301. //------------------------------------------------------------------------------
  3302. function TClipperBase.DoMaxima(e: PActive): PActive;
  3303. var
  3304. eNext, ePrev, eMaxPair: PActive;
  3305. begin
  3306. ePrev := e.prevInAEL;
  3307. eNext := e.nextInAEL;
  3308. Result := eNext;
  3309. if IsOpenEnd(e.vertTop) then
  3310. begin
  3311. if IsHotEdge(e) then AddOutPt(e, e.top);
  3312. if not IsHorizontal(e) then
  3313. begin
  3314. if IsHotEdge(e) then
  3315. begin
  3316. if IsFront(e) then
  3317. e.outrec.frontE := nil else
  3318. e.outrec.backE := nil;
  3319. e.outrec := nil;
  3320. end;
  3321. DeleteFromAEL(e);
  3322. end;
  3323. Exit;
  3324. end else
  3325. begin
  3326. eMaxPair := GetMaximaPair(e);
  3327. if not assigned(eMaxPair) then Exit; // EMaxPair is a horizontal ...
  3328. end;
  3329. if IsJoined(e) then UndoJoin(e, e.top);
  3330. if IsJoined(eMaxPair) then UndoJoin(eMaxPair, eMaxPair.top);
  3331. // only non-horizontal maxima here.
  3332. // process any edges between maxima pair ...
  3333. while (eNext <> eMaxPair) do
  3334. begin
  3335. IntersectEdges(e, eNext, e.top);
  3336. SwapPositionsInAEL(e, eNext);
  3337. eNext := e.nextInAEL;
  3338. end;
  3339. if IsOpen(e) then
  3340. begin
  3341. // must be in the middle of an open path
  3342. if IsHotEdge(e) then
  3343. AddLocalMaxPoly(e, eMaxPair, e.top);
  3344. DeleteFromAEL(eMaxPair);
  3345. DeleteFromAEL(e);
  3346. if assigned(ePrev) then
  3347. Result := ePrev.nextInAEL else
  3348. Result := FActives;
  3349. end else
  3350. begin
  3351. // e.NextInAEL == eMaxPair
  3352. if IsHotEdge(e) then
  3353. AddLocalMaxPoly(e, eMaxPair, e.top);
  3354. DeleteFromAEL(e);
  3355. DeleteFromAEL(eMaxPair);
  3356. if assigned(ePrev) then
  3357. Result := ePrev.nextInAEL else
  3358. Result := FActives;
  3359. end;
  3360. end;
  3361. //------------------------------------------------------------------------------
  3362. function TClipperBase.BuildPaths(var closedPaths, openPaths: TPaths64): Boolean;
  3363. var
  3364. i: Integer;
  3365. closedCnt, openCnt: integer;
  3366. outRec: POutRec;
  3367. begin
  3368. closedCnt := Length(closedPaths);
  3369. openCnt := Length(openPaths);
  3370. try
  3371. i := 0;
  3372. while i < FOutRecList.Count do
  3373. begin
  3374. outRec := FOutRecList.UnsafeGet(i);
  3375. inc(i);
  3376. if not assigned(outRec.pts) then Continue;
  3377. if outRec.isOpen then
  3378. begin
  3379. SetLength(openPaths, openCnt +1);
  3380. if BuildPath(outRec.pts, FReverseSolution,
  3381. true, openPaths[openCnt]) then inc(openCnt);
  3382. end else
  3383. begin
  3384. // nb: CleanCollinear can add to FOutRecList
  3385. CleanCollinear(outRec);
  3386. // closed paths should always return a Positive orientation
  3387. // except when ReverseSolution == true
  3388. SetLength(closedPaths, closedCnt +1);
  3389. if BuildPath(outRec.pts, FReverseSolution,
  3390. false, closedPaths[closedCnt]) then
  3391. inc(closedCnt);
  3392. end;
  3393. end;
  3394. result := true;
  3395. except
  3396. result := false;
  3397. end;
  3398. end;
  3399. //------------------------------------------------------------------------------
  3400. function TClipperBase.CheckBounds(outrec: POutRec): Boolean;
  3401. begin
  3402. if not Assigned(outrec.pts) then
  3403. Result := false
  3404. else if not outrec.bounds.IsEmpty then
  3405. Result := true
  3406. else
  3407. begin
  3408. CleanCollinear(outrec);
  3409. result := Assigned(outrec.pts) and
  3410. BuildPath(outrec.pts, FReverseSolution, false, outrec.path);
  3411. if not Result then Exit;
  3412. outrec.bounds := Clipper.Core.GetBounds(outrec.path);
  3413. end;
  3414. end;
  3415. //------------------------------------------------------------------------------
  3416. function TClipperBase.CheckSplitOwner(outrec: POutRec; const splits: TOutRecArray): Boolean;
  3417. var
  3418. i : integer;
  3419. split : POutrec;
  3420. begin
  3421. // returns true if a valid owner is found in splits
  3422. // (and also assigns it to outrec.owner)
  3423. Result := true;
  3424. for i := 0 to High(splits) do
  3425. begin
  3426. split := GetRealOutRec(splits[i]);
  3427. if (split = nil) or
  3428. (split = outrec) or
  3429. (split.recursiveCheck = outrec) then Continue;
  3430. split.recursiveCheck := outrec; // prevent infinite loops
  3431. if Assigned(split.splits) and
  3432. CheckSplitOwner(outrec, split.splits) then Exit
  3433. else if IsValidOwner(outrec, split) and
  3434. CheckBounds(split) and
  3435. (split.bounds.Contains(outrec.bounds) and
  3436. Path1InsidePath2(outrec.pts, split.pts)) then
  3437. begin
  3438. outrec.owner := split;
  3439. Exit;
  3440. end;
  3441. end;
  3442. Result := false;
  3443. end;
  3444. //------------------------------------------------------------------------------
  3445. procedure TClipperBase.RecursiveCheckOwners(outrec: POutRec; polytree: TPolyPathBase);
  3446. begin
  3447. // pre-condition: outrec will have valid bounds
  3448. // post-condition: if a valid path, outrec will have a polypath
  3449. if Assigned(outrec.polypath) or
  3450. outrec.bounds.IsEmpty then
  3451. Exit;
  3452. while Assigned(outrec.owner) do
  3453. begin
  3454. if Assigned(outrec.owner.splits) and
  3455. CheckSplitOwner(outrec, outrec.owner.splits) then Break;
  3456. if Assigned(outrec.owner.pts) and
  3457. CheckBounds(outrec.owner) and
  3458. (outrec.owner.bounds.Contains(outrec.bounds) and
  3459. Path1InsidePath2(outrec.pts, outrec.owner.pts)) then break;
  3460. outrec.owner := outrec.owner.owner;
  3461. end;
  3462. if Assigned(outrec.owner) then
  3463. begin
  3464. if not Assigned(outrec.owner.polypath) then
  3465. RecursiveCheckOwners(outrec.owner, polytree);
  3466. outrec.polypath := outrec.owner.polypath.AddChild(outrec.path)
  3467. end else
  3468. outrec.polypath := polytree.AddChild(outrec.path);
  3469. end;
  3470. //------------------------------------------------------------------------------
  3471. function TClipperBase.BuildTree(polytree: TPolyPathBase;
  3472. out openPaths: TPaths64): Boolean;
  3473. var
  3474. i : Integer;
  3475. cntOpen : Integer;
  3476. outrec : POutRec;
  3477. openPath : TPath64;
  3478. begin
  3479. try
  3480. polytree.Clear;
  3481. if FHasOpenPaths then
  3482. setLength(openPaths, FOutRecList.Count);
  3483. cntOpen := 0;
  3484. i := 0;
  3485. // FOutRecList.Count is not static here because
  3486. // CheckBounds below can indirectly add additional
  3487. // OutRec (via FixOutRecPts & CleanCollinear)
  3488. while i < FOutRecList.Count do
  3489. begin
  3490. outrec := FOutRecList.UnsafeGet(i);
  3491. inc(i);
  3492. if not assigned(outrec.pts) or
  3493. assigned(outrec.polypath) then Continue;
  3494. if outrec.isOpen then
  3495. begin
  3496. if BuildPath(outrec.pts,
  3497. FReverseSolution, true, openPath) then
  3498. begin
  3499. openPaths[cntOpen] := openPath;
  3500. inc(cntOpen);
  3501. end;
  3502. Continue;
  3503. end;
  3504. if CheckBounds(outrec) then
  3505. RecursiveCheckOwners(outrec, polytree);
  3506. end;
  3507. setLength(openPaths, cntOpen);
  3508. Result := FSucceeded;
  3509. except
  3510. Result := false;
  3511. end;
  3512. end;
  3513. //------------------------------------------------------------------------------
  3514. function TClipperBase.GetBounds: TRect64;
  3515. var
  3516. i: Integer;
  3517. v, vStart: PVertex;
  3518. begin
  3519. Result := Rect64(MaxInt64, MaxInt64, -MaxInt64, -MaxInt64);
  3520. for i := 0 to FVertexArrayList.Count -1 do
  3521. begin
  3522. vStart := UnsafeGet(FVertexArrayList, i);
  3523. v := vStart;
  3524. repeat
  3525. if v.pt.X < Result.Left then Result.Left := v.pt.X
  3526. else if v.pt.X > Result.Right then Result.Right := v.pt.X;
  3527. if v.pt.Y < Result.Top then Result.Top := v.pt.Y
  3528. else if v.pt.Y > Result.Bottom then Result.Bottom := v.pt.Y;
  3529. v := v.next;
  3530. until v = vStart;
  3531. end;
  3532. if Result.Left > Result.Right then Result := NullRect64;
  3533. end;
  3534. //------------------------------------------------------------------------------
  3535. // TClipper methods
  3536. //------------------------------------------------------------------------------
  3537. procedure TClipper64.AddReuseableData(const reuseableData: TReuseableDataContainer64);
  3538. begin
  3539. inherited AddReuseableData(reuseableData);
  3540. end;
  3541. //------------------------------------------------------------------------------
  3542. procedure TClipper64.AddSubject(const subject: TPath64);
  3543. begin
  3544. AddPath(subject, ptSubject, false);
  3545. end;
  3546. //------------------------------------------------------------------------------
  3547. procedure TClipper64.AddSubject(const subjects: TPaths64);
  3548. begin
  3549. AddPaths(subjects, ptSubject, false);
  3550. end;
  3551. //------------------------------------------------------------------------------
  3552. procedure TClipper64.AddOpenSubject(const subject: TPath64);
  3553. begin
  3554. AddPath(subject, ptSubject, true);
  3555. end;
  3556. //------------------------------------------------------------------------------
  3557. procedure TClipper64.AddOpenSubject(const subjects: TPaths64);
  3558. begin
  3559. AddPaths(subjects, ptSubject, true);
  3560. end;
  3561. //------------------------------------------------------------------------------
  3562. procedure TClipper64.AddClip(const clip: TPath64);
  3563. begin
  3564. AddPath(clip, ptClip, false);
  3565. end;
  3566. //------------------------------------------------------------------------------
  3567. procedure TClipper64.AddClip(const clips: TPaths64);
  3568. begin
  3569. AddPaths(clips, ptClip, false);
  3570. end;
  3571. //------------------------------------------------------------------------------
  3572. function TClipper64.Execute(clipType: TClipType;
  3573. fillRule: TFillRule; out closedSolutions: TPaths64): Boolean;
  3574. var
  3575. dummy: TPaths64;
  3576. begin
  3577. FUsingPolytree := false;
  3578. closedSolutions := nil;
  3579. try try
  3580. ExecuteInternal(clipType, fillRule, false);
  3581. Result := Succeeded and
  3582. BuildPaths(closedSolutions, dummy);
  3583. except
  3584. Result := false;
  3585. end;
  3586. finally
  3587. if not ClearSolutionOnly then Result := false;
  3588. end;
  3589. end;
  3590. //------------------------------------------------------------------------------
  3591. function TClipper64.Execute(clipType: TClipType; fillRule: TFillRule;
  3592. out closedSolutions, openSolutions: TPaths64): Boolean;
  3593. begin
  3594. closedSolutions := nil;
  3595. openSolutions := nil;
  3596. FUsingPolytree := false;
  3597. try try
  3598. ExecuteInternal(clipType, fillRule, false);
  3599. Result := Succeeded and
  3600. BuildPaths(closedSolutions, openSolutions);
  3601. except
  3602. Result := false;
  3603. end;
  3604. finally
  3605. if not ClearSolutionOnly then Result := false;
  3606. end;
  3607. end;
  3608. //------------------------------------------------------------------------------
  3609. function TClipper64.Execute(clipType: TClipType; fillRule: TFillRule;
  3610. var solutionTree: TPolyTree64; out openSolutions: TPaths64): Boolean;
  3611. begin
  3612. if not assigned(solutionTree) then
  3613. Raise EClipper2LibException(rsClipper_PolyTreeErr);
  3614. solutionTree.Clear;
  3615. FUsingPolytree := true;
  3616. openSolutions := nil;
  3617. try try
  3618. ExecuteInternal(clipType, fillRule, true);
  3619. Result := Succeeded and
  3620. BuildTree(solutionTree, openSolutions);
  3621. except
  3622. Result := false;
  3623. end;
  3624. finally
  3625. if not ClearSolutionOnly then Result := false;
  3626. end;
  3627. end;
  3628. //------------------------------------------------------------------------------
  3629. // TPolyPathBase methods
  3630. //------------------------------------------------------------------------------
  3631. constructor TPolyPathBase.Create;
  3632. begin
  3633. FChildList := TList.Create;
  3634. end;
  3635. //------------------------------------------------------------------------------
  3636. destructor TPolyPathBase.Destroy;
  3637. begin
  3638. Clear;
  3639. FChildList.Free;
  3640. inherited Destroy;
  3641. end;
  3642. //------------------------------------------------------------------------------
  3643. type
  3644. PPolyPathBase = ^TPolyPathBase;
  3645. procedure TPolyPathBase.Clear;
  3646. var
  3647. i: integer;
  3648. ppb: PPolyPathBase;
  3649. begin
  3650. if FChildList.Count = 0 then Exit;
  3651. ppb := @FChildList.List[0];
  3652. for i := 0 to FChildList.Count -1 do
  3653. begin
  3654. ppb^.Free;
  3655. inc(ppb);
  3656. end;
  3657. FChildList.Clear;
  3658. end;
  3659. //------------------------------------------------------------------------------
  3660. function TPolyPathBase.GetChild(index: Integer): TPolyPathBase;
  3661. begin
  3662. if (index < 0) or (index >= FChildList.Count) then
  3663. Result := nil else
  3664. Result := FChildList[index];
  3665. end;
  3666. //------------------------------------------------------------------------------
  3667. function TPolyPathBase.GetLevel: Integer;
  3668. var
  3669. pp: TPolyPathBase;
  3670. begin
  3671. Result := 0;
  3672. pp := Parent;
  3673. while Assigned(pp) do
  3674. begin
  3675. inc(Result);
  3676. pp := pp.Parent;
  3677. end;
  3678. end;
  3679. //------------------------------------------------------------------------------
  3680. function TPolyPathBase.GetIsHole: Boolean;
  3681. begin
  3682. Result := Iif(Assigned(Parent), not Odd(GetLevel), false);
  3683. end;
  3684. //------------------------------------------------------------------------------
  3685. function TPolyPathBase.GetChildCnt: Integer;
  3686. begin
  3687. Result := FChildList.Count;
  3688. end;
  3689. //------------------------------------------------------------------------------
  3690. //TPolyPath method
  3691. //------------------------------------------------------------------------------
  3692. function TPolyPath64.AddChild(const path: TPath64): TPolyPathBase;
  3693. begin
  3694. Result := TPolyPath64.Create;
  3695. Result.Parent := self;
  3696. TPolyPath64(Result).FPath := path;;
  3697. ChildList.Add(Result);
  3698. end;
  3699. //------------------------------------------------------------------------------
  3700. function TPolyPath64.GetChild64(index: Integer): TPolyPath64;
  3701. begin
  3702. Result := TPolyPath64(GetChild(index));
  3703. end;
  3704. //------------------------------------------------------------------------------
  3705. // TClipperD methods
  3706. //------------------------------------------------------------------------------
  3707. constructor TClipperD.Create(precision: integer);
  3708. begin
  3709. inherited Create;
  3710. CheckPrecisionRange(precision);
  3711. FScale := Math.Power(10, precision);
  3712. FInvScale := 1/FScale;
  3713. end;
  3714. //------------------------------------------------------------------------------
  3715. {$IFDEF USINGZ}
  3716. procedure TClipperD.CheckCallback;
  3717. begin
  3718. // only when the user defined ZCallback function has been assigned
  3719. // do we assign the proxy callback ZCB to ClipperBase
  3720. if Assigned(ZCallback) then
  3721. inherited ZCallback := ZCB else
  3722. inherited ZCallback := nil;
  3723. end;
  3724. //------------------------------------------------------------------------------
  3725. procedure TClipperD.ZCB(const bot1, top1, bot2, top2: TPoint64;
  3726. var intersectPt: TPoint64);
  3727. var
  3728. tmp: TPointD;
  3729. begin
  3730. if not assigned(fZCallback) then Exit;
  3731. // de-scale (x & y)
  3732. // temporarily convert integers to their initial float values
  3733. // this will slow clipping marginally but will make it much easier
  3734. // to understand the coordinates passed to the callback function
  3735. tmp := ScalePoint(intersectPt, FInvScale);
  3736. //do the callback
  3737. fZCallback(
  3738. ScalePoint(bot1, FInvScale),
  3739. ScalePoint(top1, FInvScale),
  3740. ScalePoint(bot2, FInvScale),
  3741. ScalePoint(top2, FInvScale), tmp);
  3742. intersectPt.Z := tmp.Z;
  3743. end;
  3744. //------------------------------------------------------------------------------
  3745. {$ENDIF}
  3746. procedure TClipperD.AddSubject(const pathD: TPathD);
  3747. var
  3748. p: TPath64;
  3749. begin
  3750. if FScale = 0 then FScale := DefaultClipperDScale;
  3751. p := ScalePath(pathD, FScale);
  3752. AddPath(p, ptSubject, false);
  3753. end;
  3754. //------------------------------------------------------------------------------
  3755. procedure TClipperD.AddSubject(const pathsD: TPathsD);
  3756. var
  3757. pp: TPaths64;
  3758. begin
  3759. if FScale = 0 then FScale := DefaultClipperDScale;
  3760. pp := ScalePaths(pathsD, FScale);
  3761. AddPaths(pp, ptSubject, false);
  3762. end;
  3763. //------------------------------------------------------------------------------
  3764. procedure TClipperD.AddOpenSubject(const pathD: TPathD);
  3765. var
  3766. p: TPath64;
  3767. begin
  3768. if FScale = 0 then FScale := DefaultClipperDScale;
  3769. p := ScalePath(pathD, FScale);
  3770. AddPath(p, ptSubject, true);
  3771. end;
  3772. //------------------------------------------------------------------------------
  3773. procedure TClipperD.AddOpenSubject(const pathsD: TPathsD);
  3774. var
  3775. pp: TPaths64;
  3776. begin
  3777. if FScale = 0 then FScale := DefaultClipperDScale;
  3778. pp := ScalePaths(pathsD, FScale);
  3779. AddPaths(pp, ptSubject, true);
  3780. end;
  3781. //------------------------------------------------------------------------------
  3782. procedure TClipperD.AddClip(const pathD: TPathD);
  3783. var
  3784. p: TPath64;
  3785. begin
  3786. if FScale = 0 then FScale := DefaultClipperDScale;
  3787. p := ScalePath(pathD, FScale);
  3788. AddPath(p, ptClip, false);
  3789. end;
  3790. //------------------------------------------------------------------------------
  3791. procedure TClipperD.AddClip(const pathsD: TPathsD);
  3792. var
  3793. pp: TPaths64;
  3794. begin
  3795. if FScale = 0 then FScale := DefaultClipperDScale;
  3796. pp := ScalePaths(pathsD, FScale);
  3797. AddPaths(pp, ptClip, false);
  3798. end;
  3799. //------------------------------------------------------------------------------
  3800. function TClipperD.Execute(clipType: TClipType; fillRule: TFillRule;
  3801. out closedSolutions: TPathsD): Boolean;
  3802. var
  3803. dummy: TPathsD;
  3804. begin
  3805. Result := Execute(clipType, fillRule, closedSolutions, dummy);
  3806. end;
  3807. //------------------------------------------------------------------------------
  3808. function TClipperD.Execute(clipType: TClipType; fillRule: TFillRule;
  3809. out closedSolutions, openSolutions: TPathsD): Boolean;
  3810. var
  3811. solClosed, solOpen: TPaths64;
  3812. begin
  3813. {$IFDEF USINGZ}
  3814. CheckCallback;
  3815. {$ENDIF}
  3816. closedSolutions := nil;
  3817. openSolutions := nil;
  3818. try try
  3819. ExecuteInternal(clipType, fillRule, false);
  3820. Result := BuildPaths(solClosed, solOpen);
  3821. if not Result then Exit;
  3822. closedSolutions := ScalePathsD(solClosed, FInvScale);
  3823. openSolutions := ScalePathsD(solOpen, FInvScale);
  3824. except
  3825. Result := false;
  3826. end;
  3827. finally
  3828. if not ClearSolutionOnly then Result := false;
  3829. end;
  3830. end;
  3831. //------------------------------------------------------------------------------
  3832. function TClipperD.Execute(clipType: TClipType; fillRule: TFillRule;
  3833. var solutionsTree: TPolyTreeD; out openSolutions: TPathsD): Boolean;
  3834. var
  3835. open_Paths: TPaths64;
  3836. begin
  3837. if not assigned(solutionsTree) then
  3838. Raise EClipper2LibException(rsClipper_PolyTreeErr);
  3839. {$IFDEF USINGZ}
  3840. CheckCallback;
  3841. {$ENDIF}
  3842. solutionsTree.Clear;
  3843. FUsingPolytree := true;
  3844. solutionsTree.SetScale(fScale);
  3845. openSolutions := nil;
  3846. try try
  3847. ExecuteInternal(clipType, fillRule, true);
  3848. BuildTree(solutionsTree, open_Paths);
  3849. openSolutions := ScalePathsD(open_Paths, FInvScale);
  3850. Result := true;
  3851. except
  3852. Result := false;
  3853. end;
  3854. finally
  3855. if not ClearSolutionOnly then Result := false;
  3856. end;
  3857. end;
  3858. //------------------------------------------------------------------------------
  3859. // TPolyPathD methods
  3860. //------------------------------------------------------------------------------
  3861. function TPolyPathD.AddChild(const path: TPath64): TPolyPathBase;
  3862. begin
  3863. Result := TPolyPathD.Create;
  3864. Result.Parent := self;
  3865. TPolyPathD(Result).fScale := fScale;
  3866. TPolyPathD(Result).FPath := ScalePathD(path, 1/FScale);
  3867. ChildList.Add(Result);
  3868. end;
  3869. //------------------------------------------------------------------------------
  3870. function TPolyPathD.AddChild(const path: TPathD): TPolyPathBase;
  3871. begin
  3872. Result := TPolyPathD.Create;
  3873. Result.Parent := self;
  3874. TPolyPathD(Result).fScale := fScale;
  3875. TPolyPathD(Result).FPath := path;
  3876. ChildList.Add(Result);
  3877. end;
  3878. //------------------------------------------------------------------------------
  3879. function TPolyPathD.GetChildD(index: Integer): TPolyPathD;
  3880. begin
  3881. Result := TPolyPathD(GetChild(index));
  3882. end;
  3883. //------------------------------------------------------------------------------
  3884. // TPolyTreeD
  3885. //------------------------------------------------------------------------------
  3886. procedure TPolyTreeD.SetScale(value: double);
  3887. begin
  3888. FScale := value;
  3889. end;
  3890. //------------------------------------------------------------------------------
  3891. end.