Clipper.Engine.pas 123 KB

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