Clipper.Engine.pas 131 KB

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