GR32_Clipper1.pas 99 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389
  1. unit GR32_Clipper1 deprecated 'Use GR32_Clipper2';
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is GR32_Clipper
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Angus Johnson
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2012-2019
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. {$IFDEF FPC}
  34. {$DEFINE USEINLINING}
  35. {$ELSE}
  36. {$IF CompilerVersion >= 18} // Delphi 2007
  37. // While USEINLINING has been supported since D2005, both D2005 and D2006
  38. // have an Inline codegen bug (QC41166) so ignore Inline until D2007.
  39. {$DEFINE USEINLINING}
  40. {$IF CompilerVersion >= 25.0} // Delphi XE4+
  41. {$LEGACYIFEND ON}
  42. {$IFEND}
  43. {$IFEND}
  44. {$IF CompilerVersion < 14}
  45. Requires Delphi version 6 or above.
  46. {$IFEND}
  47. {$ENDIF}
  48. {$IFDEF DEBUG}
  49. {$UNDEF USEINLINING}
  50. {$ENDIF}
  51. interface
  52. uses
  53. Classes, SysUtils, Math, GR32;
  54. type
  55. TPoint64 = record X, Y: Int64; end;
  56. // TPath: a simple data structure to represent a series of vertices, whether
  57. // open (poly-line) or closed (polygon). A path may be simple or complex (self
  58. // intersecting). For simple polygons, path orientation (whether clockwise or
  59. // counterclockwise) is generally used to differentiate outer paths from inner
  60. // paths (holes). For complex polygons (and also for overlapping polygons),
  61. // explicit 'filling rules' (see below) are used to indicate regions that are
  62. // inside (filled) and regions that are outside (unfilled) a specific polygon.
  63. TPath = array of TPoint64;
  64. TPaths = array of TPath;
  65. TArrayOfPaths = array of TPaths;
  66. TClipType = (ctNone, ctIntersection, ctUnion, ctDifference, ctXor);
  67. // Note: all clipping operations except for Difference are commutative.
  68. TPathType = (ptSubject, ptClip);
  69. TFillRule = (frEvenOdd, frNonZero, frPositive, frNegative);
  70. EClipperLibException = class(Exception);
  71. TJoinType = (jtSquare, jtRound, jtRoundEx, jtMiter);
  72. TEndType = (etPolygon, etOpenJoined, etOpenButt, etOpenSquare, etOpenRound);
  73. TVertexFlag = (vfOpenStart, vfOpenEnd, vfLocMax, vfLocMin);
  74. TVertexFlags = set of TVertexFlag;
  75. PVertex = ^TVertex;
  76. TVertex = record
  77. Pt : TPoint64;
  78. next : PVertex;
  79. prev : PVertex;
  80. flags : TVertexFlags;
  81. end;
  82. PVertexArray = ^TVertexArray;
  83. TVertexArray = array[0..MaxInt div sizeof(TVertex) -1] of TVertex;
  84. // Every closed path (or polygon) is made up of a series of vertices forming
  85. // edges that alternate between going up (relative to the Y-axis) and going
  86. // down. Edges consecutively going up or consecutively going down are called
  87. // 'bounds' (or sides if they're simple polygons). 'Local Minima' refer to
  88. // vertices where descending bounds become ascending ones.
  89. PLocalMinima = ^TLocalMinima;
  90. TLocalMinima = record
  91. vertex : PVertex;
  92. PolyType : TPathType;
  93. IsOpen : Boolean;
  94. end;
  95. TOutRec = class;
  96. TOutPt = class
  97. Pt : TPoint64;
  98. Next : TOutPt;
  99. Prev : TOutPt;
  100. OutRec : TOutRec; // used in descendant classes
  101. end;
  102. PActive = ^TActive;
  103. TActive = record
  104. op : TOutPt; // used in descendant classes
  105. Bot : TPoint64;
  106. Top : TPoint64;
  107. CurrX : Int64;
  108. Dx : Double; // inverse of edge slope (zero = vertical)
  109. WindDx : Integer; // wind direction (ascending: +1; descending: -1)
  110. WindCnt : Integer; // current wind count
  111. WindCnt2 : Integer; // current wind count of the opposite TPolyType
  112. OutRec : TOutRec;
  113. // AEL: 'active edge list' (Vatti's AET - active edge table)
  114. // a linked list of all edges (from left to right) that are present
  115. // (or 'active') within the current scanbeam (a horizontal 'beam' that
  116. // sweeps from bottom to top over the paths in the clipping operation).
  117. PrevInAEL: PActive;
  118. NextInAEL: PActive;
  119. // SEL: 'sorted edge list' (Vatti's ST - sorted table)
  120. // linked list used when sorting edges into their new positions at the
  121. // top of scanbeams, but also (re)used to process horizontals.
  122. PrevInSEL: PActive;
  123. NextInSEL: PActive;
  124. Jump : PActive; // for merge sorting (see BuildIntersectList())
  125. VertTop : PVertex;
  126. LocMin : PLocalMinima; // the bottom of an edge 'bound' (also Vatti)
  127. end;
  128. PIntersectNode = ^TIntersectNode;
  129. TIntersectNode = record
  130. Edge1 : PActive;
  131. Edge2 : PActive;
  132. Pt : TPoint64;
  133. end;
  134. PScanLine = ^TScanLine;
  135. TScanLine = record
  136. Y : Int64;
  137. Next : PScanLine;
  138. end;
  139. TOutRecState = (osUndefined, osOpen, osOuter,
  140. osOuterCheck, osInner, osInnerCheck);
  141. // OutRec: contains a path in the clipping solution. Edges in the AEL will
  142. // have OutRec pointers assigned when they form part of the clipping solution.
  143. TOutRec = class
  144. Idx : Integer;
  145. Owner : TOutRec;
  146. frontE : PActive;
  147. backE : PActive;
  148. Pts : TOutPt;
  149. State : TOutRecState;
  150. end;
  151. TClipper = class
  152. private
  153. FBotY : Int64;
  154. FScanLine : PScanLine;
  155. FLocMinListSorted : Boolean;
  156. FHasOpenPaths : Boolean;
  157. FCurrentLocMinIdx : Integer;
  158. FClipType : TClipType;
  159. FFillRule : TFillRule;
  160. FIntersectList : TList;
  161. FOutRecList : TList;
  162. FLocMinList : TList;
  163. FActives : PActive; // see AEL above
  164. FSel : PActive; // see SEL above
  165. FVertexList : TList;
  166. procedure Reset;
  167. procedure InsertScanLine(const Y: Int64);
  168. function PopScanLine(out Y: Int64): Boolean;
  169. function PopLocalMinima(Y: Int64;
  170. out localMinima: PLocalMinima): Boolean;
  171. procedure DisposeScanLineList;
  172. procedure DisposeOutRec(index: Integer);
  173. procedure DisposeAllOutRecs;
  174. procedure DisposeVerticesAndLocalMinima;
  175. procedure AddPathToVertexList(const path: TArrayOfFloatPoint;
  176. polyType: TPathType; isOpen: Boolean);
  177. function IsContributingClosed(e: PActive): Boolean;
  178. function IsContributingOpen(e: PActive): Boolean;
  179. procedure SetWindCountForClosedPathEdge(e: PActive);
  180. procedure SetWindCountForOpenPathEdge(e: PActive);
  181. procedure InsertLocalMinimaIntoAEL(const botY: Int64);
  182. procedure InsertLeftEdge(e: PActive);
  183. procedure PushHorz(e: PActive); {$IFDEF USEINLINING} inline; {$ENDIF}
  184. function PopHorz(out e: PActive): Boolean;
  185. {$IFDEF USEINLINING} inline; {$ENDIF}
  186. procedure StartOpenPath(e: PActive; const pt: TPoint64);
  187. procedure UpdateEdgeIntoAEL(var e: PActive);
  188. procedure IntersectEdges(e1, e2: PActive;
  189. const pt: TPoint64; orientationCheckRequired: Boolean = false);
  190. procedure DeleteFromAEL(e: PActive);
  191. procedure AdjustCurrXAndCopyToSEL(topY: Int64);
  192. procedure DoIntersections(const topY: Int64);
  193. procedure DisposeIntersectNodes;
  194. procedure AddNewIntersectNode(e1, e2: PActive; topY: Int64);
  195. function BuildIntersectList(const topY: Int64): Boolean;
  196. procedure ProcessIntersectList;
  197. procedure SwapPositionsInAEL(e1, e2: PActive);
  198. procedure DoHorizontal(horzEdge: PActive);
  199. procedure DoTopOfScanbeam(Y: Int64);
  200. function DoMaxima(e: PActive): PActive;
  201. function AddOutPt(e: PActive; const pt: TPoint64): TOutPt;
  202. procedure AddLocalMinPoly(e1, e2: PActive; const pt: TPoint64;
  203. IsNew: Boolean = false; orientationCheckRequired: Boolean = false);
  204. procedure AddLocalMaxPoly(e1, e2: PActive; const pt: TPoint64);
  205. procedure JoinOutrecPaths(e1, e2: PActive);
  206. function GetIntersectNode(index: Integer): PIntersectNode;
  207. {$IFDEF USEINLINING} inline; {$ENDIF}
  208. protected
  209. procedure CleanUp; // unlike Clear, CleanUp preserves added paths
  210. procedure ExecuteInternal(clipType: TClipType;
  211. fillRule: TFillRule); virtual;
  212. function BuildResult(out closedPaths,
  213. openPaths: TArrayOfArrayOfFloatPoint): Boolean;
  214. property OutRecList: TList read FOutRecList;
  215. property IntersectNode[index: Integer]: PIntersectNode
  216. read GetIntersectNode;
  217. public
  218. constructor Create; virtual;
  219. destructor Destroy; override;
  220. procedure Clear;
  221. function GetBounds: TFloatRect;
  222. // ADDPATH & ADDPATHS METHODS ...
  223. procedure AddPath(const path64: TArrayOfFloatPoint;
  224. polyType: TPathType = ptSubject; isOpen: Boolean = false); overload;
  225. procedure AddPath(const path: TArrayOfFixedPoint;
  226. polyType: TPathType = ptSubject; isOpen: Boolean = false); overload;
  227. procedure AddPaths(const paths64: TArrayOfArrayOfFloatPoint;
  228. polyType: TPathType = ptSubject;
  229. isOpen: Boolean = false); overload;
  230. procedure AddPaths(const paths: TArrayOfArrayOfFixedPoint;
  231. polyType: TPathType = ptSubject;
  232. isOpen: Boolean = false); overload;
  233. // EXECUTE METHODS ...
  234. function Execute(clipType: TClipType; fillRule: TFillRule;
  235. out closedPaths: TArrayOfArrayOfFloatPoint): Boolean; overload;
  236. function Execute(clipType: TClipType; fillRule: TFillRule;
  237. out closedPaths: TArrayOfArrayOfFixedPoint): Boolean; overload;
  238. function Execute(clipType: TClipType; fillRule: TFillRule;
  239. out closedPaths, openPaths: TArrayOfArrayOfFloatPoint): Boolean; overload;
  240. function Execute(clipType: TClipType; fillRule: TFillRule;
  241. out closedPaths, openPaths: TArrayOfArrayOfFixedPoint): Boolean; overload;
  242. end;
  243. TClipperOffset = class
  244. private
  245. FDelta: Double;
  246. FJoinType: TJoinType;
  247. FEndType : TEndType;
  248. FStepSizeSin, FStepSizeCos: Extended;
  249. FMiterLim, FMiterLimit: Double;
  250. FStepsPerRad: Double;
  251. FArcTolerance: Double;
  252. FNorms: TArrayOfFloatPoint;
  253. FSolution: TArrayOfArrayOfFloatPoint;
  254. FSolutionLen: integer;
  255. FPathsIn: TArrayOfArrayOfFloatPoint;
  256. FPathIn: TArrayOfFloatPoint;
  257. FPathOut: TArrayOfFloatPoint;
  258. FPathOutLen: Integer;
  259. procedure AddPoint(const pt: TFloatPoint);
  260. procedure DoSquare(j, k: Integer);
  261. procedure DoMiter(j, k: Integer; cosAplus1: Double);
  262. procedure DoRound(j, k: Integer);
  263. procedure OffsetPoint(j,k: Integer);
  264. function CheckPaths: boolean;
  265. function GetLowestPolygonIdx: integer;
  266. procedure OffsetPaths;
  267. procedure BuildNormals;
  268. procedure ReverseNormals;
  269. procedure OffsetPolygon;
  270. procedure OffsetOpenJoined;
  271. procedure OffsetOpenPath;
  272. public
  273. constructor Create(MiterLimit: Double = 2.0; ArcTolerance: Double = 0.0);
  274. destructor Destroy; override;
  275. procedure AddPath(const path: TArrayOfFloatPoint);
  276. procedure AddPaths(const paths: TArrayOfArrayOfFloatPoint);
  277. procedure Clear;
  278. procedure Execute(delta: Double; jt: TJoinType; et: TEndType;
  279. out solution: TArrayOfArrayOfFloatPoint);
  280. property MiterLimit: Double read FMiterLimit write FMiterLimit;
  281. property ArcTolerance: Double read FArcTolerance write FArcTolerance;
  282. end;
  283. function InflatePaths(const paths: TArrayOfArrayOfFloatPoint;
  284. delta: Double; jt: TJoinType; et: TEndType;
  285. miterLimit: single = 0): TArrayOfArrayOfFloatPoint;
  286. implementation
  287. const
  288. Tolerance : Double = 1.0E-15;
  289. DefaultArcFrac : Double = 0.02;
  290. Two_Pi : Double = 2 * PI;
  291. LowestIp : TPoint64 = (X: High(Int64); Y: High(Int64));
  292. // OVERFLOWCHECKS OFF is a necessary workaround for a compiler bug that very
  293. // occasionally report incorrect overflow errors in Delphi versions before 10.2.
  294. // see https://forums.embarcadero.com/message.jspa?messageID=871444
  295. {$OVERFLOWCHECKS OFF}
  296. resourcestring
  297. rsClipper_OpenPathErr = 'Only subject paths can be open.';
  298. rsClipper_ClippingErr = 'Undefined clipping error';
  299. //------------------------------------------------------------------------------
  300. // Miscellaneous Functions ...
  301. //------------------------------------------------------------------------------
  302. function Point64(const fp: TFloatPoint): TPoint64; overload;
  303. {$IFDEF USEINLINING} inline; {$ENDIF}
  304. begin
  305. Result.X := Round(fp.X * FixedOne);
  306. Result.Y := Round(fp.Y * FixedOne);
  307. end;
  308. //------------------------------------------------------------------------------
  309. function Point64(const X, Y: Int64): TPoint64; overload;
  310. begin
  311. Result.X := X;
  312. Result.Y := Y;
  313. end;
  314. //------------------------------------------------------------------------------
  315. function FloatPoint(const pt: TPoint64): TFloatPoint; overload;
  316. {$IFDEF USEINLINING} inline; {$ENDIF}
  317. begin
  318. Result.X := pt.X * FixedToFloat;
  319. Result.Y := pt.Y * FixedToFloat;
  320. end;
  321. //------------------------------------------------------------------------------
  322. function FixedToFloat(const fixed: TArrayOfFixedPoint): TArrayOfFloatPoint;
  323. var
  324. i, len: Integer;
  325. begin
  326. len := length(fixed);
  327. setLength(Result, len);
  328. for i := 0 to len -1 do
  329. Result[i] := FloatPoint(fixed[i]);
  330. end;
  331. //------------------------------------------------------------------------------
  332. function FloatToFixed(const float: TArrayOfFloatPoint):
  333. TArrayOfFixedPoint; overload;
  334. var
  335. i, len: Integer;
  336. begin
  337. len := length(float);
  338. setLength(Result, len);
  339. for i := 0 to len -1 do
  340. Result[i] := FixedPoint(float[i]);
  341. end;
  342. //------------------------------------------------------------------------------
  343. function FloatToFixed(const float: TArrayOfArrayOfFloatPoint):
  344. TArrayOfArrayOfFixedPoint; overload;
  345. var
  346. i, len: Integer;
  347. begin
  348. len := length(float);
  349. setLength(Result, len);
  350. for i := 0 to len -1 do
  351. Result[i] := FloatToFixed(float[i]);
  352. end;
  353. //------------------------------------------------------------------------------
  354. function PointsEqual(const p1, p2: TPoint64): Boolean; overload;
  355. {$IFDEF USEINLINING} inline; {$ENDIF}
  356. begin
  357. Result := (p1.X = p2.X) and (p1.Y = p2.Y);
  358. end;
  359. //------------------------------------------------------------------------------
  360. function PointsEqual(const p1, p2: TFloatPoint): Boolean; overload;
  361. {$IFDEF USEINLINING} inline; {$ENDIF}
  362. begin
  363. Result := (p1.X = p2.X) and (p1.Y = p2.Y);
  364. end;
  365. //------------------------------------------------------------------------------
  366. function IsOpen(e: PActive): Boolean; overload;
  367. {$IFDEF USEINLINING} inline; {$ENDIF}
  368. begin
  369. Result := e.LocMin.IsOpen;
  370. end;
  371. //------------------------------------------------------------------------------
  372. function IsOpen(outrec: TOutRec): Boolean; overload;
  373. {$IFDEF USEINLINING} inline; {$ENDIF}
  374. begin
  375. Result := outrec.State = osOpen;
  376. end;
  377. //------------------------------------------------------------------------------
  378. function IsOuter(outrec: TOutRec): Boolean;
  379. {$IFDEF USEINLINING} inline; {$ENDIF}
  380. begin
  381. Result := outrec.State in [osOuter, osOuterCheck];
  382. end;
  383. //------------------------------------------------------------------------------
  384. procedure SetAsOuter(outrec: TOutRec); {$IFDEF USEINLINING} inline; {$ENDIF}
  385. begin
  386. outrec.State := osOuter;
  387. end;
  388. //------------------------------------------------------------------------------
  389. function IsInner(outrec: TOutRec): Boolean;
  390. {$IFDEF USEINLINING} inline; {$ENDIF}
  391. begin
  392. Result := outrec.State in [osInner, osInnerCheck];
  393. end;
  394. //------------------------------------------------------------------------------
  395. procedure SetAsInner(outrec: TOutRec); {$IFDEF USEINLINING} inline; {$ENDIF}
  396. begin
  397. outrec.State := osInner;
  398. end;
  399. //------------------------------------------------------------------------------
  400. procedure SetCheckFlag(outrec: TOutRec); {$IFDEF USEINLINING} inline; {$ENDIF}
  401. begin
  402. if outrec.State = osInner then
  403. outrec.State := osInnerCheck
  404. else if outrec.State = osOuter then
  405. outrec.State := osOuterCheck;
  406. end;
  407. //------------------------------------------------------------------------------
  408. procedure UnsetCheckFlag(outrec: TOutRec); {$IFDEF USEINLINING} inline; {$ENDIF}
  409. begin
  410. if outrec.State = osInnerCheck then outrec.State := osInner
  411. else if outrec.State = osOuterCheck then outrec.State := osOuter;
  412. end;
  413. //------------------------------------------------------------------------------
  414. function IsHotEdge(e: PActive): Boolean; {$IFDEF USEINLINING} inline; {$ENDIF}
  415. begin
  416. Result := assigned(e.OutRec);
  417. end;
  418. //------------------------------------------------------------------------------
  419. function GetPrevHotEdge(e: PActive): PActive;
  420. {$IFDEF USEINLINING} inline; {$ENDIF}
  421. begin
  422. Result := e.PrevInAEL;
  423. while assigned(Result) and (IsOpen(Result) or not IsHotEdge(Result)) do
  424. Result := Result.PrevInAEL;
  425. end;
  426. //------------------------------------------------------------------------------
  427. function IsFront(e: PActive): Boolean; {$IFDEF USEINLINING} inline; {$ENDIF}
  428. begin
  429. // the front edge will be the LEFT edge when it's an OUTER polygon
  430. // so that outer polygons will be orientated clockwise
  431. Result := (e = e.OutRec.frontE);
  432. end;
  433. //------------------------------------------------------------------------------
  434. function IsInvalidPath(op: TOutPt): Boolean;
  435. {$IFDEF USEINLINING} inline; {$ENDIF}
  436. begin
  437. Result := not assigned(op) or (op.Next = op);
  438. end;
  439. //------------------------------------------------------------------------------
  440. (*******************************************************************************
  441. * Dx: 0(90deg) *
  442. * | *
  443. * +inf (180deg) <--- o ---> -inf (0deg) *
  444. *******************************************************************************)
  445. function GetDx(const pt1, pt2: TPoint64): Double;
  446. {$IFDEF USEINLINING} inline; {$ENDIF}
  447. var
  448. dy: Int64;
  449. begin
  450. dy := (pt2.Y - pt1.Y);
  451. if dy <> 0 then Result := (pt2.X - pt1.X) / dy
  452. else if (pt2.X > pt1.X) then Result := NegInfinity
  453. else Result := Infinity;
  454. end;
  455. //------------------------------------------------------------------------------
  456. function TopX(e: PActive; const currentY: Int64): Int64; overload;
  457. {$IFDEF USEINLINING} inline; {$ENDIF}
  458. begin
  459. if (currentY = e.Top.Y) or (e.Top.X = e.Bot.X) then Result := e.Top.X
  460. else Result := e.Bot.X + Round(e.Dx*(currentY - e.Bot.Y));
  461. end;
  462. //------------------------------------------------------------------------------
  463. function TopX(const pt1, pt2: TPoint64; const Y: Int64): Int64; overload;
  464. {$IFDEF USEINLINING} inline; {$ENDIF}
  465. var
  466. dx: Double;
  467. begin
  468. if (Y = pt1.Y) then Result := pt1.X
  469. else if (Y = pt2.Y) then Result := pt2.X
  470. else if (pt1.Y = pt2.Y) or (pt1.X = pt2.X) then Result := pt2.X
  471. else
  472. begin
  473. dx := GetDx(pt1, pt2);
  474. Result := pt1.X + Round(dx * (Y - pt1.Y));
  475. end;
  476. end;
  477. //------------------------------------------------------------------------------
  478. function IsHorizontal(e: PActive): Boolean;
  479. {$IFDEF USEINLINING} inline; {$ENDIF}
  480. begin
  481. Result := (e.Top.Y = e.Bot.Y);
  482. end;
  483. //------------------------------------------------------------------------------
  484. function IsHeadingRightHorz(e: PActive): Boolean;
  485. {$IFDEF USEINLINING} inline; {$ENDIF}
  486. begin
  487. Result := (e.Dx = NegInfinity);
  488. end;
  489. //------------------------------------------------------------------------------
  490. function IsHeadingLeftHorz(e: PActive): Boolean;
  491. {$IFDEF USEINLINING} inline; {$ENDIF}
  492. begin
  493. Result := (e.Dx = Infinity);
  494. end;
  495. //------------------------------------------------------------------------------
  496. procedure SwapActives(var e1, e2: PActive);
  497. {$IFDEF USEINLINING} inline; {$ENDIF}
  498. var
  499. e: PActive;
  500. begin
  501. e := e1; e1 := e2; e2 := e;
  502. end;
  503. //------------------------------------------------------------------------------
  504. function GetPolyType(const e: PActive): TPathType;
  505. {$IFDEF USEINLINING} inline; {$ENDIF}
  506. begin
  507. Result := e.LocMin.PolyType;
  508. end;
  509. //------------------------------------------------------------------------------
  510. function IsSamePolyType(const e1, e2: PActive): Boolean;
  511. {$IFDEF USEINLINING} inline; {$ENDIF}
  512. begin
  513. Result := e1.LocMin.PolyType = e2.LocMin.PolyType;
  514. end;
  515. //------------------------------------------------------------------------------
  516. function GetIntersectPoint(e1, e2: PActive): TPoint64;
  517. var
  518. b1, b2, m: Double;
  519. begin
  520. if (e1.Dx = e2.Dx) then
  521. begin
  522. Result := e1.Top;
  523. Exit;
  524. end
  525. else if e1.Dx = 0 then
  526. begin
  527. Result.X := e1.Bot.X;
  528. if IsHorizontal(e2) then
  529. Result.Y := e2.Bot.Y
  530. else
  531. begin
  532. with e2^ do b2 := Bot.Y - (Bot.X/Dx);
  533. Result.Y := round(Result.X/e2.Dx + b2);
  534. end;
  535. end
  536. else if e2.Dx = 0 then
  537. begin
  538. Result.X := e2.Bot.X;
  539. if IsHorizontal(e1) then
  540. Result.Y := e1.Bot.Y
  541. else
  542. begin
  543. with e1^ do b1 := Bot.Y - (Bot.X/Dx);
  544. Result.Y := round(Result.X/e1.Dx + b1);
  545. end;
  546. end else
  547. begin
  548. with e1^ do b1 := Bot.X - Bot.Y * Dx;
  549. with e2^ do b2 := Bot.X - Bot.Y * Dx;
  550. m := (b2-b1)/(e1.Dx - e2.Dx);
  551. Result.Y := round(m);
  552. if Abs(e1.Dx) < Abs(e2.Dx) then
  553. Result.X := round(e1.Dx * m + b1) else
  554. Result.X := round(e2.Dx * m + b2);
  555. end;
  556. end;
  557. //------------------------------------------------------------------------------
  558. procedure SetDx(e: PActive); {$IFDEF USEINLINING} inline; {$ENDIF}
  559. begin
  560. e.Dx := GetDx(e.Bot, e.Top);
  561. end;
  562. //------------------------------------------------------------------------------
  563. function IsLeftBound(e: PActive): Boolean; {$IFDEF USEINLINING} inline; {$ENDIF}
  564. begin
  565. Result := e.WindDx > 0;
  566. end;
  567. //------------------------------------------------------------------------------
  568. function NextVertex(e: PActive): PVertex; overload;
  569. {$IFDEF USEINLINING} inline; {$ENDIF}
  570. begin
  571. if IsLeftBound(e) then
  572. Result := e.vertTop.next else
  573. Result := e.vertTop.prev;
  574. end;
  575. //------------------------------------------------------------------------------
  576. function NextVertex(op: PVertex; goingFwd: Boolean): PVertex; overload;
  577. {$IFDEF USEINLINING} inline; {$ENDIF}
  578. begin
  579. if goingFwd then Result := op.next
  580. else Result := op.prev;
  581. end;
  582. //------------------------------------------------------------------------------
  583. function PrevVertex(op: PVertex; goingFwd: Boolean): PVertex;
  584. {$IFDEF USEINLINING} inline; {$ENDIF}
  585. begin
  586. if goingFwd then Result := op.prev
  587. else Result := op.next;
  588. end;
  589. //------------------------------------------------------------------------------
  590. function CrossProduct(const pt1, pt2, pt3: TPoint64): Double;
  591. var
  592. x1,x2,y1,y2: Double;
  593. begin
  594. x1 := pt2.X - pt1.X;
  595. y1 := pt2.Y - pt1.Y;
  596. x2 := pt3.X - pt2.X;
  597. y2 := pt3.Y - pt2.Y;
  598. Result := (x1 * y2 - y1 * x2);
  599. end;
  600. //---------------------------------------------------------------------------
  601. function IsClockwise(vertex: PVertex): Boolean; overload;
  602. {$IFDEF USEINLINING} inline; {$ENDIF}
  603. begin
  604. Result := CrossProduct(vertex.prev.Pt, vertex.Pt, vertex.next.Pt) >= 0;
  605. end;
  606. //----------------------------------------------------------------------
  607. function IsClockwise(op: TOutPt): Boolean; overload;
  608. {$IFDEF USEINLINING} inline; {$ENDIF}
  609. begin
  610. Result := CrossProduct(op.prev.Pt, op.Pt, op.next.Pt) >= 0;
  611. end;
  612. //----------------------------------------------------------------------
  613. function IsMaxima(e: PActive): Boolean; {$IFDEF USEINLINING} inline; {$ENDIF}
  614. begin
  615. Result := vfLocMax in e.vertTop.flags;
  616. end;
  617. //------------------------------------------------------------------------------
  618. procedure TerminateHotOpen(e: PActive);
  619. begin
  620. if e.OutRec.frontE = e then
  621. e.OutRec.frontE := nil else
  622. e.OutRec.backE := nil;
  623. e.OutRec := nil;
  624. end;
  625. //------------------------------------------------------------------------------
  626. function GetMaximaPair(e: PActive): PActive;
  627. begin
  628. if IsHorizontal(e) then
  629. begin
  630. // we can't be sure whether the MaximaPair is on the left or right, so ...
  631. Result := e.PrevInAEL;
  632. while assigned(Result) and (Result.CurrX >= e.Top.X) do
  633. begin
  634. if Result.vertTop = e.vertTop then Exit; // Found!
  635. Result := Result.PrevInAEL;
  636. end;
  637. Result := e.NextInAEL;
  638. while assigned(Result) and (TopX(Result, e.Top.Y) <= e.Top.X) do
  639. begin
  640. if Result.vertTop = e.vertTop then Exit; // Found!
  641. Result := Result.NextInAEL;
  642. end;
  643. end else
  644. begin
  645. Result := e.NextInAEL;
  646. while assigned(Result) do
  647. begin
  648. if Result.vertTop = e.vertTop then Exit; // Found!
  649. Result := Result.NextInAEL;
  650. end;
  651. end;
  652. Result := nil;
  653. end;
  654. //------------------------------------------------------------------------------
  655. function PointCount(pts: TOutPt): Integer; {$IFDEF USEINLINING} inline; {$ENDIF}
  656. var
  657. p: TOutPt;
  658. begin
  659. Result := 0;
  660. if not Assigned(pts) then Exit;
  661. p := pts;
  662. repeat
  663. Inc(Result);
  664. p := p.Next;
  665. until p = pts;
  666. end;
  667. //------------------------------------------------------------------------------
  668. function BuildPath(op: TOutPt): TArrayOfFloatPoint;
  669. var
  670. i,j, opCnt: Integer;
  671. begin
  672. Result := nil;
  673. opCnt := PointCount(op);
  674. if (opCnt < 2) then Exit;
  675. setLength(Result, opCnt);
  676. Result[0] := FloatPoint(op.Pt);
  677. op := op.Next;
  678. j := 1;
  679. for i := 0 to opCnt -2 do
  680. begin
  681. Result[j] := FloatPoint(op.Pt);
  682. if not PointsEqual(Result[j], Result[j-1]) then inc(j);
  683. op := op.Next;
  684. end;
  685. setLength(Result, j);
  686. end;
  687. //------------------------------------------------------------------------------
  688. procedure DisposeOutPt(pp: TOutPt); {$IFDEF USEINLINING} inline; {$ENDIF}
  689. begin
  690. pp.Prev.Next := pp.Next;
  691. pp.Next.Prev := pp.Prev;
  692. pp.Free;
  693. end;
  694. //------------------------------------------------------------------------------
  695. procedure DisposePolyPts(pp: TOutPt); {$IFDEF USEINLINING} inline; {$ENDIF}
  696. var
  697. tmpPp: TOutPt;
  698. begin
  699. pp.Prev.Next := nil;
  700. while Assigned(pp) do
  701. begin
  702. tmpPp := pp;
  703. pp := pp.Next;
  704. tmpPp.Free;
  705. end;
  706. end;
  707. //------------------------------------------------------------------------------
  708. function LocMinListSort(item1, item2: Pointer): Integer;
  709. var
  710. dy: Int64;
  711. begin
  712. dy := PLocalMinima(item2).vertex.Pt.Y - PLocalMinima(item1).vertex.Pt.Y;
  713. if dy < 0 then Result := -1
  714. else if dy > 0 then Result := 1
  715. else Result := 0;
  716. end;
  717. //------------------------------------------------------------------------------
  718. procedure SetSides(outRec: TOutRec; startEdge, endEdge: PActive);
  719. {$IFDEF USEINLINING} inline; {$ENDIF}
  720. begin
  721. outRec.frontE := startEdge;
  722. outRec.backE := endEdge;
  723. end;
  724. //------------------------------------------------------------------------------
  725. procedure SwapOutRecs(e1, e2: PActive);
  726. var
  727. or1, or2: TOutRec;
  728. e: PActive;
  729. begin
  730. or1 := e1.OutRec;
  731. or2 := e2.OutRec;
  732. if (or1 = or2) then
  733. begin
  734. e := or1.frontE;
  735. or1.frontE := or1.backE;
  736. or1.backE := e;
  737. Exit;
  738. end;
  739. if assigned(or1) then
  740. begin
  741. if e1 = or1.frontE then
  742. or1.frontE := e2 else
  743. or1.backE := e2;
  744. end;
  745. if assigned(or2) then
  746. begin
  747. if e2 = or2.frontE then
  748. or2.frontE := e1 else
  749. or2.backE := e1;
  750. end;
  751. e1.OutRec := or2;
  752. e2.OutRec := or1;
  753. end;
  754. //------------------------------------------------------------------------------
  755. function Area(const path: TArrayOfFloatPoint): Double; overload;
  756. var
  757. i, j, highI: Integer;
  758. d: Double;
  759. begin
  760. Result := 0.0;
  761. highI := High(path);
  762. if (highI < 2) then Exit;
  763. j := highI;
  764. for i := 0 to highI do
  765. begin
  766. d := (path[j].X + path[i].X);
  767. Result := Result + d * (path[j].Y - path[i].Y);
  768. j := i;
  769. end;
  770. Result := -Result * 0.5;
  771. end;
  772. //------------------------------------------------------------------------------
  773. function Area(op: TOutPt): Double; overload;
  774. var
  775. op2: TOutPt;
  776. d: Double;
  777. begin
  778. // positive results are clockwise
  779. Result := 0;
  780. op2 := op;
  781. if Assigned(op2) then
  782. repeat
  783. d := op2.Prev.Pt.X + op2.Pt.X;
  784. Result := Result + d * (op2.Prev.Pt.Y - op2.Pt.Y);
  785. op2 := op2.Next;
  786. until op2 = op;
  787. Result := Result * -0.5;
  788. end;
  789. //------------------------------------------------------------------------------
  790. procedure ReverseOutPts(op: TOutPt);
  791. var
  792. op1, op2: TOutPt;
  793. begin
  794. if not Assigned(op) then Exit;
  795. op1 := op;
  796. repeat
  797. op2:= op1.Next;
  798. op1.Next := op1.Prev;
  799. op1.Prev := op2;
  800. op1 := op2;
  801. until op1 = op;
  802. end;
  803. //------------------------------------------------------------------------------
  804. function RecheckInnerOuter(e: PActive): Boolean;
  805. var
  806. wasOuter, isOuter: Boolean;
  807. e2: PActive;
  808. area: Double;
  809. begin
  810. area := GR32_Clipper.Area(e.outrec.Pts);
  811. Result := area <> 0;
  812. if not Result then Exit; // returns false when area == 0
  813. wasOuter := GR32_Clipper.IsOuter(e.OutRec);
  814. isOuter := true;
  815. e2 := e.PrevInAEL;
  816. while assigned(e2) do
  817. begin
  818. if IsHotEdge(e2) and not IsOpen(e2) then isOuter := not isOuter;
  819. e2 := e2.PrevInAEL;
  820. end;
  821. if isOuter <> wasOuter then
  822. begin
  823. if isOuter then SetAsOuter(e.outrec)
  824. else SetAsInner(e.outrec);
  825. end;
  826. e2 := GetPrevHotEdge(e);
  827. if isOuter then
  828. begin
  829. if assigned(e2) and IsInner(e2.OutRec) then e.OutRec.Owner := e2.OutRec
  830. else e.OutRec.Owner := nil;
  831. end else
  832. begin
  833. if not assigned(e2) then SetAsOuter(e.OutRec)
  834. else if IsInner(e2.OutRec) then e.OutRec.Owner := e2.OutRec.Owner
  835. else e.OutRec.Owner := e2.OutRec;
  836. end;
  837. if (area > 0) <> isOuter then ReverseOutPts(e.outrec.Pts);
  838. UnsetCheckFlag(e.OutRec);
  839. end;
  840. //------------------------------------------------------------------------------
  841. procedure SwapSides(outRec: TOutRec); {$IFDEF USEINLINING} inline; {$ENDIF}
  842. var
  843. e2: PActive;
  844. begin
  845. e2 := outRec.frontE;
  846. outRec.frontE := outRec.backE;
  847. outRec.backE := e2;
  848. outRec.Pts := outRec.Pts.Next;
  849. end;
  850. //------------------------------------------------------------------------------
  851. function FixSides(e: PActive): Boolean;
  852. begin
  853. Result := not RecheckInnerOuter(e) or (IsOuter(e.OutRec) <> IsFront(e));
  854. if Result then SwapSides(e.OutRec);
  855. end;
  856. //------------------------------------------------------------------------------
  857. procedure SetOwnerAndInnerOuterState(e: PActive);
  858. var
  859. e2: PActive;
  860. outRec: TOutRec;
  861. begin
  862. outRec := e.OutRec;
  863. if IsOpen(e) then
  864. begin
  865. outRec.Owner := nil;
  866. outRec.State := osOpen;
  867. Exit;
  868. end;
  869. // set owner ...
  870. if IsHeadingLeftHorz(e) then
  871. begin
  872. e2 := e.NextInAEL; // ie assess state from opposite direction
  873. while assigned(e2) and (not IsHotEdge(e2) or IsOpen(e2)) do
  874. e2 := e2.NextInAEL;
  875. if not assigned(e2) then outRec.Owner := nil
  876. else if IsOuter(e2.OutRec) = (e2.OutRec.frontE = e2) then
  877. outRec.Owner := e2.OutRec.Owner
  878. else
  879. outRec.Owner := e2.OutRec;
  880. end else
  881. begin
  882. e2 := GetPrevHotEdge(e);
  883. if not assigned(e2) then
  884. outRec.Owner := nil
  885. else if IsOuter(e2.OutRec) = (e2.OutRec.backE = e2) then
  886. outRec.Owner := e2.OutRec.Owner
  887. else
  888. outRec.Owner := e2.OutRec;
  889. end;
  890. // set inner/outer ...
  891. if not assigned(outRec.Owner) or IsInner(outRec.Owner) then
  892. outRec.State := osOuter else
  893. outRec.State := osInner;
  894. end;
  895. //------------------------------------------------------------------------------
  896. function EdgesAdjacentInAEL(node: PIntersectNode): Boolean;
  897. {$IFDEF USEINLINING} inline; {$ENDIF}
  898. begin
  899. with node^ do
  900. Result := (Edge1.NextInAEL = Edge2) or (Edge1.PrevInAEL = Edge2);
  901. end;
  902. //------------------------------------------------------------------------------
  903. function IntersectListSort(node1, node2: Pointer): Integer;
  904. begin
  905. Result := PIntersectNode(node2).Pt.Y - PIntersectNode(node1).Pt.Y;
  906. if (Result = 0) and (node1 <> node2) then
  907. Result := PIntersectNode(node1).Pt.X - PIntersectNode(node2).Pt.X;
  908. end;
  909. //------------------------------------------------------------------------------
  910. // TClipper methods ...
  911. //------------------------------------------------------------------------------
  912. constructor TClipper.Create;
  913. begin
  914. FLocMinList := TList.Create;
  915. FOutRecList := TList.Create;
  916. FIntersectList := TList.Create;
  917. FVertexList := TList.Create;
  918. end;
  919. //------------------------------------------------------------------------------
  920. destructor TClipper.Destroy;
  921. begin
  922. Clear;
  923. FLocMinList.Free;
  924. FOutRecList.Free;
  925. FIntersectList.Free;
  926. FVertexList.Free;
  927. inherited;
  928. end;
  929. //------------------------------------------------------------------------------
  930. procedure TClipper.CleanUp;
  931. var
  932. dummy: Int64;
  933. begin
  934. try
  935. // in case of exceptions ...
  936. while assigned(FActives) do DeleteFromAEL(FActives);
  937. while assigned(FScanLine) do PopScanLine(dummy);
  938. DisposeIntersectNodes;
  939. DisposeScanLineList;
  940. DisposeAllOutRecs;
  941. except
  942. end;
  943. end;
  944. //------------------------------------------------------------------------------
  945. procedure TClipper.Clear;
  946. begin
  947. CleanUp;
  948. DisposeVerticesAndLocalMinima;
  949. FCurrentLocMinIdx := 0;
  950. FLocMinListSorted := false;
  951. FHasOpenPaths := False;
  952. end;
  953. //------------------------------------------------------------------------------
  954. procedure TClipper.Reset;
  955. var
  956. i: Integer;
  957. begin
  958. if not FLocMinListSorted then
  959. begin
  960. FLocMinList.Sort(LocMinListSort);
  961. FLocMinListSorted := true;
  962. end;
  963. for i := FLocMinList.Count -1 downto 0 do
  964. InsertScanLine(PLocalMinima(FLocMinList[i]).vertex.Pt.Y);
  965. FCurrentLocMinIdx := 0;
  966. FActives := nil;
  967. FSel := nil;
  968. end;
  969. //------------------------------------------------------------------------------
  970. procedure TClipper.InsertScanLine(const Y: Int64);
  971. var
  972. newSl, sl: PScanLine;
  973. begin
  974. // The scanline list is a single-linked list of all the Y coordinates of
  975. // subject and clip vertices in the clipping operation (sorted descending).
  976. // However, only scanline Y's at Local Minima are inserted before clipping
  977. // starts. While scanlines are removed sequentially during the sweep, new
  978. // scanlines are only inserted whenever edge bounds are updated. This keeps
  979. // the scanline list relatively short, optimising performance.
  980. if not Assigned(FScanLine) then
  981. begin
  982. new(newSl);
  983. newSl.Y := Y;
  984. FScanLine := newSl;
  985. newSl.Next := nil;
  986. end else if Y > FScanLine.Y then
  987. begin
  988. new(newSl);
  989. newSl.Y := Y;
  990. newSl.Next := FScanLine;
  991. FScanLine := newSl;
  992. end else
  993. begin
  994. sl := FScanLine;
  995. while Assigned(sl.Next) and (Y <= sl.Next.Y) do
  996. sl := sl.Next;
  997. if Y = sl.Y then Exit; // skip duplicates
  998. new(newSl);
  999. newSl.Y := Y;
  1000. newSl.Next := sl.Next;
  1001. sl.Next := newSl;
  1002. end;
  1003. end;
  1004. //------------------------------------------------------------------------------
  1005. function TClipper.PopScanLine(out Y: Int64): Boolean;
  1006. var
  1007. sl: PScanLine;
  1008. begin
  1009. Result := assigned(FScanLine);
  1010. if not Result then Exit;
  1011. Y := FScanLine.Y;
  1012. sl := FScanLine;
  1013. FScanLine := FScanLine.Next;
  1014. dispose(sl);
  1015. end;
  1016. //------------------------------------------------------------------------------
  1017. function TClipper.PopLocalMinima(Y: Int64;
  1018. out localMinima: PLocalMinima): Boolean;
  1019. begin
  1020. Result := false;
  1021. if FCurrentLocMinIdx = FLocMinList.Count then Exit;
  1022. localMinima := PLocalMinima(FLocMinList[FCurrentLocMinIdx]);
  1023. if (localMinima.vertex.Pt.Y = Y) then
  1024. begin
  1025. inc(FCurrentLocMinIdx);
  1026. Result := true;
  1027. end;
  1028. end;
  1029. //------------------------------------------------------------------------------
  1030. procedure TClipper.DisposeScanLineList;
  1031. var
  1032. sl: PScanLine;
  1033. begin
  1034. while Assigned(FScanLine) do
  1035. begin
  1036. sl := FScanLine.Next;
  1037. Dispose(FScanLine);
  1038. FScanLine := sl;
  1039. end;
  1040. end;
  1041. //------------------------------------------------------------------------------
  1042. procedure TClipper.DisposeOutRec(index: Integer);
  1043. var
  1044. outRec: TOutRec;
  1045. begin
  1046. outRec := FOutRecList[index];
  1047. if Assigned(outRec.Pts) then DisposePolyPts(outRec.Pts);
  1048. outRec.Free;
  1049. end;
  1050. //------------------------------------------------------------------------------
  1051. procedure TClipper.DisposeAllOutRecs;
  1052. var
  1053. i: Integer;
  1054. begin
  1055. for i := 0 to FOutRecList.Count -1 do DisposeOutRec(i);
  1056. FOutRecList.Clear;
  1057. end;
  1058. //------------------------------------------------------------------------------
  1059. procedure TClipper.DisposeVerticesAndLocalMinima;
  1060. var
  1061. i: Integer;
  1062. begin
  1063. for i := 0 to FLocMinList.Count -1 do
  1064. Dispose(PLocalMinima(FLocMinList[i]));
  1065. FLocMinList.Clear;
  1066. for i := 0 to FVertexList.Count -1 do FreeMem(FVertexList[i]);
  1067. FVertexList.Clear;
  1068. end;
  1069. //------------------------------------------------------------------------------
  1070. procedure TClipper.AddPathToVertexList(const path: TArrayOfFloatPoint;
  1071. polyType: TPathType; isOpen: Boolean);
  1072. var
  1073. i, j, pathLen: Integer;
  1074. isFlat, goingUp, p0IsMinima, p0IsMaxima: Boolean;
  1075. v: PVertex;
  1076. va: PVertexArray;
  1077. procedure AddLocMin(vert: PVertex);
  1078. var
  1079. lm: PLocalMinima;
  1080. begin
  1081. if vfLocMin in vert.flags then Exit; // ensures vertex is added only once
  1082. Include(vert.flags, vfLocMin);
  1083. new(lm);
  1084. lm.vertex := vert;
  1085. lm.PolyType := polyType;
  1086. lm.IsOpen := isOpen;
  1087. FLocMinList.Add(lm); // nb: sorted in Reset()
  1088. end;
  1089. //----------------------------------------------------------------------------
  1090. begin
  1091. pathLen := length(path);
  1092. if (pathLen < 2) then Exit;
  1093. p0IsMinima := false;
  1094. p0IsMaxima := false;
  1095. i := 1;
  1096. // find the first non-horizontal segment in the path ...
  1097. while (i < pathLen) and (path[i].Y = path[0].Y) do inc(i);
  1098. isFlat := i = pathLen;
  1099. if isFlat then
  1100. begin
  1101. if not isOpen then Exit; // Ignore closed paths that have ZERO area.
  1102. goingUp := false; // And this just stops a compiler warning.
  1103. end else
  1104. begin
  1105. goingUp := path[i].Y < path[0].Y;
  1106. if goingUp then
  1107. begin
  1108. i := pathLen -1;
  1109. while path[i].Y = path[0].Y do dec(i);
  1110. p0IsMinima := path[i].Y < path[0].Y; // p[0].Y == a minima
  1111. end else
  1112. begin
  1113. i := pathLen -1;
  1114. while path[i].Y = path[0].Y do dec(i);
  1115. p0IsMaxima := path[i].Y > path[0].Y; // p[0].Y == a maxima
  1116. end;
  1117. end;
  1118. GetMem(va, sizeof(TVertex) * pathLen);
  1119. FVertexList.Add(va);
  1120. va[0].Pt := Point64(path[0]);
  1121. va[0].flags := [];
  1122. if isOpen then
  1123. begin
  1124. include(va[0].flags, vfOpenStart);
  1125. if goingUp then
  1126. AddLocMin(@va[0]) else
  1127. include(va[0].flags, vfLocMax);
  1128. end;
  1129. // nb: polygon orientation is determined later (see InsertLocalMinimaIntoAEL).
  1130. i := 0;
  1131. for j := 1 to pathLen -1 do
  1132. begin
  1133. va[j].Pt := Point64(path[j]);
  1134. if PointsEqual(va[j].Pt, va[i].Pt) then Continue;
  1135. va[j].flags := [];
  1136. va[i].next := @va[j];
  1137. va[j].prev := @va[i];
  1138. if (path[j].Y > path[i].Y) and goingUp then
  1139. begin
  1140. include(va[i].flags, vfLocMax);
  1141. goingUp := false;
  1142. end
  1143. else if (path[j].Y < path[i].Y) and not goingUp then
  1144. begin
  1145. goingUp := true;
  1146. AddLocMin(@va[i]);
  1147. end;
  1148. i := j;
  1149. end;
  1150. // i: index of the last vertex in the path.
  1151. va[i].next := @va[0];
  1152. va[0].prev := @va[i];
  1153. if isOpen then
  1154. begin
  1155. include(va[i].flags, vfOpenEnd);
  1156. if goingUp then
  1157. include(va[i].flags, vfLocMax) else
  1158. AddLocMin(@va[i]);
  1159. end
  1160. else if goingUp then
  1161. begin
  1162. // going up so find local maxima ...
  1163. v := @va[i];
  1164. while (v.Next.Pt.Y <= v.Pt.Y) do v := v.next;
  1165. include(v.flags, vfLocMax);
  1166. if p0IsMinima then AddLocMin(@va[0]); // ie just turned to going up
  1167. end else
  1168. begin
  1169. // going down so find local minima ...
  1170. v := @va[i];
  1171. while (v.Next.Pt.Y >= v.Pt.Y) do v := v.next;
  1172. AddLocMin(v);
  1173. if p0IsMaxima then include(va[0].flags, vfLocMax);
  1174. end;
  1175. end;
  1176. //------------------------------------------------------------------------------
  1177. procedure TClipper.AddPath(const path64: TArrayOfFloatPoint;
  1178. PolyType: TPathType; isOpen: Boolean);
  1179. begin
  1180. if isOpen then
  1181. begin
  1182. if (PolyType = ptClip) then
  1183. raise EClipperLibException.Create(rsClipper_OpenPathErr);
  1184. FHasOpenPaths := true;
  1185. end;
  1186. FLocMinListSorted := false;
  1187. AddPathToVertexList(path64, polyType, isOpen);
  1188. end;
  1189. //------------------------------------------------------------------------------
  1190. procedure TClipper.AddPath(const path: TArrayOfFixedPoint;
  1191. PolyType: TPathType; isOpen: Boolean);
  1192. begin
  1193. AddPathToVertexList(FixedToFloat(path), polyType, isOpen);
  1194. end;
  1195. //------------------------------------------------------------------------------
  1196. procedure TClipper.AddPaths(const paths64: TArrayOfArrayOfFloatPoint;
  1197. polyType: TPathType; isOpen: Boolean);
  1198. var
  1199. i: Integer;
  1200. begin
  1201. for i := 0 to high(paths64) do AddPath(paths64[i], polyType, isOpen);
  1202. end;
  1203. //------------------------------------------------------------------------------
  1204. procedure TClipper.AddPaths(const paths: TArrayOfArrayOfFixedPoint;
  1205. polyType: TPathType = ptSubject; isOpen: Boolean = false);
  1206. var
  1207. i: Integer;
  1208. begin
  1209. for i := 0 to high(paths) do AddPath(paths[i], polyType, isOpen);
  1210. end;
  1211. //------------------------------------------------------------------------------
  1212. function TClipper.IsContributingClosed(e: PActive): Boolean;
  1213. begin
  1214. Result := false;
  1215. case FFillRule of
  1216. frNonZero: if abs(e.WindCnt) <> 1 then Exit;
  1217. frPositive: if (e.WindCnt <> 1) then Exit;
  1218. frNegative: if (e.WindCnt <> -1) then Exit;
  1219. end;
  1220. case FClipType of
  1221. ctIntersection:
  1222. case FFillRule of
  1223. frEvenOdd, frNonZero: Result := (e.WindCnt2 <> 0);
  1224. frPositive: Result := (e.WindCnt2 > 0);
  1225. frNegative: Result := (e.WindCnt2 < 0);
  1226. end;
  1227. ctUnion:
  1228. case FFillRule of
  1229. frEvenOdd, frNonZero: Result := (e.WindCnt2 = 0);
  1230. frPositive: Result := (e.WindCnt2 <= 0);
  1231. frNegative: Result := (e.WindCnt2 >= 0);
  1232. end;
  1233. ctDifference:
  1234. if GetPolyType(e) = ptSubject then
  1235. case FFillRule of
  1236. frEvenOdd, frNonZero: Result := (e.WindCnt2 = 0);
  1237. frPositive: Result := (e.WindCnt2 <= 0);
  1238. frNegative: Result := (e.WindCnt2 >= 0);
  1239. end
  1240. else
  1241. case FFillRule of
  1242. frEvenOdd, frNonZero: Result := (e.WindCnt2 <> 0);
  1243. frPositive: Result := (e.WindCnt2 > 0);
  1244. frNegative: Result := (e.WindCnt2 < 0);
  1245. end;
  1246. ctXor:
  1247. Result := true;
  1248. end;
  1249. end;
  1250. //------------------------------------------------------------------------------
  1251. function TClipper.IsContributingOpen(e: PActive): Boolean;
  1252. begin
  1253. case FClipType of
  1254. ctIntersection:
  1255. Result := (e.WindCnt2 <> 0);
  1256. ctXor:
  1257. Result := (e.WindCnt <> 0) <> (e.WindCnt2 <> 0);
  1258. ctDifference:
  1259. Result := (e.WindCnt2 = 0);
  1260. else // ctUnion:
  1261. Result := (e.WindCnt = 0) and (e.WindCnt2 = 0);
  1262. end;
  1263. end;
  1264. //------------------------------------------------------------------------------
  1265. procedure TClipper.SetWindCountForClosedPathEdge(e: PActive);
  1266. var
  1267. e2: PActive;
  1268. begin
  1269. // Wind counts refer to polygon regions not edges, so here an edge's WindCnt
  1270. // indicates the higher of the wind counts for the two regions touching the
  1271. // edge. (nb: Adjacent regions can only ever have their wind counts differ by
  1272. // one. Also, open paths have no meaningful wind directions or counts.)
  1273. e2 := e.PrevInAEL;
  1274. // find the nearest closed edge of the same PolyType in AEL (heading left)
  1275. while Assigned(e2) and (not IsSamePolyType(e2, e) or IsOpen(e2)) do
  1276. e2 := e2.PrevInAEL;
  1277. if not Assigned(e2) then
  1278. begin
  1279. e.WindCnt := e.WindDx;
  1280. e2 := FActives;
  1281. end
  1282. else if (FFillRule = frEvenOdd) then
  1283. begin
  1284. e.WindCnt := e.WindDx;
  1285. e.WindCnt2 := e2.WindCnt2;
  1286. e2 := e2.NextInAEL;
  1287. end else
  1288. begin
  1289. // NonZero, positive, or negative filling here ...
  1290. // if e's WindCnt is in the SAME direction as its WindDx, then polygon
  1291. // filling will be on the right of 'e'.
  1292. // nb: neither e2.WindCnt nor e2.WindDx should ever be 0.
  1293. if (e2.WindCnt * e2.WindDx < 0) then
  1294. begin
  1295. // opposite directions so 'e' is outside 'e2' ...
  1296. if (Abs(e2.WindCnt) > 1) then
  1297. begin
  1298. // outside prev poly but still inside another.
  1299. if (e2.WindDx * e.WindDx < 0) then
  1300. // reversing direction so use the same WC
  1301. e.WindCnt := e2.WindCnt else
  1302. // otherwise keep 'reducing' the WC by 1 (ie towards 0) ...
  1303. e.WindCnt := e2.WindCnt + e.WindDx;
  1304. end
  1305. // now outside all polys of same polytype so set own WC ...
  1306. else e.WindCnt := e.WindDx;
  1307. end else
  1308. begin
  1309. // 'e' must be inside 'e2'
  1310. if (e2.WindDx * e.WindDx < 0) then
  1311. // reversing direction so use the same WC
  1312. e.WindCnt := e2.WindCnt
  1313. else
  1314. // otherwise keep 'increasing' the WC by 1 (ie away from 0) ...
  1315. e.WindCnt := e2.WindCnt + e.WindDx;
  1316. end;
  1317. e.WindCnt2 := e2.WindCnt2;
  1318. e2 := e2.NextInAEL;
  1319. end;
  1320. // update WindCnt2 ...
  1321. if FFillRule = frEvenOdd then
  1322. while (e2 <> e) do
  1323. begin
  1324. if IsSamePolyType(e2, e) or IsOpen(e2) then // do nothing
  1325. else if e.WindCnt2 = 0 then e.WindCnt2 := 1
  1326. else e.WindCnt2 := 0;
  1327. e2 := e2.NextInAEL;
  1328. end
  1329. else
  1330. while (e2 <> e) do
  1331. begin
  1332. if not IsSamePolyType(e2, e) and not IsOpen(e2) then
  1333. Inc(e.WindCnt2, e2.WindDx);
  1334. e2 := e2.NextInAEL;
  1335. end;
  1336. end;
  1337. //------------------------------------------------------------------------------
  1338. procedure TClipper.SetWindCountForOpenPathEdge(e: PActive);
  1339. var
  1340. e2: PActive;
  1341. cnt1, cnt2: Integer;
  1342. begin
  1343. e2 := FActives;
  1344. if FFillRule = frEvenOdd then
  1345. begin
  1346. cnt1 := 0;
  1347. cnt2 := 0;
  1348. while (e2 <> e) do
  1349. begin
  1350. if (GetPolyType(e2) = ptClip) then inc(cnt2)
  1351. else if not IsOpen(e2) then inc(cnt1);
  1352. e2 := e2.NextInAEL;
  1353. end;
  1354. if Odd(cnt1) then e.WindCnt := 1 else e.WindCnt := 0;
  1355. if Odd(cnt2) then e.WindCnt2 := 1 else e.WindCnt2 := 0;
  1356. end else
  1357. begin
  1358. // if FClipType in [ctUnion, ctDifference] then e.WindCnt := e.WindDx;
  1359. while (e2 <> e) do
  1360. begin
  1361. if (GetPolyType(e2) = ptClip) then inc(e.WindCnt2, e2.WindDx)
  1362. else if not IsOpen(e2) then inc(e.WindCnt, e2.WindDx);
  1363. e2 := e2.NextInAEL;
  1364. end;
  1365. end;
  1366. end;
  1367. //------------------------------------------------------------------------------
  1368. function IsValidAelOrder(a1, a2: PActive): Boolean;
  1369. var
  1370. pt1, pt2: TPoint64;
  1371. op1, op2: PVertex;
  1372. X: Int64;
  1373. begin
  1374. if a2.CurrX <> a1.CurrX then
  1375. begin
  1376. Result := a2.CurrX > a1.CurrX;
  1377. Exit;
  1378. end;
  1379. pt1 := a1.Bot; pt2 := a2.Bot;
  1380. op1 := a1.VertTop; op2 := a2.VertTop;
  1381. while true do
  1382. begin
  1383. if op1.Pt.Y >= op2.Pt.Y then
  1384. begin
  1385. X := TopX(pt2, op2.Pt, op1.Pt.Y) - op1.Pt.X;
  1386. Result := X > 0;
  1387. if X <> 0 then Exit;
  1388. if op2.Pt.Y = op1.Pt.Y then
  1389. begin
  1390. pt2 := op2.Pt;
  1391. op2 := NextVertex(op2, IsLeftBound(a2));
  1392. end;
  1393. pt1 := op1.Pt;
  1394. op1 := NextVertex(op1, IsLeftBound(a1));
  1395. end else
  1396. begin
  1397. X := op2.Pt.X - TopX(pt1, op1.Pt, op2.Pt.Y);
  1398. Result := X > 0;
  1399. if X <> 0 then Exit;
  1400. pt2 := op2.Pt;
  1401. op2 := NextVertex(op2, IsLeftBound(a2));
  1402. end;
  1403. if (op1.Pt.Y > pt1.Y) then
  1404. begin
  1405. Result := (a1.WindDx > 0) <> IsClockwise(PrevVertex(op1, a1.WindDx > 0));
  1406. Exit;
  1407. end else if (op2.Pt.Y > pt2.Y) then
  1408. begin
  1409. Result := (a2.WindDx > 0) = IsClockwise(PrevVertex(op2, a2.WindDx > 0));
  1410. Exit;
  1411. end;
  1412. end;
  1413. Result := true;
  1414. end;
  1415. //------------------------------------------------------------------------------
  1416. procedure TClipper.InsertLeftEdge(e: PActive);
  1417. var
  1418. e2: PActive;
  1419. begin
  1420. if not Assigned(FActives) then
  1421. begin
  1422. e.PrevInAEL := nil;
  1423. e.NextInAEL := nil;
  1424. FActives := e;
  1425. end
  1426. else if IsValidAelOrder(e, FActives) then
  1427. begin
  1428. e.PrevInAEL := nil;
  1429. e.NextInAEL := FActives;
  1430. FActives.PrevInAEL := e;
  1431. FActives := e;
  1432. end else
  1433. begin
  1434. e2 := FActives;
  1435. while Assigned(e2.NextInAEL) and IsValidAelOrder(e2.NextInAEL, e) do
  1436. e2 := e2.NextInAEL;
  1437. e.NextInAEL := e2.NextInAEL;
  1438. if Assigned(e2.NextInAEL) then e2.NextInAEL.PrevInAEL := e;
  1439. e.PrevInAEL := e2;
  1440. e2.NextInAEL := e;
  1441. end;
  1442. end;
  1443. //----------------------------------------------------------------------
  1444. procedure InsertRightEdge(e, e2: PActive);
  1445. begin
  1446. e2.NextInAEL := e.NextInAEL;
  1447. if Assigned(e.NextInAEL) then e.NextInAEL.PrevInAEL := e2;
  1448. e2.PrevInAEL := e;
  1449. e.NextInAEL := e2;
  1450. end;
  1451. //----------------------------------------------------------------------
  1452. procedure TClipper.InsertLocalMinimaIntoAEL(const botY: Int64);
  1453. var
  1454. leftB, rightB: PActive;
  1455. locMin: PLocalMinima;
  1456. contributing: Boolean;
  1457. begin
  1458. // Add local minima (if any) at BotY ...
  1459. // nb: horizontal local minima edges should contain locMin.vertex.prev
  1460. while PopLocalMinima(botY, locMin) do
  1461. begin
  1462. if (vfOpenStart in locMin.vertex.flags) then
  1463. begin
  1464. leftB := nil;
  1465. end else
  1466. begin
  1467. new(leftB);
  1468. FillChar(leftB^, sizeof(TActive), 0);
  1469. leftB.LocMin := locMin;
  1470. leftB.OutRec := nil;
  1471. leftB.Bot := locMin.vertex.Pt;
  1472. leftB.vertTop := locMin.vertex.prev; // ie descending
  1473. leftB.Top := leftB.vertTop.Pt;
  1474. leftB.CurrX := leftB.Bot.X;
  1475. leftB.WindDx := -1;
  1476. SetDx(leftB);
  1477. end;
  1478. if (vfOpenEnd in locMin.vertex.flags) then
  1479. begin
  1480. rightB := nil;
  1481. end else
  1482. begin
  1483. new(rightB);
  1484. FillChar(rightB^, sizeof(TActive), 0);
  1485. rightB.LocMin := locMin;
  1486. rightB.OutRec := nil;
  1487. rightB.Bot := locMin.vertex.Pt;
  1488. rightB.vertTop := locMin.vertex.next; // ie ascending
  1489. rightB.Top := rightB.vertTop.Pt;
  1490. rightB.CurrX := rightB.Bot.X;
  1491. rightB.WindDx := 1;
  1492. SetDx(rightB);
  1493. end;
  1494. // Currently LeftB is just the descending bound and RightB is the ascending.
  1495. // Now if the LeftB isn't on the left of RightB then we need swap them.
  1496. if assigned(leftB) and assigned(rightB) then
  1497. begin
  1498. if IsHorizontal(leftB) then
  1499. begin
  1500. if IsHeadingRightHorz(leftB) then SwapActives(leftB, rightB);
  1501. end
  1502. else if IsHorizontal(rightB) then
  1503. begin
  1504. if IsHeadingLeftHorz(rightB) then SwapActives(leftB, rightB);
  1505. end
  1506. else if (leftB.Dx < rightB.Dx) then SwapActives(leftB, rightB);
  1507. end
  1508. else if not assigned(leftB) then
  1509. begin
  1510. leftB := rightB;
  1511. rightB := nil;
  1512. end;
  1513. InsertLeftEdge(leftB); // /// //
  1514. // todo: further validation of position in AEL ???
  1515. if IsOpen(leftB) then
  1516. begin
  1517. SetWindCountForOpenPathEdge(leftB);
  1518. contributing := IsContributingOpen(leftB);
  1519. end else
  1520. begin
  1521. SetWindCountForClosedPathEdge(leftB);
  1522. contributing := IsContributingClosed(leftB);
  1523. end;
  1524. if assigned(rightB) then
  1525. begin
  1526. rightB.WindCnt := leftB.WindCnt;
  1527. rightB.WindCnt2 := leftB.WindCnt2;
  1528. InsertRightEdge(leftB, rightB); // /// //
  1529. if contributing then
  1530. AddLocalMinPoly(leftB, rightB, leftB.Bot, true);
  1531. if IsHorizontal(rightB) then
  1532. PushHorz(rightB) else
  1533. InsertScanLine(rightB.Top.Y);
  1534. end
  1535. else if contributing then
  1536. StartOpenPath(leftB, leftB.Bot);
  1537. if IsHorizontal(leftB) then
  1538. PushHorz(leftB) else
  1539. InsertScanLine(leftB.Top.Y);
  1540. end;
  1541. end;
  1542. //------------------------------------------------------------------------------
  1543. procedure TClipper.PushHorz(e: PActive);
  1544. begin
  1545. if assigned(FSel) then
  1546. e.NextInSEL := FSel else
  1547. e.NextInSEL := nil;
  1548. FSel := e;
  1549. end;
  1550. //------------------------------------------------------------------------------
  1551. function TClipper.PopHorz(out e: PActive): Boolean;
  1552. begin
  1553. Result := assigned(FSel);
  1554. if not Result then Exit;
  1555. e := FSel;
  1556. FSel := FSel.NextInSEL;
  1557. end;
  1558. //------------------------------------------------------------------------------
  1559. procedure TClipper.AddLocalMinPoly(e1, e2: PActive; const pt: TPoint64;
  1560. IsNew: Boolean = false; orientationCheckRequired: Boolean = false);
  1561. var
  1562. outRec: TOutRec;
  1563. op: TOutPt;
  1564. begin
  1565. outRec := TOutRec.Create;
  1566. outRec.Idx := FOutRecList.Add(outRec);
  1567. outRec.Pts := nil;
  1568. e1.OutRec := outRec;
  1569. SetOwnerAndInnerOuterState(e1);
  1570. // flag when orientatation needs to be rechecked later ...
  1571. if orientationCheckRequired then SetCheckFlag(outRec);
  1572. e2.OutRec := outRec;
  1573. if not IsOpen(e1) then
  1574. begin
  1575. // Setting the owner and inner/outer states (above) is an essential
  1576. // precursor to setting edge 'sides' (ie left and right sides of output
  1577. // polygons) and hence the orientation of output paths ...
  1578. if IsOuter(outRec) = IsNew then
  1579. SetSides(outRec, e1, e2) else
  1580. SetSides(outRec, e2, e1);
  1581. end;
  1582. op := TOutPt.Create;
  1583. outRec.Pts := op;
  1584. op.Pt := pt;
  1585. op.Prev := op;
  1586. op.Next := op;
  1587. // nb: currently e1.NextInAEL == e2 but this could change on return
  1588. end;
  1589. //------------------------------------------------------------------------------
  1590. procedure TClipper.AddLocalMaxPoly(e1, e2: PActive; const pt: TPoint64);
  1591. var
  1592. op: TOutPt;
  1593. begin
  1594. if not IsOpen(e1) and (IsFront(e1) = IsFront(e2)) then
  1595. if not FixSides(e1) then FixSides(e2);
  1596. op := AddOutPt(e1, pt);
  1597. // AddOutPt(e2, pt); // this may no longer be necessary
  1598. if (e1.OutRec = e2.OutRec) then
  1599. begin
  1600. if e1.OutRec.State in [osOuterCheck, osInnerCheck] then
  1601. RecheckInnerOuter(e1);
  1602. // nb: IsClockwise() is generally faster than Area() but will occasionally
  1603. // give false positives when there are tiny self-intersections at the top...
  1604. if IsOuter(e1.OutRec) then
  1605. begin
  1606. if not IsClockwise(op) and (Area(op) < 0) then
  1607. ReverseOutPts(e1.OutRec.Pts);
  1608. end else
  1609. begin
  1610. if IsClockwise(op) and (Area(op) > 0) then
  1611. ReverseOutPts(e1.OutRec.Pts);
  1612. end;
  1613. e1.outRec.frontE := nil;
  1614. e1.outRec.backE := nil;
  1615. e1.OutRec := nil;
  1616. e2.OutRec := nil;
  1617. end
  1618. // and to preserve the winding orientation of Outrec ...
  1619. else if e1.OutRec.Idx < e2.OutRec.Idx then
  1620. JoinOutrecPaths(e1, e2) else
  1621. JoinOutrecPaths(e2, e1);
  1622. end;
  1623. //------------------------------------------------------------------------------
  1624. procedure TClipper.JoinOutrecPaths(e1, e2: PActive);
  1625. var
  1626. p1_start, p1_end, p2_start, p2_end: TOutPt;
  1627. begin
  1628. if (IsFront(e1) = IsFront(e2)) then
  1629. begin
  1630. // one or other 'side' must be wrong ...
  1631. if IsOpen(e1) then SwapSides(e2.OutRec)
  1632. else if not FixSides(e1) and not FixSides(e2) then
  1633. raise EClipperLibException.Create(rsClipper_ClippingErr);
  1634. if e1.OutRec.Owner = e2.OutRec then
  1635. e1.OutRec.Owner := e2.OutRec.Owner;
  1636. end;
  1637. // join e2 outrec path onto e1 outrec path and then delete e2 outrec path
  1638. // pointers. (see joining_outpt.svg)
  1639. p1_start := e1.OutRec.Pts;
  1640. p2_start := e2.OutRec.Pts;
  1641. p1_end := p1_start.Next;
  1642. p2_end := p2_start.Next;
  1643. if IsFront(e1) then
  1644. begin
  1645. p2_end.Prev := p1_start;
  1646. p1_start.Next := p2_end;
  1647. p2_start.Next := p1_end;
  1648. p1_end.Prev := p2_start;
  1649. e1.OutRec.Pts := p2_start;
  1650. e1.OutRec.frontE := e2.OutRec.frontE;
  1651. if not IsOpen(e1) then e1.OutRec.frontE.OutRec := e1.OutRec;
  1652. // strip duplicates ...
  1653. if (p2_end <> p2_start) and PointsEqual(p2_end.Pt, p2_end.Prev.Pt) then
  1654. DisposeOutPt(p2_end);
  1655. end else
  1656. begin
  1657. p1_end.Prev := p2_start;
  1658. p2_start.Next := p1_end;
  1659. p1_start.Next := p2_end;
  1660. p2_end.Prev := p1_start;
  1661. e1.OutRec.backE := e2.OutRec.backE;
  1662. if not IsOpen(e1) then e1.OutRec.backE.OutRec := e1.OutRec;
  1663. // strip duplicates ...
  1664. if (p1_end <> p1_start) and PointsEqual(p1_end.Pt, p1_end.Prev.Pt) then
  1665. DisposeOutPt(p1_end);
  1666. end;
  1667. if PointsEqual(e1.OutRec.Pts.Pt, e1.OutRec.Pts.Prev.Pt) and
  1668. not IsInvalidPath(e1.OutRec.Pts) then
  1669. DisposeOutPt(e1.OutRec.Pts.Prev);
  1670. // after joining, the e2.OutRec must contains no vertices ...
  1671. e2.OutRec.frontE := nil;
  1672. e2.OutRec.backE := nil;
  1673. e2.OutRec.Pts := nil;
  1674. e2.OutRec.Owner := e1.OutRec; // this may be redundant
  1675. // and e1 and e2 are maxima and are about to be dropped from the Actives list.
  1676. e1.OutRec := nil;
  1677. e2.OutRec := nil;
  1678. end;
  1679. //------------------------------------------------------------------------------
  1680. function TClipper.AddOutPt(e: PActive; const pt: TPoint64): TOutPt;
  1681. var
  1682. opFront, opBack: TOutPt;
  1683. toFront: Boolean;
  1684. outrec: TOutRec;
  1685. begin
  1686. // Outrec.OutPts: a circular doubly-linked-list of POutPt where ...
  1687. // opFront[.Prev]* ~~~> opBack & opBack == opFront.Next
  1688. outrec := e.OutRec;
  1689. toFront := IsFront(e);
  1690. opFront := outrec.Pts;
  1691. opBack := opFront.Next;
  1692. if toFront and PointsEqual(pt, opFront.Pt) then
  1693. Result := opFront
  1694. else if not toFront and PointsEqual(pt, opBack.Pt) then
  1695. Result := opBack
  1696. else
  1697. begin
  1698. Result := TOutPt.Create;
  1699. Result.Pt := pt;
  1700. opBack.Prev := Result;
  1701. Result.Prev := opFront;
  1702. Result.Next := opBack;
  1703. opFront.Next := Result;
  1704. if toFront then outrec.Pts := Result;
  1705. end;
  1706. end;
  1707. //------------------------------------------------------------------------------
  1708. procedure TClipper.StartOpenPath(e: PActive; const pt: TPoint64);
  1709. var
  1710. outRec: TOutRec;
  1711. op: TOutPt;
  1712. begin
  1713. outRec := TOutRec.Create;
  1714. outRec.Idx := FOutRecList.Add(outRec);
  1715. outRec.Owner := nil;
  1716. outRec.State := osOpen;
  1717. outRec.Pts := nil;
  1718. outRec.frontE := nil;
  1719. outRec.backE := nil;
  1720. e.OutRec := outRec;
  1721. op := TOutPt.Create;
  1722. outRec.Pts := op;
  1723. op.Pt := pt;
  1724. op.Prev := op;
  1725. op.Next := op;
  1726. end;
  1727. //------------------------------------------------------------------------------
  1728. procedure TClipper.UpdateEdgeIntoAEL(var e: PActive);
  1729. begin
  1730. e.Bot := e.Top;
  1731. e.vertTop := NextVertex(e);
  1732. e.Top := e.vertTop.Pt;
  1733. e.CurrX := e.Bot.X;
  1734. SetDx(e);
  1735. if not IsHorizontal(e) then InsertScanLine(e.Top.Y);
  1736. end;
  1737. //------------------------------------------------------------------------------
  1738. procedure TClipper.IntersectEdges(e1, e2: PActive;
  1739. const pt: TPoint64; orientationCheckRequired: Boolean = false);
  1740. var
  1741. e1WindCnt, e2WindCnt, e1WindCnt2, e2WindCnt2: Integer;
  1742. begin
  1743. // MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
  1744. if FHasOpenPaths and (IsOpen(e1) or IsOpen(e2)) then
  1745. begin
  1746. if (IsOpen(e1) and IsOpen(e2) ) then Exit;
  1747. // the following line avoids duplicating a whole lot of code ...
  1748. if IsOpen(e2) then SwapActives(e1, e2);
  1749. case FClipType of
  1750. ctIntersection, ctDifference:
  1751. if IsSamePolyType(e1, e2) or (abs(e2.WindCnt) <> 1) then Exit;
  1752. ctUnion:
  1753. if IsHotEdge(e1) <> ((abs(e2.WindCnt) <> 1) or
  1754. (IsHotEdge(e1) <> (e2.WindCnt2 <> 0))) then Exit; // just works!
  1755. ctXor:
  1756. if (abs(e2.WindCnt) <> 1) then Exit;
  1757. end;
  1758. // toggle contribution ...
  1759. if IsHotEdge(e1) then
  1760. begin
  1761. AddOutPt(e1, pt);
  1762. TerminateHotOpen(e1);
  1763. end
  1764. else StartOpenPath(e1, pt);
  1765. Exit;
  1766. end;
  1767. // UPDATE WINDING COUNTS...
  1768. if IsSamePolyType(e1, e2) then
  1769. begin
  1770. if FFillRule = frEvenOdd then
  1771. begin
  1772. e1WindCnt := e1.WindCnt;
  1773. e1.WindCnt := e2.WindCnt;
  1774. e2.WindCnt := e1WindCnt;
  1775. end else
  1776. begin
  1777. if e1.WindCnt + e2.WindDx = 0 then
  1778. e1.WindCnt := -e1.WindCnt else
  1779. Inc(e1.WindCnt, e2.WindDx);
  1780. if e2.WindCnt - e1.WindDx = 0 then
  1781. e2.WindCnt := -e2.WindCnt else
  1782. Dec(e2.WindCnt, e1.WindDx);
  1783. end;
  1784. end else
  1785. begin
  1786. if FFillRule <> frEvenOdd then Inc(e1.WindCnt2, e2.WindDx)
  1787. else if e1.WindCnt2 = 0 then e1.WindCnt2 := 1
  1788. else e1.WindCnt2 := 0;
  1789. if FFillRule <> frEvenOdd then Dec(e2.WindCnt2, e1.WindDx)
  1790. else if e2.WindCnt2 = 0 then e2.WindCnt2 := 1
  1791. else e2.WindCnt2 := 0;
  1792. end;
  1793. case FFillRule of
  1794. frPositive:
  1795. begin
  1796. e1WindCnt := e1.WindCnt;
  1797. e2WindCnt := e2.WindCnt;
  1798. end;
  1799. frNegative:
  1800. begin
  1801. e1WindCnt := -e1.WindCnt;
  1802. e2WindCnt := -e2.WindCnt;
  1803. end;
  1804. else
  1805. begin
  1806. e1WindCnt := abs(e1.WindCnt);
  1807. e2WindCnt := abs(e2.WindCnt);
  1808. end;
  1809. end;
  1810. if (not IsHotEdge(e1) and not (e1WindCnt in [0,1])) or
  1811. (not IsHotEdge(e2) and not (e2WindCnt in [0,1])) then Exit;
  1812. // NOW PROCESS THE INTERSECTION ...
  1813. // if both edges are 'hot' ...
  1814. if IsHotEdge(e1) and IsHotEdge(e2) then
  1815. begin
  1816. if not (e1WindCnt in [0,1]) or not (e2WindCnt in [0,1]) or
  1817. (not IsSamePolyType(e1, e2) and (fClipType <> ctXor)) then
  1818. begin
  1819. AddLocalMaxPoly(e1, e2, pt);
  1820. end else if IsFront(e1) or (e1.OutRec = e2.OutRec) then
  1821. begin
  1822. AddLocalMaxPoly(e1, e2, pt);
  1823. AddLocalMinPoly(e1, e2, pt);
  1824. end else
  1825. begin
  1826. // right & left bounds touching, NOT maxima & minima ...
  1827. AddOutPt(e1, pt);
  1828. AddOutPt(e2, pt);
  1829. SwapOutRecs(e1, e2);
  1830. end;
  1831. end
  1832. // if one or other edge is 'hot' ...
  1833. else if IsHotEdge(e1) then
  1834. begin
  1835. AddOutPt(e1, pt);
  1836. SwapOutRecs(e1, e2);
  1837. end
  1838. else if IsHotEdge(e2) then
  1839. begin
  1840. AddOutPt(e2, pt);
  1841. SwapOutRecs(e1, e2);
  1842. end
  1843. else // neither edge is 'hot'
  1844. begin
  1845. case FFillRule of
  1846. frPositive:
  1847. begin
  1848. e1WindCnt2 := e1.WindCnt2;
  1849. e2WindCnt2 := e2.WindCnt2;
  1850. end;
  1851. frNegative:
  1852. begin
  1853. e1WindCnt2 := -e1.WindCnt2;
  1854. e2WindCnt2 := -e2.WindCnt2;
  1855. end
  1856. else
  1857. begin
  1858. e1WindCnt2 := abs(e1.WindCnt2);
  1859. e2WindCnt2 := abs(e2.WindCnt2);
  1860. end;
  1861. end;
  1862. if not IsSamePolyType(e1, e2) then
  1863. begin
  1864. AddLocalMinPoly(e1, e2, pt, false, orientationCheckRequired);
  1865. end
  1866. else if (e1WindCnt = 1) and (e2WindCnt = 1) then
  1867. case FClipType of
  1868. ctIntersection:
  1869. if (e1WindCnt2 > 0) and (e2WindCnt2 > 0) then
  1870. AddLocalMinPoly(e1, e2, pt, false, orientationCheckRequired);
  1871. ctUnion:
  1872. if (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0) then
  1873. AddLocalMinPoly(e1, e2, pt, false, orientationCheckRequired);
  1874. ctDifference:
  1875. if ((GetPolyType(e1) = ptClip) and
  1876. (e1WindCnt2 > 0) and (e2WindCnt2 > 0)) or
  1877. ((GetPolyType(e1) = ptSubject) and
  1878. (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0)) then
  1879. AddLocalMinPoly(e1, e2, pt, false, orientationCheckRequired);
  1880. ctXor:
  1881. AddLocalMinPoly(e1, e2, pt, false, orientationCheckRequired);
  1882. end
  1883. end;
  1884. end;
  1885. //------------------------------------------------------------------------------
  1886. procedure TClipper.DeleteFromAEL(e: PActive);
  1887. var
  1888. aelPrev, aelNext: PActive;
  1889. begin
  1890. aelPrev := e.PrevInAEL;
  1891. aelNext := e.NextInAEL;
  1892. if not Assigned(aelPrev) and not Assigned(aelNext) and
  1893. (e <> FActives) then Exit; // already deleted
  1894. if Assigned(aelPrev) then aelPrev.NextInAEL := aelNext
  1895. else FActives := aelNext;
  1896. if Assigned(aelNext) then aelNext.PrevInAEL := aelPrev;
  1897. Dispose(e);
  1898. end;
  1899. //------------------------------------------------------------------------------
  1900. procedure TClipper.AdjustCurrXAndCopyToSEL(topY: Int64);
  1901. var
  1902. e: PActive;
  1903. begin
  1904. FSel := FActives;
  1905. e := FActives;
  1906. while Assigned(e) do
  1907. begin
  1908. e.PrevInSEL := e.PrevInAEL;
  1909. e.NextInSEL := e.NextInAEL;
  1910. e.CurrX := TopX(e, topY);
  1911. e := e.NextInAEL;
  1912. end;
  1913. end;
  1914. //------------------------------------------------------------------------------
  1915. procedure TClipper.ExecuteInternal(clipType: TClipType;
  1916. fillRule: TFillRule);
  1917. var
  1918. Y: Int64;
  1919. e: PActive;
  1920. begin
  1921. if clipType = ctNone then Exit;
  1922. FFillRule := fillRule;
  1923. FClipType := clipType;
  1924. Reset;
  1925. if not PopScanLine(Y) then Exit;
  1926. while true do
  1927. begin
  1928. InsertLocalMinimaIntoAEL(Y);
  1929. while PopHorz(e) do DoHorizontal(e);
  1930. FBotY := Y; // FBotY == bottom of scanbeam
  1931. if not PopScanLine(Y) then Break; // Y new top of scanbeam
  1932. DoIntersections(Y);
  1933. DoTopOfScanbeam(Y);
  1934. end;
  1935. end;
  1936. //------------------------------------------------------------------------------
  1937. function TClipper.Execute(clipType: TClipType;
  1938. fillRule: TFillRule; out closedPaths: TArrayOfArrayOfFloatPoint): Boolean;
  1939. var
  1940. dummy: TArrayOfArrayOfFloatPoint;
  1941. begin
  1942. Result := true;
  1943. closedPaths := nil;
  1944. try try
  1945. ExecuteInternal(clipType, fillRule);
  1946. BuildResult(closedPaths, dummy);
  1947. except
  1948. Result := false;
  1949. end;
  1950. finally
  1951. CleanUp;
  1952. end;
  1953. end;
  1954. //------------------------------------------------------------------------------
  1955. function TClipper.Execute(clipType: TClipType;
  1956. fillRule: TFillRule; out closedPaths: TArrayOfArrayOfFixedPoint): Boolean;
  1957. var
  1958. tmp: TArrayOfArrayOfFloatPoint;
  1959. begin
  1960. Result := Execute(clipType, fillRule, tmp);
  1961. closedPaths := FloatToFixed(tmp);
  1962. end;
  1963. //------------------------------------------------------------------------------
  1964. function TClipper.Execute(clipType: TClipType; fillRule: TFillRule;
  1965. out closedPaths, openPaths: TArrayOfArrayOfFloatPoint): Boolean;
  1966. begin
  1967. Result := true;
  1968. closedPaths := nil;
  1969. openPaths := nil;
  1970. try try
  1971. ExecuteInternal(clipType, fillRule);
  1972. BuildResult(closedPaths, openPaths);
  1973. except
  1974. Result := false;
  1975. end;
  1976. finally
  1977. CleanUp;
  1978. end;
  1979. end;
  1980. //------------------------------------------------------------------------------
  1981. function TClipper.Execute(clipType: TClipType; fillRule: TFillRule;
  1982. out closedPaths, openPaths: TArrayOfArrayOfFixedPoint): Boolean;
  1983. var
  1984. tmp, tmp2: TArrayOfArrayOfFloatPoint;
  1985. begin
  1986. Result := Execute(clipType, fillRule, tmp, tmp2);
  1987. closedPaths := FloatToFixed(tmp);
  1988. openPaths := FloatToFixed(tmp2);
  1989. end;
  1990. //------------------------------------------------------------------------------
  1991. procedure TClipper.DoIntersections(const topY: Int64);
  1992. begin
  1993. if BuildIntersectList(topY) then
  1994. try
  1995. ProcessIntersectList;
  1996. finally
  1997. DisposeIntersectNodes;
  1998. end;
  1999. end;
  2000. //------------------------------------------------------------------------------
  2001. procedure TClipper.DisposeIntersectNodes;
  2002. var
  2003. i: Integer;
  2004. begin
  2005. for i := 0 to FIntersectList.Count - 1 do
  2006. Dispose(IntersectNode[i]);
  2007. FIntersectList.Clear;
  2008. end;
  2009. //------------------------------------------------------------------------------
  2010. procedure TClipper.AddNewIntersectNode(e1, e2: PActive; topY: Int64);
  2011. var
  2012. pt: TPoint64;
  2013. node: PIntersectNode;
  2014. begin
  2015. pt := GetIntersectPoint(e1, e2);
  2016. // Rounding errors can occasionally place the calculated intersection
  2017. // point either below or above the scanbeam, so check and correct ...
  2018. if (pt.Y > FBotY) then
  2019. begin
  2020. // E.Curr.Y is still at the bottom of scanbeam here
  2021. pt.Y := FBotY;
  2022. // use the more vertical of the 2 edges to derive pt.X ...
  2023. if (abs(e1.Dx) < abs(e2.Dx)) then
  2024. pt.X := TopX(e1, FBotY) else
  2025. pt.X := TopX(e2, FBotY);
  2026. end
  2027. else if pt.Y < topY then
  2028. begin
  2029. // TopY = top of scanbeam
  2030. pt.Y := topY;
  2031. if e1.Top.Y = topY then
  2032. pt.X := e1.Top.X
  2033. else if e2.Top.Y = topY then
  2034. pt.X := e2.Top.X
  2035. else if (abs(e1.Dx) < abs(e2.Dx)) then
  2036. pt.X := e1.CurrX
  2037. else
  2038. pt.X := e2.CurrX;
  2039. end;
  2040. new(node);
  2041. node.Edge1 := e1;
  2042. node.Edge2 := e2;
  2043. node.Pt := pt;
  2044. FIntersectList.Add(node);
  2045. end;
  2046. //------------------------------------------------------------------------------
  2047. function TClipper.BuildIntersectList(const topY: Int64): Boolean;
  2048. var
  2049. i, lCnt, rCnt, jumpSize: Integer;
  2050. first, second, base, prevBase, p, n, tmp: PActive;
  2051. begin
  2052. Result := false;
  2053. if not Assigned(FActives) or not Assigned(FActives.NextInAEL) then Exit;
  2054. // Calculate edge positions at the top of the current scanbeam, and from this
  2055. // we will determine the intersections required to reach these new positions.
  2056. AdjustCurrXAndCopyToSEL(topY);
  2057. // Track every edge intersection between the bottom and top of each scanbeam,
  2058. // using a stable merge sort to ensure edges are adjacent when intersecting.
  2059. // Re merge sorts see https://stackoverflow.com/a/46319131/359538
  2060. jumpSize := 1;
  2061. while (true) do
  2062. begin
  2063. first := FSel;
  2064. prevBase := nil;
  2065. // sort successive larger jump counts of nodes ...
  2066. while assigned(first) do
  2067. begin
  2068. if (jumpSize = 1) then
  2069. begin
  2070. second := first.NextInSEL;
  2071. if not assigned(second) then
  2072. begin
  2073. first.Jump := nil;
  2074. break;
  2075. end;
  2076. first.Jump := second.NextInSEL;
  2077. end else
  2078. begin
  2079. second := first.Jump;
  2080. if not assigned(second) then
  2081. begin
  2082. first.Jump := nil;
  2083. break;
  2084. end;
  2085. first.Jump := second.Jump;
  2086. end;
  2087. // now sort first and second groups ...
  2088. base := first;
  2089. lCnt := jumpSize; rCnt := jumpSize;
  2090. while (lCnt > 0) and (rCnt > 0) do
  2091. begin
  2092. if (first.CurrX > second.CurrX) then
  2093. begin
  2094. tmp := second.PrevInSEL;
  2095. // create intersect 'node' events for each time 'second' needs to
  2096. // move left, ie intersecting with its prior edge ...
  2097. for i := 1 to lCnt do
  2098. begin
  2099. AddNewIntersectNode(tmp, second, topY);
  2100. tmp := tmp.PrevInSEL;
  2101. end;
  2102. // now move the out of place 'second' to it's new position in SEL ...
  2103. if (first = base) then
  2104. begin
  2105. if assigned(prevBase) then prevBase.Jump := second;
  2106. base := second;
  2107. base.Jump := first.Jump;
  2108. if (first.PrevInSEL = nil) then FSel := second;
  2109. end;
  2110. tmp := second.NextInSEL;
  2111. // first remove 'second' from list ...
  2112. p := second.PrevInSEL;
  2113. n := second.NextInSEL;
  2114. p.NextInSEL := n;
  2115. if Assigned(n) then n.PrevInSEL := p;
  2116. // and then reinsert 'second' into list just before 'first' ...
  2117. p := first.PrevInSEL;
  2118. if assigned(p) then p.NextInSEL := second;
  2119. first.PrevInSEL := second;
  2120. second.PrevInSEL := p;
  2121. second.NextInSEL := first;
  2122. second := tmp;
  2123. if not assigned(second) then break;
  2124. dec(rCnt);
  2125. end else
  2126. begin
  2127. first := first.NextInSEL;
  2128. dec(lCnt);
  2129. end;
  2130. end;
  2131. first := base.Jump;
  2132. prevBase := base;
  2133. end;
  2134. if FSel.Jump = nil then Break
  2135. else jumpSize := jumpSize shl 1;
  2136. end;
  2137. Result := FIntersectList.Count > 0;
  2138. end;
  2139. //------------------------------------------------------------------------------
  2140. function TClipper.GetIntersectNode(index: Integer): PIntersectNode;
  2141. begin
  2142. Result := PIntersectNode(FIntersectList[index]);
  2143. end;
  2144. //------------------------------------------------------------------------------
  2145. procedure TClipper.ProcessIntersectList;
  2146. var
  2147. i, j, highI: Integer;
  2148. node: PIntersectNode;
  2149. begin
  2150. // We now have a list of intersections required so that edges will be
  2151. // correctly positioned at the top of the scanbeam. However, it's important
  2152. // that edge intersections are processed from the bottom up, but it's also
  2153. // crucial that intersections only occur between adjacent edges.
  2154. // First we do a quicksort so intersections proceed in a bottom up order ...
  2155. FIntersectList.Sort(IntersectListSort);
  2156. // Now as we process these intersections, we must sometimes adjust the order
  2157. // to ensure that intersecting edges are always adjacent ...
  2158. highI := FIntersectList.Count - 1;
  2159. for i := 0 to highI do
  2160. begin
  2161. if not EdgesAdjacentInAEL(FIntersectList[i]) then
  2162. begin
  2163. j := i + 1;
  2164. while not EdgesAdjacentInAEL(FIntersectList[j]) do inc(j);
  2165. // Swap IntersectNodes ...
  2166. node := FIntersectList[i];
  2167. FIntersectList[i] := FIntersectList[j];
  2168. FIntersectList[j] := node;
  2169. end;
  2170. with IntersectNode[i]^ do
  2171. begin
  2172. // Occasionally a non-minima intersection is processed before its own
  2173. // minima. This causes problems with orientation so we need to flag it ...
  2174. if (i < highI) and (IntersectNode[i+1].Pt.Y > Pt.Y) then
  2175. IntersectEdges(Edge1, Edge2, Pt, true) else
  2176. IntersectEdges(Edge1, Edge2, Pt);
  2177. SwapPositionsInAEL(Edge1, Edge2);
  2178. end;
  2179. end;
  2180. end;
  2181. //------------------------------------------------------------------------------
  2182. procedure TClipper.SwapPositionsInAEL(e1, e2: PActive);
  2183. var
  2184. prev, next: PActive;
  2185. begin
  2186. // preconditon: e1 must be immediately to the left of e2
  2187. next := e2.NextInAEL;
  2188. if Assigned(next) then next.PrevInAEL := e1;
  2189. prev := e1.PrevInAEL;
  2190. if Assigned(prev) then prev.NextInAEL := e2;
  2191. e2.PrevInAEL := prev;
  2192. e2.NextInAEL := e1;
  2193. e1.PrevInAEL := e2;
  2194. e1.NextInAEL := next;
  2195. if not Assigned(e2.PrevInAEL) then FActives := e2;
  2196. end;
  2197. //------------------------------------------------------------------------------
  2198. procedure TClipper.DoHorizontal(horzEdge: PActive);
  2199. var
  2200. e, maxPair: PActive;
  2201. horzLeft, horzRight: Int64;
  2202. isLeftToRight: Boolean;
  2203. pt: TPoint64;
  2204. isMax: Boolean;
  2205. procedure ResetHorzDirection;
  2206. var
  2207. e: PActive;
  2208. begin
  2209. if (horzEdge.Bot.X = horzEdge.Top.X) then
  2210. begin
  2211. // the horizontal edge is going nowhere ...
  2212. horzLeft := horzEdge.CurrX;
  2213. horzRight := horzEdge.CurrX;
  2214. e := horzEdge.NextInAEL;
  2215. while assigned(e) and (e <> maxPair) do
  2216. e := e.NextInAEL;
  2217. isLeftToRight := assigned(e);
  2218. end
  2219. else if horzEdge.CurrX < horzEdge.Top.X then
  2220. begin
  2221. horzLeft := horzEdge.CurrX;
  2222. horzRight := horzEdge.Top.X;
  2223. isLeftToRight := true;
  2224. end else
  2225. begin
  2226. horzLeft := horzEdge.Top.X;
  2227. horzRight := horzEdge.CurrX;
  2228. isLeftToRight := false;
  2229. end;
  2230. end;
  2231. //------------------------------------------------------------------------
  2232. begin
  2233. (*******************************************************************************
  2234. * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or *
  2235. * bottom of a scanbeam) are processed as if layered. The order in which HEs *
  2236. * are processed doesn't matter. HEs intersect with the bottom vertices of *
  2237. * other HEs [#] and with non-horizontal edges [*]. Once these intersections *
  2238. * are completed, intermediate HEs are 'promoted' to the next edge in their *
  2239. * bounds, and they in turn may be intersected [%] by other HEs. *
  2240. * *
  2241. * eg: 3 horizontals at a scanline: / | / / *
  2242. * | / | (HE3) o=========%==========o *
  2243. * o=======o (HE2) / | / / *
  2244. * o============#=========*======*========#=========o (HE1) *
  2245. * / | / | / *
  2246. *******************************************************************************)
  2247. // with closed paths, simplify consecutive horizontals into a 'single' edge
  2248. if not IsOpen(horzEdge) then
  2249. begin
  2250. pt := horzEdge.Bot;
  2251. while not IsMaxima(horzEdge) and
  2252. (NextVertex(horzEdge).Pt.Y = pt.Y) do
  2253. UpdateEdgeIntoAEL(horzEdge);
  2254. horzEdge.Bot := pt;
  2255. horzEdge.CurrX := pt.X;
  2256. // update Dx in case of direction change ...
  2257. if horzEdge.Bot.X < horzEdge.Top.X then
  2258. horzEdge.Dx := NegInfinity else
  2259. horzEdge.Dx := Infinity;
  2260. end;
  2261. maxPair := nil;
  2262. if IsMaxima(horzEdge) and (not IsOpen(horzEdge) or
  2263. ([vfOpenStart, vfOpenEnd] * horzEdge.vertTop.flags = [])) then
  2264. maxPair := GetMaximaPair(horzEdge);
  2265. ResetHorzDirection;
  2266. if IsHotEdge(horzEdge) then
  2267. AddOutPt(horzEdge, Point64(horzEdge.CurrX, horzEdge.Bot.Y));
  2268. while true do // loops through consec. horizontal edges (if open)
  2269. begin
  2270. isMax := IsMaxima(horzEdge);
  2271. if isLeftToRight then
  2272. e := horzEdge.NextInAEL else
  2273. e := horzEdge.PrevInAEL;
  2274. while assigned(e) do
  2275. begin
  2276. // Break if we've gone past the end of the horizontal ...
  2277. if (isLeftToRight and (e.CurrX > horzRight)) or
  2278. (not isLeftToRight and (e.CurrX < horzLeft)) then Break;
  2279. // or if we've got to the end of an intermediate horizontal edge ...
  2280. if (E.CurrX = horzEdge.Top.X) and not isMax and not IsHorizontal(e) then
  2281. begin
  2282. pt := NextVertex(horzEdge).Pt;
  2283. if(isLeftToRight and (TopX(E, pt.Y) >= pt.X)) or
  2284. (not isLeftToRight and (TopX(E, pt.Y) <= pt.X)) then Break;
  2285. end;
  2286. if (e = maxPair) then
  2287. begin
  2288. if IsHotEdge(horzEdge) then
  2289. begin
  2290. if isLeftToRight then
  2291. AddLocalMaxPoly(horzEdge, e, horzEdge.Top) else
  2292. AddLocalMaxPoly(e, horzEdge, horzEdge.Top);
  2293. end;
  2294. DeleteFromAEL(e);
  2295. DeleteFromAEL(horzEdge);
  2296. Exit;
  2297. end;
  2298. pt := Point64(e.CurrX, horzEdge.Bot.Y);
  2299. if (isLeftToRight) then
  2300. begin
  2301. IntersectEdges(horzEdge, e, pt);
  2302. SwapPositionsInAEL(horzEdge, e);
  2303. e := horzEdge.NextInAEL;
  2304. end else
  2305. begin
  2306. IntersectEdges(e, horzEdge, pt);
  2307. SwapPositionsInAEL(e, horzEdge);
  2308. e := horzEdge.PrevInAEL;
  2309. end;
  2310. end;
  2311. // check if we've finished with (consecutive) horizontals ...
  2312. if isMax or (NextVertex(horzEdge).Pt.Y <> horzEdge.Top.Y) then Break;
  2313. // still more horizontals in bound to process ...
  2314. UpdateEdgeIntoAEL(horzEdge);
  2315. ResetHorzDirection;
  2316. if IsOpen(horzEdge) then
  2317. begin
  2318. if IsMaxima(horzEdge) then maxPair := GetMaximaPair(horzEdge);
  2319. if IsHotEdge(horzEdge) then AddOutPt(horzEdge, horzEdge.Bot);
  2320. end;
  2321. end;
  2322. if IsHotEdge(horzEdge) then
  2323. AddOutPt(horzEdge, horzEdge.Top);
  2324. if not IsOpen(horzEdge) then
  2325. UpdateEdgeIntoAEL(horzEdge) // this is the end of an intermediate horiz.
  2326. else if not IsMaxima(horzEdge) then
  2327. UpdateEdgeIntoAEL(horzEdge)
  2328. else if not assigned(maxPair) then // ie open at top
  2329. DeleteFromAEL(horzEdge)
  2330. else if IsHotEdge(horzEdge) then
  2331. AddLocalMaxPoly(horzEdge, maxPair, horzEdge.Top)
  2332. else
  2333. begin
  2334. DeleteFromAEL(maxPair); DeleteFromAEL(horzEdge);
  2335. end;
  2336. end;
  2337. //------------------------------------------------------------------------------
  2338. procedure TClipper.DoTopOfScanbeam(Y: Int64);
  2339. var
  2340. e: PActive;
  2341. begin
  2342. FSel := nil; // FSel is reused to flag horizontals (see PushHorz below)
  2343. e := FActives;
  2344. while Assigned(e) do
  2345. begin
  2346. // nb: 'e' will never be horizontal here
  2347. if (e.Top.Y = Y) then
  2348. begin
  2349. // the following helps to avoid micro self-intersections
  2350. // with negligible impact on performance ...
  2351. e.CurrX := e.Top.X;
  2352. if assigned(e.PrevInAEL) and (e.PrevInAEL.CurrX = e.CurrX) and
  2353. (e.PrevInAEL.Bot.Y <> Y) and IsHotEdge(e.PrevInAEL) then
  2354. AddOutPt(e.PrevInAEL, e.Top);
  2355. if assigned(e.NextInAEL) and (e.NextInAEL.CurrX = e.CurrX) and
  2356. (e.NextInAEL.Top.Y <> Y) and IsHotEdge(e.NextInAEL) then
  2357. AddOutPt(e.NextInAEL, e.Top);
  2358. if IsMaxima(e) then
  2359. begin
  2360. e := DoMaxima(e); // TOP OF BOUND (MAXIMA)
  2361. Continue;
  2362. end else
  2363. begin
  2364. // INTERMEDIATE VERTEX ...
  2365. UpdateEdgeIntoAEL(e);
  2366. if IsHotEdge(e) then AddOutPt(e, e.Bot);
  2367. if IsHorizontal(e) then
  2368. PushHorz(e); // horizontals are processed later
  2369. end;
  2370. end;
  2371. e := e.NextInAEL;
  2372. end;
  2373. end;
  2374. //------------------------------------------------------------------------------
  2375. function TClipper.DoMaxima(e: PActive): PActive;
  2376. var
  2377. eNext, ePrev, eMaxPair: PActive;
  2378. begin
  2379. ePrev := e.PrevInAEL;
  2380. eNext := e.NextInAEL;
  2381. Result := eNext;
  2382. if IsOpen(e) and ([vfOpenStart, vfOpenEnd] * e.vertTop.flags <> []) then
  2383. begin
  2384. if IsHotEdge(e) then AddOutPt(e, e.Top);
  2385. if not IsHorizontal(e) then
  2386. begin
  2387. if IsHotEdge(e) then TerminateHotOpen(e);
  2388. DeleteFromAEL(e);
  2389. end;
  2390. Exit;
  2391. end else
  2392. begin
  2393. eMaxPair := GetMaximaPair(e);
  2394. if not assigned(eMaxPair) then Exit; // EMaxPair is a horizontal ...
  2395. end;
  2396. // only non-horizontal maxima here.
  2397. // process any edges between maxima pair ...
  2398. while (eNext <> eMaxPair) do
  2399. begin
  2400. IntersectEdges(e, eNext, e.Top);
  2401. SwapPositionsInAEL(e, eNext);
  2402. eNext := e.NextInAEL;
  2403. end;
  2404. if IsOpen(e) then
  2405. begin
  2406. if IsHotEdge(e) then
  2407. begin
  2408. if assigned(eMaxPair) then
  2409. AddLocalMaxPoly(e, eMaxPair, e.Top) else
  2410. AddOutPt(e, e.Top);
  2411. end;
  2412. if assigned(eMaxPair) then
  2413. DeleteFromAEL(eMaxPair);
  2414. DeleteFromAEL(e);
  2415. if assigned(ePrev) then
  2416. Result := ePrev.NextInAEL else
  2417. Result := FActives;
  2418. Exit;
  2419. end;
  2420. // here E.NextInAEL == ENext == EMaxPair ...
  2421. if IsHotEdge(e) then
  2422. AddLocalMaxPoly(e, eMaxPair, e.Top);
  2423. DeleteFromAEL(e);
  2424. DeleteFromAEL(eMaxPair);
  2425. if assigned(ePrev) then
  2426. Result := ePrev.NextInAEL else
  2427. Result := FActives;
  2428. end;
  2429. //------------------------------------------------------------------------------
  2430. function TClipper.BuildResult(out closedPaths,
  2431. openPaths: TArrayOfArrayOfFloatPoint): Boolean;
  2432. var
  2433. i, j, cntClosed, cntOpen: Integer;
  2434. outRec: TOutRec;
  2435. begin
  2436. try
  2437. cntClosed := 0; cntOpen := 0;
  2438. SetLength(closedPaths, FOutRecList.Count);
  2439. SetLength(openPaths, FOutRecList.Count);
  2440. for i := 0 to FOutRecList.Count -1 do
  2441. begin
  2442. outRec := FOutRecList[i];
  2443. if not assigned(outRec.Pts) then Continue;
  2444. if IsOpen(outRec) then
  2445. begin
  2446. openPaths[cntOpen] := BuildPath(outRec.Pts);
  2447. if length(openPaths[cntOpen]) > 1 then inc(cntOpen);
  2448. end else
  2449. begin
  2450. closedPaths[cntClosed] := BuildPath(outRec.Pts);
  2451. j := high(closedPaths[cntClosed]);
  2452. if (j > 1) and PointsEqual(closedPaths[cntClosed][0],
  2453. closedPaths[cntClosed][j]) then
  2454. setlength(closedPaths[cntClosed], j);
  2455. if j > 1 then inc(cntClosed);
  2456. end;
  2457. end;
  2458. SetLength(closedPaths, cntClosed);
  2459. SetLength(openPaths, cntOpen);
  2460. Result := true;
  2461. except
  2462. Result := false;
  2463. end;
  2464. end;
  2465. //------------------------------------------------------------------------------
  2466. function TClipper.GetBounds: TFloatRect;
  2467. var
  2468. i: Integer;
  2469. v, vStart: PVertex;
  2470. begin
  2471. if FVertexList.Count = 0 then
  2472. Result := FloatRect(0, 0, 0, 0)
  2473. else
  2474. with PVertex(FVertexList[0]).Pt do
  2475. Result := FloatRect(X, Y, X, Y);
  2476. for i := 0 to FVertexList.Count -1 do
  2477. begin
  2478. vStart := FVertexList[i];
  2479. v := vStart;
  2480. repeat
  2481. if v.Pt.X < Result.Left then Result.Left := v.Pt.X
  2482. else if v.Pt.X > Result.Right then Result.Right := v.Pt.X;
  2483. if v.Pt.Y < Result.Top then Result.Top := v.Pt.Y
  2484. else if v.Pt.Y > Result.Bottom then Result.Bottom := v.Pt.Y;
  2485. v := v.next;
  2486. until v = vStart;
  2487. end;
  2488. end;
  2489. //------------------------------------------------------------------------------
  2490. // Miscellaneous ClipperOffset support functions
  2491. //------------------------------------------------------------------------------
  2492. const
  2493. MinFloat = -3.49E38;
  2494. MaxFloat = 3.49E38;
  2495. procedure AppendPath(var paths: TArrayOfArrayOfFloatPoint;
  2496. const extra: TArrayOfFloatPoint);
  2497. var
  2498. len: Integer;
  2499. begin
  2500. len := length(paths);
  2501. SetLength(paths, len +1);
  2502. paths[len] := extra;
  2503. end;
  2504. //------------------------------------------------------------------------------
  2505. procedure StripDuplicates(var path: TArrayOfFloatPoint);
  2506. var
  2507. i, len: integer;
  2508. begin
  2509. len := length(path);
  2510. i := 1;
  2511. while i < len do
  2512. begin
  2513. if (path[i].X = path[i-1].X) and (path[i].Y = path[i-1].Y) then
  2514. begin
  2515. dec(len);
  2516. if (i < len) then
  2517. Move(path[i+1], path[i], (len-i)*SizeOf(TFloatPoint));
  2518. SetLength(path, len);
  2519. end else
  2520. inc(i);
  2521. end;
  2522. end;
  2523. //------------------------------------------------------------------------------
  2524. function ReversePath(const path: TArrayOfFloatPoint): TArrayOfFloatPoint;
  2525. var
  2526. i, highI: Integer;
  2527. begin
  2528. highI := high(path);
  2529. SetLength(Result, highI +1);
  2530. for i := 0 to highI do
  2531. Result[i] := path[highI - i];
  2532. end;
  2533. //------------------------------------------------------------------------------
  2534. function DistanceSqr(const pt1, pt2: TFloatPoint): TFloat;
  2535. begin
  2536. Result := (pt1.X - pt2.X)*(pt1.X - pt2.X) + (pt1.Y - pt2.Y)*(pt1.Y - pt2.Y);
  2537. end;
  2538. //------------------------------------------------------------------------------
  2539. function GetUnitNormal(const pt1, pt2: TFloatPoint): TFloatPoint;
  2540. var
  2541. dx, dy, inverseHypot: Double;
  2542. begin
  2543. if PointsEqual(pt1, pt2) then
  2544. begin
  2545. Result.X := 0;
  2546. Result.Y := 0;
  2547. Exit;
  2548. end;
  2549. dx := (pt2.X - pt1.X);
  2550. dy := (pt2.Y - pt1.Y);
  2551. inverseHypot := 1 / Hypot(dx, dy);
  2552. dx := dx * inverseHypot;
  2553. dy := dy * inverseHypot;
  2554. Result.X := dy;
  2555. Result.Y := -dx
  2556. end;
  2557. //------------------------------------------------------------------------------
  2558. // TClipperOffset methods
  2559. //------------------------------------------------------------------------------
  2560. constructor TClipperOffset.Create(MiterLimit: Double; ArcTolerance: Double);
  2561. begin
  2562. inherited Create;
  2563. if MiterLimit = 0 then MiterLimit := 2;
  2564. FMiterLimit := MiterLimit;
  2565. FArcTolerance := ArcTolerance;
  2566. end;
  2567. //------------------------------------------------------------------------------
  2568. destructor TClipperOffset.Destroy;
  2569. begin
  2570. Clear;
  2571. inherited;
  2572. end;
  2573. //------------------------------------------------------------------------------
  2574. procedure TClipperOffset.Clear;
  2575. begin
  2576. FPathsIn := nil;
  2577. FNorms := nil;
  2578. FSolution := nil;
  2579. end;
  2580. //------------------------------------------------------------------------------
  2581. procedure TClipperOffset.AddPath(const path: TArrayOfFloatPoint);
  2582. begin
  2583. if assigned(path) then
  2584. AppendPath(FPathsIn, path);
  2585. end;
  2586. //------------------------------------------------------------------------------
  2587. procedure TClipperOffset.AddPaths(const paths: TArrayOfArrayOfFloatPoint);
  2588. var
  2589. i: Integer;
  2590. begin
  2591. for i := 0 to High(paths) do
  2592. AddPath(paths[i]);
  2593. end;
  2594. //------------------------------------------------------------------------------
  2595. function TClipperOffset.GetLowestPolygonIdx: integer;
  2596. var
  2597. i,j, len: Integer;
  2598. pt: TFloatPoint;
  2599. p: TArrayOfFloatPoint;
  2600. begin
  2601. result := -1;
  2602. pt := FloatPoint(MaxFloat, MinFloat);
  2603. for i := 0 to high(FPathsIn) do
  2604. begin
  2605. if FPathsIn[i] = nil then
  2606. Continue;
  2607. p := FPathsIn[i];
  2608. len := length(p);
  2609. for j := 0 to len -1 do
  2610. begin
  2611. if (p[j].Y < pt.Y) then
  2612. continue;
  2613. if (p[j].Y > pt.Y) or (p[j].X < pt.X) then
  2614. begin
  2615. pt := p[j];
  2616. result := i;
  2617. end;
  2618. end;
  2619. end;
  2620. end;
  2621. //------------------------------------------------------------------------------
  2622. function TClipperOffset.CheckPaths: boolean;
  2623. var
  2624. i,len, minLen: Integer;
  2625. openPaths: Boolean;
  2626. begin
  2627. Result := False;
  2628. openPaths := not (FEndType in [etPolygon, etOpenJoined]);
  2629. if openPaths then minLen := 1 else minLen := 3;
  2630. for i := 0 to high(FPathsIn) do
  2631. begin
  2632. StripDuplicates(FPathsIn[i]);
  2633. len := length(FPathsIn[i]);
  2634. if not openPaths and (len > 1) and
  2635. PointsEqual(FPathsIn[i][0], FPathsIn[i][len-1]) then
  2636. begin
  2637. setlength(FPathsIn[i], len -1);
  2638. dec(len);
  2639. end;
  2640. if len < minLen then
  2641. FPathsIn[i] := nil
  2642. else
  2643. Result := True;
  2644. end;
  2645. end;
  2646. //------------------------------------------------------------------------------
  2647. procedure TClipperOffset.OffsetPaths;
  2648. var
  2649. i, len: Integer;
  2650. arcTol, absDelta, steps: Double;
  2651. tmpEndType: TEndType;
  2652. begin
  2653. absDelta := Abs(FDelta);
  2654. len := length(FPathsIn);
  2655. // if a Zero offset, then simply copy paths to FSolution and return ...
  2656. if absDelta < Tolerance then
  2657. begin
  2658. FSolutionLen := 0;
  2659. SetLength(FSolution, len);
  2660. for i := 0 to high(FPathsIn) do
  2661. if assigned(FPathsIn[i]) then
  2662. begin
  2663. FSolution[FSolutionLen] := FPathsIn[i];
  2664. inc(FSolutionLen);
  2665. end;
  2666. SetLength(FSolution, FSolutionLen);
  2667. Exit;
  2668. end;
  2669. // FMiterLimit: see offset_triginometry3.svg
  2670. if FMiterLimit > 1 then FMiterLim := 2/(sqr(FMiterLimit))
  2671. else FMiterLim := 2;
  2672. if (FArcTolerance <= DefaultArcFrac) then
  2673. arcTol := DefaultArcFrac else
  2674. arcTol := FArcTolerance;
  2675. if (FJoinType in [jtRound, jtRoundEx]) or (FEndType = etOpenRound) then
  2676. begin
  2677. // get steps per 360 degrees (see offset_triginometry2.svg)
  2678. steps := PI / ArcCos(1 - arcTol / absDelta);
  2679. // avoid excessive precision ...
  2680. if (steps > absDelta * Pi) then steps := absDelta * Pi;
  2681. FStepsPerRad := steps / Two_Pi;
  2682. Math.SinCos(Two_Pi / steps, FStepSizeSin, FStepSizeCos);
  2683. if FDelta < 0 then FStepSizeSin := -FStepSizeSin;
  2684. end;
  2685. if (FEndType = etOpenJoined) then
  2686. SetLength(FSolution, len *2) else
  2687. SetLength(FSolution, len);
  2688. FSolutionLen := 0;
  2689. for i := 0 to len -1 do
  2690. begin
  2691. FPathIn := FPathsIn[i];
  2692. if FPathIn = nil then Continue;
  2693. FPathOutLen := 0;
  2694. FPathOut := nil;
  2695. if Length(FPathIn) = 1 then
  2696. begin
  2697. // a simple workaround using OffsetOpenPath to construct
  2698. // either a circle or a square point offset ...
  2699. tmpEndType := FEndType;
  2700. if FEndType = etOpenButt then FEndType := etOpenSquare;
  2701. SetLength(FPathIn, 2);
  2702. FPathIn[1] := FPathIn[0];
  2703. SetLength(FNorms, 2);
  2704. FNorms[0] := FloatPoint(1,0);
  2705. OffsetOpenPath;
  2706. FEndType := tmpEndType;
  2707. end else
  2708. begin
  2709. BuildNormals;
  2710. if FEndType = etPolygon then
  2711. OffsetPolygon
  2712. else if FEndType = etOpenJoined then
  2713. OffsetOpenJoined
  2714. else
  2715. OffsetOpenPath;
  2716. end;
  2717. if FPathOutLen = 0 then Continue;
  2718. SetLength(FPathOut, FPathOutLen);
  2719. FSolution[FSolutionLen] := FPathOut;
  2720. Inc(FSolutionLen);
  2721. end;
  2722. SetLength(FSolution, FSolutionLen);
  2723. end;
  2724. //------------------------------------------------------------------------------
  2725. procedure TClipperOffset.BuildNormals;
  2726. var
  2727. i, len: integer;
  2728. begin
  2729. len := Length(FPathIn);
  2730. SetLength(FNorms, len);
  2731. for i := 0 to len-2 do
  2732. FNorms[i] := GetUnitNormal(FPathIn[i], FPathIn[i+1]);
  2733. FNorms[len -1] := GetUnitNormal(FPathIn[len -1], FPathIn[0]);
  2734. end;
  2735. //------------------------------------------------------------------------------
  2736. procedure TClipperOffset.ReverseNormals;
  2737. var
  2738. i, highI: integer;
  2739. tmp: TFloatPoint;
  2740. begin
  2741. FNorms := ReversePath(FNorms);
  2742. highI := high(FNorms);
  2743. tmp := FNorms[0];
  2744. for i := 1 to highI do
  2745. begin
  2746. FNorms[i-1].X := -FNorms[i].X;
  2747. FNorms[i-1].Y := -FNorms[i].Y;
  2748. end;
  2749. FNorms[highI].X := -tmp.X;
  2750. FNorms[highI].Y := -tmp.Y;
  2751. end;
  2752. //------------------------------------------------------------------------------
  2753. procedure TClipperOffset.OffsetPolygon;
  2754. var
  2755. i,j: integer;
  2756. begin
  2757. j := high(FPathIn);
  2758. for i := 0 to high(FPathIn) do
  2759. begin
  2760. OffsetPoint(i, j);
  2761. j := i;
  2762. end;
  2763. end;
  2764. //------------------------------------------------------------------------------
  2765. procedure TClipperOffset.OffsetOpenJoined;
  2766. begin
  2767. OffsetPolygon;
  2768. FPathIn := ReversePath(FPathIn);
  2769. SetLength(FPathOut, FPathOutLen);
  2770. FSolution[FSolutionLen] := FPathOut;
  2771. Inc(FSolutionLen);
  2772. FPathOutLen := 0;
  2773. FPathOut := nil;
  2774. ReverseNormals;
  2775. OffsetPolygon;
  2776. end;
  2777. //------------------------------------------------------------------------------
  2778. procedure TClipperOffset.OffsetOpenPath;
  2779. procedure DoButtCap(highI: integer);
  2780. begin
  2781. AddPoint(FloatPoint(FPathIn[highI].X + FNorms[highI-1].X *FDelta,
  2782. FPathIn[highI].Y + FNorms[highI-1].Y * FDelta));
  2783. AddPoint(FloatPoint(FPathIn[highI].X - FNorms[highI-1].X *FDelta,
  2784. FPathIn[highI].Y - FNorms[highI-1].Y * FDelta));
  2785. end;
  2786. procedure DoSquareCap(highI: integer; toStart: Boolean);
  2787. var
  2788. pt: TFloatPoint;
  2789. const
  2790. sc: array[boolean] of integer = (1, -1);
  2791. begin
  2792. pt := FloatPoint(FPathIn[highI].X + FNorms[highI-1].X *FDelta,
  2793. FPathIn[highI].Y + FNorms[highI-1].Y * FDelta);
  2794. AddPoint(pt);
  2795. AddPoint(FloatPoint(pt.X - FNorms[highI-1].Y *FDelta,
  2796. pt.Y - FNorms[highI-1].X * FDelta * sc[true]));
  2797. pt := FloatPoint(FPathIn[highI].X - FNorms[highI-1].X *FDelta,
  2798. FPathIn[highI].Y - FNorms[highI-1].Y * FDelta);
  2799. AddPoint(FloatPoint(pt.X - FNorms[highI-1].Y *FDelta,
  2800. pt.Y - FNorms[highI-1].X * FDelta * sc[true]));
  2801. AddPoint(pt);
  2802. end;
  2803. procedure DoRoundCap(highI: integer); // 180 degrees
  2804. var
  2805. i: integer;
  2806. steps: Integer;
  2807. pt: TFloatPoint;
  2808. begin
  2809. steps := Round(FStepsPerRad * PI);
  2810. pt.X := FNorms[highI-1].X * FDelta;
  2811. pt.Y := FNorms[highI-1].Y * FDelta;
  2812. for i := 1 to steps do
  2813. begin
  2814. AddPoint(FloatPoint(FPathIn[highI].X + pt.X, FPathIn[highI].Y + pt.Y));
  2815. pt := FloatPoint(pt.X * FStepSizeCos - FStepSizeSin * pt.Y,
  2816. pt.X * FStepSizeSin + pt.Y * FStepSizeCos);
  2817. end;
  2818. end;
  2819. var
  2820. i,j, highI: integer;
  2821. begin
  2822. highI := high(FPathIn);
  2823. j := 0;
  2824. for i := 1 to highI -1 do
  2825. begin
  2826. OffsetPoint(i, j);
  2827. j := i;
  2828. end;
  2829. // cap the end first ...
  2830. case FEndType of
  2831. etOpenButt: DoButtCap(highI);
  2832. etOpenRound: DoRoundCap(highI);
  2833. else DoSquareCap(highI, false);
  2834. end;
  2835. FPathIn := ReversePath(FPathIn);
  2836. ReverseNormals;
  2837. j := 0;
  2838. for i := 0 to highI -1 do
  2839. begin
  2840. OffsetPoint(i, j);
  2841. j := i;
  2842. end;
  2843. // now cap the start ...
  2844. case FEndType of
  2845. etOpenButt: DoButtCap(highI);
  2846. etOpenRound: DoRoundCap(highI);
  2847. else DoSquareCap(highI, true);
  2848. end;
  2849. end;
  2850. //------------------------------------------------------------------------------
  2851. procedure TClipperOffset.Execute(delta: Double; jt: TJoinType; et: TEndType;
  2852. out solution: TArrayOfArrayOfFloatPoint);
  2853. var
  2854. negate: Boolean;
  2855. lowestIdx: integer;
  2856. begin
  2857. solution := nil;
  2858. if length(FPathsIn) = 0 then Exit;
  2859. FJoinType := jt;
  2860. FEndType := et;
  2861. if (not CheckPaths) then
  2862. exit;
  2863. negate := false;
  2864. if (et = etPolygon) then
  2865. begin
  2866. // the lowermost polygon must be an outer polygon. So we can use that as the
  2867. // designated orientation for outer polygons (needed for tidy-up clipping)
  2868. lowestIdx := GetLowestPolygonIdx;
  2869. negate := (Area(FPathsIn[lowestIdx]) < 0);
  2870. // if polygon orientations are reversed, then 'negate' ...
  2871. // if negate then FDelta := FDelta;
  2872. end;
  2873. if FEndType <> etPolygon then
  2874. FDelta := Abs(delta) else
  2875. FDelta := delta;
  2876. OffsetPaths;
  2877. // solution := FSolution;
  2878. // clean up self-intersections ...
  2879. with TClipper.Create do
  2880. try
  2881. AddPaths(FSolution, ptSubject);
  2882. if negate then
  2883. Execute(ctUnion, frNegative, solution) else
  2884. Execute(ctUnion, frPositive, solution);
  2885. finally
  2886. free;
  2887. end;
  2888. end;
  2889. //------------------------------------------------------------------------------
  2890. procedure TClipperOffset.AddPoint(const pt: TFloatPoint);
  2891. const
  2892. BuffLength = 32;
  2893. begin
  2894. if FPathOutLen = length(FPathOut) then
  2895. SetLength(FPathOut, FPathOutLen + BuffLength);
  2896. if (FPathOutLen > 0) and PointsEqual(FPathOut[FPathOutLen-1], pt) then Exit;
  2897. FPathOut[FPathOutLen] := pt;
  2898. Inc(FPathOutLen);
  2899. end;
  2900. //------------------------------------------------------------------------------
  2901. procedure TClipperOffset.DoSquare(j, k: Integer);
  2902. begin
  2903. // Two vertices, one using the prior offset's (k) normal one the current (j).
  2904. // Do a 'normal' offset (by delta) and then another by 'de-normaling' the
  2905. // normal hence parallel to the direction of the respective edges.
  2906. if FDelta > 0 then
  2907. begin
  2908. AddPoint(FloatPoint(
  2909. FPathIn[j].X + FDelta * (FNorms[k].X - FNorms[k].Y),
  2910. FPathIn[j].Y + FDelta * (FNorms[k].Y + FNorms[k].X)));
  2911. AddPoint(FloatPoint(
  2912. FPathIn[j].X + FDelta * (FNorms[j].X + FNorms[j].Y),
  2913. FPathIn[j].Y + FDelta * (FNorms[j].Y - FNorms[j].X)));
  2914. end else
  2915. begin
  2916. AddPoint(FloatPoint(
  2917. FPathIn[j].X + FDelta * (FNorms[k].X + FNorms[k].Y),
  2918. FPathIn[j].Y + FDelta * (FNorms[k].Y - FNorms[k].X)));
  2919. AddPoint(FloatPoint(
  2920. FPathIn[j].X + FDelta * (FNorms[j].X - FNorms[j].Y),
  2921. FPathIn[j].Y + FDelta * (FNorms[j].Y + FNorms[j].X)));
  2922. end;
  2923. end;
  2924. //------------------------------------------------------------------------------
  2925. procedure TClipperOffset.DoMiter(j, k: Integer; cosAplus1: Double);
  2926. var
  2927. q: Double;
  2928. begin
  2929. // see offset_triginometry4.svg
  2930. q := FDelta / cosAplus1; // 0 < cosAplus1 <= 2
  2931. AddPoint(FloatPoint(FPathIn[j].X + (FNorms[k].X + FNorms[j].X)*q,
  2932. FPathIn[j].Y + (FNorms[k].Y + FNorms[j].Y)*q));
  2933. end;
  2934. //------------------------------------------------------------------------------
  2935. procedure TClipperOffset.DoRound(j, k: Integer);
  2936. var
  2937. i, m,n, steps: Integer;
  2938. a, delta, sinA, cosA: Double;
  2939. pt, pt2, pt3: TFloatPoint;
  2940. begin
  2941. sinA := FNorms[k].X * FNorms[j].Y - FNorms[k].Y * FNorms[j].X;
  2942. cosA := FNorms[j].X * FNorms[k].X + FNorms[j].Y * FNorms[k].Y;
  2943. a := ArcTan2(sinA, cosA);
  2944. steps := Round(FStepsPerRad * Abs(a));
  2945. if (FDelta * sinA < 0) then // ie concave
  2946. begin
  2947. a := FDelta / (cosA +1);
  2948. if (j = 0) then m := high(FPathIn) else m := j -1;
  2949. if j = high(FPathIn) then n := 0 else n := j +1;
  2950. // offset pt of concave vertex ...
  2951. pt.X := round(FPathIn[j].X + (FNorms[k].X + FNorms[j].X)*a);
  2952. pt.Y := round(FPathIn[j].Y + (FNorms[k].Y + FNorms[j].Y)*a);
  2953. a := Min(DistanceSqr(FPathIn[m], FPathIn[j]),
  2954. DistanceSqr(FPathIn[n], FPathIn[j]));
  2955. // there's no space to draw anything ...
  2956. if DistanceSqr(pt, FPathIn[j]) > a then
  2957. begin
  2958. // get the perpendicular offsets from pt2 ...
  2959. // this creates a self-intersection that'll be clipped later
  2960. pt2.X := round(FPathIn[j].X + FNorms[k].X * FDelta);
  2961. pt2.Y := round(FPathIn[j].Y + FNorms[k].Y * FDelta);
  2962. pt3.X := round(FPathIn[j].X + FNorms[j].X * FDelta);
  2963. pt3.Y := round(FPathIn[j].Y + FNorms[j].Y * FDelta);
  2964. AddPoint(pt2);
  2965. AddPoint(pt3);
  2966. Exit;
  2967. end;
  2968. a := Sqrt(a);
  2969. // get the point on each edge being the distance of the shortest edge
  2970. // from the concave vertex. (nb: unit normals to unit vectors here)
  2971. pt2.X := round(FPathIn[j].X + FNorms[k].Y * a);
  2972. pt2.Y := round(FPathIn[j].Y - FNorms[k].X * a);
  2973. pt3.X := round(FPathIn[j].X - FNorms[j].Y * a);
  2974. pt3.Y := round(FPathIn[j].Y + FNorms[j].X * a);
  2975. // now FDelta offset these points ...
  2976. pt2.X := round(pt2.X + FNorms[k].X * FDelta);
  2977. pt2.Y := round(pt2.Y + FNorms[k].Y * FDelta);
  2978. pt3.X := round(pt3.X + FNorms[j].X * FDelta);
  2979. pt3.Y := round(pt3.Y + FNorms[j].Y * FDelta);
  2980. if DistanceSqr(pt2, pt3) < Sqr(FDelta *2/MiterLimit) then
  2981. delta := Sqrt(DistanceSqr(pt2, pt3))/2 else
  2982. delta := FDelta/MiterLimit;
  2983. a := (delta + FDelta) / (cosA +1);
  2984. pt.X := round(FPathIn[j].X + (FNorms[k].X + FNorms[j].X)*a);
  2985. pt.Y := round(FPathIn[j].Y + (FNorms[k].Y + FNorms[j].Y)*a);
  2986. pt2.X := -FNorms[k].X * delta;
  2987. pt2.Y := -FNorms[k].Y * delta;
  2988. AddPoint(FloatPoint(pt.X + pt2.X, pt.Y + pt2.Y));
  2989. for i := 1 to steps -1 do
  2990. begin
  2991. pt2 := FloatPoint(pt2.X * FStepSizeCos + FStepSizeSin * pt2.Y,
  2992. -pt2.X * FStepSizeSin + pt2.Y * FStepSizeCos);
  2993. AddPoint(FloatPoint(pt.X + pt2.X, pt.Y + pt2.Y));
  2994. end;
  2995. end else
  2996. begin
  2997. // a convex vertex ...
  2998. pt := FPathIn[j];
  2999. pt2.X := FNorms[k].X * FDelta;
  3000. pt2.Y := FNorms[k].Y * FDelta;
  3001. AddPoint(FloatPoint(pt.X + pt2.X, pt.Y + pt2.Y));
  3002. for i := 1 to steps -1 do
  3003. begin
  3004. pt2 := FloatPoint(pt2.X * FStepSizeCos - FStepSizeSin * pt2.Y,
  3005. pt2.X * FStepSizeSin + pt2.Y * FStepSizeCos);
  3006. AddPoint(FloatPoint(pt.X + pt2.X, pt.Y + pt2.Y));
  3007. end;
  3008. end;
  3009. end;
  3010. //------------------------------------------------------------------------------
  3011. procedure TClipperOffset.OffsetPoint(j,k: Integer);
  3012. var
  3013. sinA, cosA: Double;
  3014. begin
  3015. // A: angle between adjoining edges (on left side WRT winding direction).
  3016. // A == 0 deg (or A == 360 deg): collinear edges heading in same direction
  3017. // A == 180 deg: collinear edges heading in opposite directions (ie a 'spike')
  3018. // sin(A) < 0: convex on left.
  3019. // cos(A) > 0: angles on both left and right sides > 90 degrees
  3020. sinA := (FNorms[k].X * FNorms[j].Y - FNorms[j].X * FNorms[k].Y);
  3021. cosA := (FNorms[j].X * FNorms[k].X + FNorms[j].Y * FNorms[k].Y);
  3022. if (Abs(sinA * FDelta) < 1.0) then // angle is close to 0 or 180 deg.
  3023. begin
  3024. if (cosA > 0) then // given condition above the angle is approaching 0 deg.
  3025. begin
  3026. if FJoinType = jtRoundEx then
  3027. DoRound(j, k)
  3028. else
  3029. // with angles approaching 0 deg collinear (whether concave or convex),
  3030. // offsetting with two or more vertices (that would be so close together)
  3031. // occasionally causes tiny self-intersections due to rounding.
  3032. // So we offset with just a single vertex here ...
  3033. AddPoint(FloatPoint(FPathIn[j].X + FNorms[k].X * FDelta,
  3034. FPathIn[j].Y + FNorms[k].Y * FDelta));
  3035. Exit;
  3036. end;
  3037. // else angle must be approaching 180 deg.
  3038. end
  3039. else if (sinA > 1.0) then sinA := 1.0
  3040. else if (sinA < -1.0) then sinA := -1.0;
  3041. if (FJoinType = jtRoundEx) then
  3042. begin
  3043. DoRound(j, k)
  3044. end
  3045. else if sinA * FDelta < 0 then // ie a concave offset
  3046. begin
  3047. AddPoint(FloatPoint(FPathIn[j].X + FNorms[k].X * FDelta,
  3048. FPathIn[j].Y + FNorms[k].Y * FDelta));
  3049. AddPoint(FPathIn[j]); // this improves clipping removal later
  3050. AddPoint(FloatPoint(FPathIn[j].X + FNorms[j].X * FDelta,
  3051. FPathIn[j].Y + FNorms[j].Y * FDelta));
  3052. end
  3053. else
  3054. begin
  3055. // convex offsets here ...
  3056. case FJoinType of
  3057. jtMiter:
  3058. // see offset_triginometry3.svg
  3059. if (1 + cosA < FMiterLim) then DoSquare(j, k)
  3060. else DoMiter(j, k, 1 + cosA);
  3061. jtSquare:
  3062. // angles >= 90 deg. don't need squaring
  3063. if cosA >= 0 then
  3064. DoMiter(j, k, 1 + cosA) else
  3065. DoSquare(j, k);
  3066. else DoRound(j, k);
  3067. end;
  3068. end;
  3069. end;
  3070. //------------------------------------------------------------------------------
  3071. //------------------------------------------------------------------------------
  3072. function InflatePaths(const paths: TArrayOfArrayOfFloatPoint;
  3073. delta: Double; jt: TJoinType; et: TEndType;
  3074. miterLimit: single): TArrayOfArrayOfFloatPoint;
  3075. begin
  3076. with TClipperOffset.Create(miterLimit) do
  3077. try
  3078. AddPaths(paths);
  3079. Execute(delta, jt, et, Result);
  3080. finally
  3081. free;
  3082. end;
  3083. end;
  3084. //------------------------------------------------------------------------------
  3085. end.