GR32_VectorUtils.pas 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077
  1. unit GR32_VectorUtils;
  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 Vectorial Polygon Rasterizer for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Mattias Andersson <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2008-2012
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$I GR32.inc}
  35. {$BOOLEVAL OFF}
  36. uses
  37. Math, {$IFDEF FPC}Types, {$ENDIF} {$IFDEF COMPILERXE2_UP}Types, {$ENDIF}
  38. GR32,
  39. GR32_Transforms,
  40. GR32_Polygons;
  41. const
  42. DEFAULT_MITER_LIMIT = 4.0;
  43. DEFAULT_MITER_LIMIT_FIXED = $40000;
  44. TWOPI = 2 * Pi;
  45. function InSignedRange(const X, X1, X2: TFloat): Boolean; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  46. function InSignedRange(const X, X1, X2: TFixed): Boolean; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  47. function Intersect(const A1, A2, B1, B2: TFloatPoint; out P: TFloatPoint): Boolean; overload;
  48. function Intersect(const A1, A2, B1, B2: TFixedPoint; out P: TFixedPoint): Boolean; overload;
  49. function VertexReduction(Points: TArrayOfFloatPoint; Epsilon: TFloat = 1): TArrayOfFloatPoint; overload;
  50. function VertexReduction(Points: TArrayOfFixedPoint; Epsilon: TFixed = FixedOne): TArrayOfFixedPoint; overload;
  51. function ClosePolygon(const Points: TArrayOfFloatPoint): TArrayOfFloatPoint; overload;
  52. function ClosePolygon(const Points: TArrayOfFixedPoint): TArrayOfFixedPoint; overload;
  53. function ClipLine(var X1, Y1, X2, Y2: Integer; MinX, MinY, MaxX, MaxY: Integer): Boolean; overload;
  54. function ClipLine(var X1, Y1, X2, Y2: TFloat; MinX, MinY, MaxX, MaxY: TFloat): Boolean; overload;
  55. function ClipLine(var X1, Y1, X2, Y2: TFixed; MinX, MinY, MaxX, MaxY: TFixed): Boolean; overload;
  56. function ClipLine(var P1, P2: TPoint; const ClipRect: TRect): Boolean; overload;
  57. function ClipLine(var P1, P2: TFloatPoint; const ClipRect: TFloatRect): Boolean; overload;
  58. function ClipLine(var P1, P2: TFixedPoint; const ClipRect: TFixedRect): Boolean; overload;
  59. type
  60. TTriangleVertexIndices = array [0 .. 2] of Integer;
  61. TArrayOfTriangleVertexIndices = array of TTriangleVertexIndices;
  62. function DelaunayTriangulation(Points: TArrayOfFloatPoint): TArrayOfTriangleVertexIndices;
  63. function BuildNormals(const Points: TArrayOfFloatPoint): TArrayOfFloatPoint; overload;
  64. function BuildNormals(const Points: TArrayOfFixedPoint): TArrayOfFixedPoint; overload;
  65. function Grow(const Points: TArrayOfFloatPoint; const Normals: TArrayOfFloatPoint;
  66. const Delta: TFloat; JoinStyle: TJoinStyle = jsMiter;
  67. Closed: Boolean = True; MiterLimit: TFloat = DEFAULT_MITER_LIMIT): TArrayOfFloatPoint; overload;
  68. function Grow(const Points: TArrayOfFloatPoint;
  69. const Delta: TFloat; JoinStyle: TJoinStyle = jsMiter;
  70. Closed: Boolean = True; MiterLimit: TFloat = DEFAULT_MITER_LIMIT): TArrayOfFloatPoint; overload;
  71. function Grow(const Points: TArrayOfFixedPoint; const Normals: TArrayOfFixedPoint;
  72. const Delta: TFixed; JoinStyle: TJoinStyle = jsMiter;
  73. Closed: Boolean = True; MiterLimit: TFixed = DEFAULT_MITER_LIMIT_FIXED): TArrayOfFixedPoint; overload;
  74. function Grow(const Points: TArrayOfFixedPoint;
  75. const Delta: TFixed; JoinStyle: TJoinStyle = jsMiter;
  76. Closed: Boolean = True; MiterLimit: TFixed = DEFAULT_MITER_LIMIT_FIXED): TArrayOfFixedPoint; overload;
  77. function ReversePolygon(const Points: TArrayOfFloatPoint): TArrayOfFloatPoint; overload;
  78. function ReversePolygon(const Points: TArrayOfFixedPoint): TArrayOfFixedPoint; overload;
  79. function BuildPolyline(const Points: TArrayOfFloatPoint; StrokeWidth: TFloat;
  80. JoinStyle: TJoinStyle = jsMiter; EndStyle: TEndStyle = esButt;
  81. MiterLimit: TFloat = DEFAULT_MITER_LIMIT): TArrayOfFloatPoint; overload;
  82. function BuildPolyPolyLine(const Points: TArrayOfArrayOfFloatPoint;
  83. Closed: Boolean; StrokeWidth: TFloat; JoinStyle: TJoinStyle = jsMiter;
  84. EndStyle: TEndStyle = esButt; MiterLimit: TFloat = DEFAULT_MITER_LIMIT): TArrayOfArrayOfFloatPoint; overload;
  85. function BuildPolyline(const Points: TArrayOfFixedPoint; StrokeWidth: TFixed;
  86. JoinStyle: TJoinStyle = jsMiter; EndStyle: TEndStyle = esButt;
  87. MiterLimit: TFixed = DEFAULT_MITER_LIMIT_FIXED): TArrayOfFixedPoint; overload;
  88. function BuildPolyPolyLine(const Points: TArrayOfArrayOfFixedPoint;
  89. Closed: Boolean; StrokeWidth: TFixed; JoinStyle: TJoinStyle = jsMiter;
  90. EndStyle: TEndStyle = esButt; MiterLimit: TFixed = DEFAULT_MITER_LIMIT_FIXED): TArrayOfArrayOfFixedPoint; overload;
  91. function BuildDashedLine(const Points: TArrayOfFloatPoint;
  92. const DashArray: TArrayOfFloat; DashOffset: TFloat = 0;
  93. Closed: Boolean = False): TArrayOfArrayOfFloatPoint; overload;
  94. function BuildDashedLine(const Points: TArrayOfFixedPoint;
  95. const DashArray: TArrayOfFixed; DashOffset: TFixed = 0;
  96. Closed: Boolean = False): TArrayOfArrayOfFixedPoint; overload;
  97. function ClipPolygon(const Points: TArrayOfFloatPoint; const ClipRect: TFloatRect): TArrayOfFloatPoint; overload;
  98. function ClipPolygon(const Points: TArrayOfFixedPoint; const ClipRect: TFixedRect): TArrayOfFixedPoint; overload;
  99. function CatPolyPolygon(const P1, P2: TArrayOfArrayOfFloatPoint): TArrayOfArrayOfFloatPoint; overload;
  100. function CatPolyPolygon(const P1, P2: TArrayOfArrayOfFixedPoint): TArrayOfArrayOfFixedPoint; overload;
  101. function CalculateCircleSteps(Radius: TFloat): Cardinal; {$IFDEF USEINLINING} inline; {$ENDIF}
  102. function BuildArc(const P: TFloatPoint; StartAngle, EndAngle, Radius: TFloat; Steps: Integer): TArrayOfFloatPoint; overload;
  103. function BuildArc(const P: TFloatPoint; StartAngle, EndAngle, Radius: TFloat): TArrayOfFloatPoint; overload;
  104. function BuildArc(const P: TFixedPoint; StartAngle, EndAngle, Radius: TFloat; Steps: Integer): TArrayOfFixedPoint; overload;
  105. function BuildArc(const P: TFixedPoint; StartAngle, EndAngle, Radius: TFloat): TArrayOfFixedPoint; overload;
  106. function Line(const P1, P2: TFloatPoint): TArrayOfFloatPoint; overload;
  107. function Line(const X1, Y1, X2, Y2: TFloat): TArrayOfFloatPoint; overload;
  108. function VertLine(const X, Y1, Y2: TFloat): TArrayOfFloatPoint;
  109. function HorzLine(const X1, Y, X2: TFloat): TArrayOfFloatPoint;
  110. function Circle(const P: TFloatPoint; const Radius: TFloat; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  111. function Circle(const P: TFloatPoint; const Radius: TFloat): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  112. function Circle(const X, Y, Radius: TFloat; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  113. function Circle(const X, Y, Radius: TFloat): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  114. function Circle(const R: TRect): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  115. function Circle(const R: TRect; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  116. function Circle(const R: TFloatRect): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  117. function Circle(const R: TFloatRect; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  118. function Pie(const P: TFloatPoint; const Radius: TFloat; const Angle, Offset: TFloat; Steps: Integer): TArrayOfFloatPoint; overload;
  119. function Pie(const P: TFloatPoint; const Radius: TFloat; const Angle: TFloat; const Offset: TFloat = 0): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  120. function Pie(const P: TFloatPoint; const Radius: TFloat; const Angle: TFloat; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  121. function Pie(const X, Y, Radius: TFloat; const Angle, Offset: TFloat; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  122. function Pie(const X, Y, Radius: TFloat; const Angle: TFloat; const Offset: TFloat = 0): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  123. function Pie(const X, Y, Radius: TFloat; const Angle: TFloat; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  124. function Ellipse(const P, R: TFloatPoint; Steps: Integer): TArrayOfFloatPoint; overload;
  125. function Ellipse(const P, R: TFloatPoint): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  126. function Ellipse(const X, Y, Rx, Ry: TFloat; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  127. function Ellipse(const X, Y, Rx, Ry: TFloat): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  128. function Ellipse(const R: TRect): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  129. function Ellipse(const R: TFloatRect): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  130. function Ellipse(const R: TRect; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  131. function Ellipse(const R: TFloatRect; Steps: Integer): TArrayOfFloatPoint; overload; {$IFDEF INLINING_ENHANCED_RECORDS} inline; {$ENDIF}
  132. function Star(const P: TFloatPoint; const InnerRadius, OuterRadius: TFloat;
  133. Vertices: Integer = 5; Rotation: TFloat = 0): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  134. function Star(const X, Y, InnerRadius, OuterRadius: TFloat;
  135. Vertices: Integer = 5; Rotation: TFloat = 0): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  136. function Star(const P: TFloatPoint; const Radius: TFloat; Vertices: Integer = 5;
  137. Rotation: TFloat = 0): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  138. function Star(const X, Y, Radius: TFloat; Vertices: Integer = 5;
  139. Rotation: TFloat = 0): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  140. function Rectangle(const R: TFloatRect): TArrayOfFloatPoint; {$IFDEF USEINLINING} inline; {$ENDIF}
  141. function RoundRect(const R: TFloatRect; const Radius: TFloat): TArrayOfFloatPoint; {$IFDEF USEINLINING} inline; {$ENDIF}
  142. function PolygonBounds(const Points: TArrayOfFloatPoint): TFloatRect; overload;
  143. function PolygonBounds(const Points: TArrayOfFixedPoint): TFixedRect; overload;
  144. function PolypolygonBounds(const Points: TArrayOfArrayOfFloatPoint): TFloatRect; overload;
  145. function PolypolygonBounds(const Points: TArrayOfArrayOfFixedPoint): TFixedRect; overload;
  146. function ScalePolygon(const Points: TArrayOfFloatPoint; ScaleX, ScaleY: TFloat): TArrayOfFloatPoint; overload;
  147. function ScalePolygon(const Points: TArrayOfFixedPoint; ScaleX, ScaleY: TFixed): TArrayOfFixedPoint; overload;
  148. function ScalePolyPolygon(const Points: TArrayOfArrayOfFloatPoint; ScaleX, ScaleY: TFloat): TArrayOfArrayOfFloatPoint; overload;
  149. function ScalePolyPolygon(const Points: TArrayOfArrayOfFixedPoint; ScaleX, ScaleY: TFixed): TArrayOfArrayOfFixedPoint; overload;
  150. procedure ScalePolygonInplace(const Points: TArrayOfFloatPoint; ScaleX, ScaleY: TFloat); overload;
  151. procedure ScalePolygonInplace(const Points: TArrayOfFixedPoint; ScaleX, ScaleY: TFixed); overload;
  152. procedure ScalePolyPolygonInplace(const Points: TArrayOfArrayOfFloatPoint; ScaleX, ScaleY: TFloat); overload;
  153. procedure ScalePolyPolygonInplace(const Points: TArrayOfArrayOfFixedPoint; ScaleX, ScaleY: TFixed); overload;
  154. function TranslatePolygon(const Points: TArrayOfFloatPoint; OffsetX, OffsetY: TFloat): TArrayOfFloatPoint; overload;
  155. function TranslatePolygon(const Points: TArrayOfFixedPoint; Offsetx, OffsetY: TFixed): TArrayOfFixedPoint; overload;
  156. function TranslatePolyPolygon(const Points: TArrayOfArrayOfFloatPoint; OffsetX, OffsetY: TFloat): TArrayOfArrayOfFloatPoint; overload;
  157. function TranslatePolyPolygon(const Points: TArrayOfArrayOfFixedPoint; OffsetX, OffsetY: TFixed): TArrayOfArrayOfFixedPoint; overload;
  158. procedure TranslatePolygonInplace(const Points: TArrayOfFloatPoint; OffsetX, OffsetY: TFloat); overload;
  159. procedure TranslatePolygonInplace(const Points: TArrayOfFixedPoint; Offsetx, OffsetY: TFixed); overload;
  160. procedure TranslatePolyPolygonInplace(const Points: TArrayOfArrayOfFloatPoint; OffsetX, OffsetY: TFloat); overload;
  161. procedure TranslatePolyPolygonInplace(const Points: TArrayOfArrayOfFixedPoint; OffsetX, OffsetY: TFixed); overload;
  162. function TransformPolygon(const Points: TArrayOfFloatPoint; Transformation: TTransformation): TArrayOfFloatPoint; overload;
  163. function TransformPolygon(const Points: TArrayOfFixedPoint; Transformation: TTransformation): TArrayOfFixedPoint; overload;
  164. function TransformPolyPolygon(const Points: TArrayOfArrayOfFloatPoint; Transformation: TTransformation): TArrayOfArrayOfFloatPoint; overload;
  165. function TransformPolyPolygon(const Points: TArrayOfArrayOfFixedPoint; Transformation: TTransformation): TArrayOfArrayOfFixedPoint; overload;
  166. function BuildPolygonF(const Data: array of TFloat): TArrayOfFloatPoint; overload;
  167. function BuildPolygonX(const Data: array of TFixed): TArrayOfFixedPoint; overload;
  168. function PolyPolygon(const Points: TArrayOfFloatPoint): TArrayOfArrayOfFloatPoint; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  169. function PolyPolygon(const Points: TArrayOfFixedPoint): TArrayOfArrayOfFixedPoint; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  170. function PointToFloatPoint(const Points: TArrayOfPoint): TArrayOfFloatPoint; overload;
  171. function PointToFloatPoint(const Points: TArrayOfArrayOfPoint): TArrayOfArrayOfFloatPoint; overload;
  172. function PointToFixedPoint(const Points: TArrayOfPoint): TArrayOfFixedPoint; overload;
  173. function PointToFixedPoint(const Points: TArrayOfArrayOfPoint): TArrayOfArrayOfFixedPoint; overload;
  174. function FixedPointToFloatPoint(const Points: TArrayOfFixedPoint): TArrayOfFloatPoint; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  175. function FixedPointToFloatPoint(const Points: TArrayOfArrayOfFixedPoint): TArrayOfArrayOfFloatPoint; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  176. function FloatPointToFixedPoint(const Points: TArrayOfFloatPoint): TArrayOfFixedPoint; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  177. function FloatPointToFixedPoint(const Points: TArrayOfArrayOfFloatPoint): TArrayOfArrayOfFixedPoint; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  178. implementation
  179. uses
  180. SysUtils,
  181. GR32_Math,
  182. GR32_Geometry,
  183. GR32_LowLevel;
  184. type
  185. TTransformationAccess = class(TTransformation);
  186. // Returns True if Min(X1, X2) <= X < Max(X1, X2)
  187. function InSignedRange(const X, X1, X2: TFloat): Boolean;
  188. begin
  189. Result := (X < X1) xor (X < X2);
  190. end;
  191. // Returns True if Min(X1, X2) <= X < Max(X1, X2)
  192. function InSignedRange(const X, X1, X2: TFixed): Boolean;
  193. begin
  194. Result := (X < X1) xor (X < X2);
  195. end;
  196. // Returns True if the line segments (A1, A2) and (B1, B2) intersects
  197. // P is the point of intersection
  198. function Intersect(const A1, A2, B1, B2: TFloatPoint; out P: TFloatPoint): Boolean;
  199. var
  200. Adx, Ady, Bdx, Bdy, ABy, ABx: TFloat;
  201. t, ta, tb: TFloat;
  202. begin
  203. Result := False;
  204. Adx := A2.X - A1.X;
  205. Ady := A2.Y - A1.Y;
  206. Bdx := B2.X - B1.X;
  207. Bdy := B2.Y - B1.Y;
  208. t := (Bdy * Adx) - (Bdx * Ady);
  209. if t = 0 then Exit; // lines are parallell
  210. ABx := A1.X - B1.X;
  211. ABy := A1.Y - B1.Y;
  212. ta := Bdx * ABy - Bdy * ABx;
  213. tb := Adx * ABy - Ady * ABx;
  214. if InSignedRange(ta, 0, t) and InSignedRange(tb, 0, t) then
  215. begin
  216. Result := True;
  217. ta := ta / t;
  218. P.X := A1.X + ta * Adx;
  219. P.Y := A1.Y + ta * Ady;
  220. end;
  221. end;
  222. function Intersect(const A1, A2, B1, B2: TFixedPoint; out P: TFixedPoint): Boolean; overload;
  223. var
  224. Adx, Ady, Bdx, Bdy, ABy, ABx: TFixed;
  225. t, ta, tb: TFixed;
  226. begin
  227. Result := False;
  228. Adx := A2.X - A1.X;
  229. Ady := A2.Y - A1.Y;
  230. Bdx := B2.X - B1.X;
  231. Bdy := B2.Y - B1.Y;
  232. t := (Bdy * Adx) - (Bdx * Ady);
  233. if t = 0 then Exit; // lines are parallell
  234. ABx := A1.X - B1.X;
  235. ABy := A1.Y - B1.Y;
  236. ta := Bdx * ABy - Bdy * ABx;
  237. tb := Adx * ABy - Ady * ABx;
  238. if InSignedRange(ta, 0, t) and InSignedRange(tb, 0, t) then
  239. begin
  240. Result := True;
  241. ta := FixedDiv(ta, t);
  242. P.X := A1.X + ta * Adx;
  243. P.Y := A1.Y + ta * Ady;
  244. end;
  245. end;
  246. function RamerDouglasPeucker(Points: TArrayOfFloatPoint; FirstIndex,
  247. LastIndex: Integer; Epsilon: TFloat = 1): TArrayOfFloatPoint; overload;
  248. var
  249. Index, DeltaMaxIndex: Integer;
  250. Delta, DeltaMax: TFloat;
  251. Parts: array [0 .. 1] of TArrayOfFloatPoint;
  252. begin
  253. if LastIndex - FirstIndex > 1 then
  254. begin
  255. // find the point with the maximum distance
  256. DeltaMax := 0;
  257. DeltaMaxIndex := 0;
  258. for Index := FirstIndex + 1 to LastIndex - 1 do
  259. begin
  260. with Points[LastIndex] do
  261. Delta := Abs((Points[Index].x - x) * (Points[FirstIndex].y - y) -
  262. (Points[Index].y - y) * (Points[FirstIndex].x - x));
  263. if Delta > DeltaMax then
  264. begin
  265. DeltaMaxIndex := Index;
  266. DeltaMax := Delta;
  267. end;
  268. end;
  269. // if max distance is greater than epsilon, recursively simplify
  270. if DeltaMax >= Epsilon * GR32_Math.Hypot(Points[FirstIndex].x - Points[LastIndex].x,
  271. Points[FirstIndex].y - Points[LastIndex].y) then
  272. begin
  273. // Recursive call
  274. Parts[0] := RamerDouglasPeucker(Points, FirstIndex, DeltaMaxIndex, Epsilon);
  275. Parts[1] := RamerDouglasPeucker(Points, DeltaMaxIndex, LastIndex, Epsilon);
  276. // Build the result list
  277. SetLength(Result, Length(Parts[0]) + Length(Parts[1]) - 1);
  278. Move(Parts[0, 0], Result[0], (Length(Parts[0]) - 1) * SizeOf(TFloatPoint));
  279. Move(Parts[1, 0], Result[Length(Parts[0]) - 1], Length(Parts[1]) *
  280. SizeOf(TFloatPoint));
  281. Exit;
  282. end;
  283. end;
  284. SetLength(Result, 2);
  285. Result[0] := Points[FirstIndex];
  286. Result[1] := Points[LastIndex];
  287. end;
  288. function RamerDouglasPeucker(Points: TArrayOfFixedPoint; FirstIndex,
  289. LastIndex: Integer; Epsilon: TFixed = 1): TArrayOfFixedPoint; overload;
  290. var
  291. Index, DeltaMaxIndex: Integer;
  292. Delta, DeltaMax: TFixed;
  293. Parts: array [0 .. 1] of TArrayOfFixedPoint;
  294. //Finds the perpendicular distance from a point to a straight line.
  295. //The coordinates of the point are specified as $ptX and $ptY.
  296. //The line passes through points l1 and l2, specified respectively with their
  297. //coordinates $l1x and $l1y, and $l2x and $l2y
  298. function PerpendicularDistance(ptX, ptY, l1x, l1y, l2x, l2y: TFixed): TFixed;
  299. var
  300. Slope, PassThroughY: TFixed;
  301. begin
  302. if (l2x = l1x) then
  303. begin
  304. //vertical lines - treat this case specially to avoid divide by zero
  305. Result := Abs(ptX - l2x);
  306. end
  307. else
  308. begin
  309. Slope := FixedDiv(l2y-l1y, l2x-l1x);
  310. PassThroughY := FixedMul(0 - l1x, Slope) + l1y;
  311. Result := FixedDiv(Abs(FixedMul(Slope, ptX) - ptY + PassThroughY),
  312. FixedSqrtHP(FixedSqr(Slope) + 1));
  313. end;
  314. end;
  315. begin
  316. if LastIndex - FirstIndex > 1 then
  317. begin
  318. // find the point with the maximum distance
  319. DeltaMax := 0;
  320. DeltaMaxIndex := 0;
  321. for Index := FirstIndex + 1 to LastIndex - 1 do
  322. begin
  323. Delta := PerpendicularDistance(
  324. Points[Index].x, Points[Index].y,
  325. Points[FirstIndex].x, Points[FirstIndex].y,
  326. Points[LastIndex].x, Points[LastIndex].y);
  327. if Delta > DeltaMax then
  328. begin
  329. DeltaMaxIndex := Index;
  330. DeltaMax := Delta;
  331. end;
  332. end;
  333. // if max distance is greater than epsilon, recursively simplify
  334. if DeltaMax > Epsilon then
  335. begin
  336. // Recursive call
  337. Parts[0] := RamerDouglasPeucker(Points, FirstIndex, DeltaMaxIndex, Epsilon);
  338. Parts[1] := RamerDouglasPeucker(Points, DeltaMaxIndex, LastIndex, Epsilon);
  339. // Build the result list
  340. SetLength(Result, Length(Parts[0]) + Length(Parts[1]) - 1);
  341. Move(Parts[0, 0], Result[0], (Length(Parts[0]) - 1) * SizeOf(TFixedPoint));
  342. Move(Parts[1, 0], Result[Length(Parts[0]) - 1], Length(Parts[1]) * SizeOf(TFixedPoint));
  343. Exit;
  344. end;
  345. end;
  346. SetLength(Result, 2);
  347. Result[0] := Points[FirstIndex];
  348. Result[1] := Points[LastIndex];
  349. end;
  350. function VertexReduction(Points: TArrayOfFloatPoint; Epsilon: TFloat = 1): TArrayOfFloatPoint;
  351. var
  352. Index: Integer;
  353. SqrEpsilon: TFloat;
  354. begin
  355. SqrEpsilon := Sqr(Epsilon);
  356. SetLength(Result, 1);
  357. Result[0] := Points[0];
  358. Index := 1;
  359. while Index < Length(Points) do
  360. begin
  361. if SqrDistance(Result[Length(Result) - 1], Points[Index]) > SqrEpsilon then
  362. begin
  363. SetLength(Result, Length(Result) + 1);
  364. Result[Length(Result) - 1] := Points[Index];
  365. end;
  366. Inc(Index);
  367. end;
  368. if Length(Result) > 2 then
  369. Result := RamerDouglasPeucker(Result, 0, Length(Result) - 1, Epsilon);
  370. end;
  371. function VertexReduction(Points: TArrayOfFixedPoint; Epsilon: TFixed): TArrayOfFixedPoint;
  372. var
  373. Index: Integer;
  374. SqrEpsilon: TFixed;
  375. begin
  376. SqrEpsilon := FixedSqr(Epsilon);
  377. SetLength(Result, 1);
  378. Result[0] := Points[0];
  379. Index := 1;
  380. while Index < Length(Points) do
  381. begin
  382. if SqrDistance(Result[Length(Result) - 1], Points[Index]) > SqrEpsilon then
  383. begin
  384. SetLength(Result, Length(Result) + 1);
  385. Result[Length(Result) - 1] := Points[Index];
  386. end;
  387. Inc(Index);
  388. end;
  389. if Length(Result) > 2 then
  390. Result := RamerDouglasPeucker(Points, 0, Length(Points) - 1, Epsilon);
  391. end;
  392. function ClosePolygon(const Points: TArrayOfFloatPoint): TArrayOfFloatPoint;
  393. var
  394. L: Integer;
  395. P1, P2: TFloatPoint;
  396. begin
  397. L := Length(Points);
  398. Result := Points;
  399. if L <= 1 then
  400. Exit;
  401. P1 := Result[0];
  402. P2 := Result[L - 1];
  403. if (P1.X = P2.X) and (P1.Y = P2.Y) then
  404. Exit;
  405. SetLength(Result, L + 1);
  406. Move(Result[0], Points[0], L * SizeOf(TFloatPoint));
  407. Result[L] := P1;
  408. end;
  409. function ClosePolygon(const Points: TArrayOfFixedPoint): TArrayOfFixedPoint;
  410. var
  411. L: Integer;
  412. P1, P2: TFixedPoint;
  413. begin
  414. L := Length(Points);
  415. Result := Points;
  416. if L <= 1 then
  417. Exit;
  418. P1 := Result[0];
  419. P2 := Result[L - 1];
  420. if (P1.X = P2.X) and (P1.Y = P2.Y) then
  421. Exit;
  422. SetLength(Result, L + 1);
  423. Move(Result[0], Points[0], L * SizeOf(TFixedPoint));
  424. Result[L] := P1;
  425. end;
  426. function ClipLine(var X1, Y1, X2, Y2: Integer; MinX, MinY, MaxX, MaxY: Integer): Boolean;
  427. var
  428. C1, C2: Integer;
  429. V: Integer;
  430. begin
  431. { Get edge codes }
  432. C1 := Ord(X1 < MinX) + Ord(X1 > MaxX) shl 1 + Ord(Y1 < MinY) shl 2 + Ord(Y1 > MaxY) shl 3;
  433. C2 := Ord(X2 < MinX) + Ord(X2 > MaxX) shl 1 + Ord(Y2 < MinY) shl 2 + Ord(Y2 > MaxY) shl 3;
  434. if ((C1 and C2) = 0) and ((C1 or C2) <> 0) then
  435. begin
  436. if (C1 and 12) <> 0 then
  437. begin
  438. if C1 < 8 then V := MinY else V := MaxY;
  439. Inc(X1, MulDiv(V - Y1, X2 - X1, Y2 - Y1));
  440. Y1 := V;
  441. C1 := Ord(X1 < MinX) + Ord(X1 > MaxX) shl 1;
  442. end;
  443. if (C2 and 12) <> 0 then
  444. begin
  445. if C2 < 8 then V := MinY else V := MaxY;
  446. Inc(X2, MulDiv(V - Y2, X2 - X1, Y2 - Y1));
  447. Y2 := V;
  448. C2 := Ord(X2 < MinX) + Ord(X2 > MaxX) shl 1;
  449. end;
  450. if ((C1 and C2) = 0) and ((C1 or C2) <> 0) then
  451. begin
  452. if C1 <> 0 then
  453. begin
  454. if C1 = 1 then V := MinX else V := MaxX;
  455. Inc(Y1, MulDiv(V - X1, Y2 - Y1, X2 - X1));
  456. X1 := V;
  457. C1 := 0;
  458. end;
  459. if C2 <> 0 then
  460. begin
  461. if C2 = 1 then V := MinX else V := MaxX;
  462. Inc(Y2, MulDiv(V - X2, Y2 - Y1, X2 - X1));
  463. X2 := V;
  464. C2 := 0;
  465. end;
  466. end;
  467. end;
  468. Result := (C1 or C2) = 0;
  469. end;
  470. function ClipLine(var X1, Y1, X2, Y2: TFloat; MinX, MinY, MaxX, MaxY: TFloat): Boolean;
  471. var
  472. C1, C2: Integer;
  473. V: TFloat;
  474. begin
  475. { Get edge codes }
  476. C1 := Ord(X1 < MinX) + Ord(X1 > MaxX) shl 1 + Ord(Y1 < MinY) shl 2 + Ord(Y1 > MaxY) shl 3;
  477. C2 := Ord(X2 < MinX) + Ord(X2 > MaxX) shl 1 + Ord(Y2 < MinY) shl 2 + Ord(Y2 > MaxY) shl 3;
  478. if ((C1 and C2) = 0) and ((C1 or C2) <> 0) then
  479. begin
  480. if (C1 and 12) <> 0 then
  481. begin
  482. if C1 < 8 then V := MinY else V := MaxY;
  483. X1 := X1 + (V - Y1) * (X2 - X1) / (Y2 - Y1);
  484. Y1 := V;
  485. C1 := Ord(X1 < MinX) + Ord(X1 > MaxX) shl 1;
  486. end;
  487. if (C2 and 12) <> 0 then
  488. begin
  489. if C2 < 8 then V := MinY else V := MaxY;
  490. X2 := X2 + (V - Y2) * (X2 - X1) / (Y2 - Y1);
  491. Y2 := V;
  492. C2 := Ord(X2 < MinX) + Ord(X2 > MaxX) shl 1;
  493. end;
  494. if ((C1 and C2) = 0) and ((C1 or C2) <> 0) then
  495. begin
  496. if C1 <> 0 then
  497. begin
  498. if C1 = 1 then V := MinX else V := MaxX;
  499. Y1 := Y1 + (V - X1) * (Y2 - Y1) / (X2 - X1);
  500. X1 := V;
  501. C1 := 0;
  502. end;
  503. if C2 <> 0 then
  504. begin
  505. if C2 = 1 then V := MinX else V := MaxX;
  506. Y2 := Y2 + (V - X2) * (Y2 - Y1) / (X2 - X1);
  507. X2 := V;
  508. C2 := 0;
  509. end;
  510. end;
  511. end;
  512. Result := (C1 or C2) = 0;
  513. end;
  514. function ClipLine(var X1, Y1, X2, Y2: TFixed; MinX, MinY, MaxX, MaxY: TFixed): Boolean;
  515. var
  516. C1, C2: Integer;
  517. V: TFixed;
  518. begin
  519. { Get edge codes }
  520. C1 := Ord(X1 < MinX) + Ord(X1 > MaxX) shl 1 + Ord(Y1 < MinY) shl 2 + Ord(Y1 > MaxY) shl 3;
  521. C2 := Ord(X2 < MinX) + Ord(X2 > MaxX) shl 1 + Ord(Y2 < MinY) shl 2 + Ord(Y2 > MaxY) shl 3;
  522. if ((C1 and C2) = 0) and ((C1 or C2) <> 0) then
  523. begin
  524. if (C1 and 12) <> 0 then
  525. begin
  526. if C1 < 8 then V := MinY else V := MaxY;
  527. X1 := X1 + FixedDiv(FixedMul(V - Y1, X2 - X1), Y2 - Y1);
  528. Y1 := V;
  529. C1 := Ord(X1 < MinX) + Ord(X1 > MaxX) shl 1;
  530. end;
  531. if (C2 and 12) <> 0 then
  532. begin
  533. if C2 < 8 then V := MinY else V := MaxY;
  534. X2 := X2 + FixedDiv(FixedMul(V - Y2, X2 - X1), Y2 - Y1);
  535. Y2 := V;
  536. C2 := Ord(X2 < MinX) + Ord(X2 > MaxX) shl 1;
  537. end;
  538. if ((C1 and C2) = 0) and ((C1 or C2) <> 0) then
  539. begin
  540. if C1 <> 0 then
  541. begin
  542. if C1 = 1 then V := MinX else V := MaxX;
  543. Y1 := Y1 + FixedDiv(FixedMul(V - X1, Y2 - Y1), X2 - X1);
  544. X1 := V;
  545. C1 := 0;
  546. end;
  547. if C2 <> 0 then
  548. begin
  549. if C2 = 1 then V := MinX else V := MaxX;
  550. Y2 := Y2 + FixedDiv(FixedMul(V - X2, Y2 - Y1), X2 - X1);
  551. X2 := V;
  552. C2 := 0;
  553. end;
  554. end;
  555. end;
  556. Result := (C1 or C2) = 0;
  557. end;
  558. function ClipLine(var P1, P2: TPoint; const ClipRect: TRect): Boolean;
  559. begin
  560. Result := ClipLine(P1.X, P1.Y, P2.X, P2.Y, ClipRect.Left, ClipRect.Top,
  561. ClipRect.Right, ClipRect.Bottom);
  562. end;
  563. function ClipLine(var P1, P2: TFloatPoint; const ClipRect: TFloatRect): Boolean;
  564. begin
  565. Result := ClipLine(P1.X, P1.Y, P2.X, P2.Y, ClipRect.Left, ClipRect.Top,
  566. ClipRect.Right, ClipRect.Bottom);
  567. end;
  568. function ClipLine(var P1, P2: TFixedPoint; const ClipRect: TFixedRect): Boolean;
  569. begin
  570. Result := ClipLine(P1.X, P1.Y, P2.X, P2.Y, ClipRect.Left, ClipRect.Top,
  571. ClipRect.Right, ClipRect.Bottom);
  572. end;
  573. procedure FastMergeSortX(const Values: TArrayOfFloatPoint;
  574. out Indexes: TArrayOfInteger; out Bounds: TFloatRect);
  575. var
  576. Temp: TArrayOfInteger;
  577. procedure Merge(I1, I2, J1, J2: Integer);
  578. var
  579. I, J, K: Integer;
  580. begin
  581. if Values[Indexes[I2]].X < Values[Indexes[J1]].X then Exit;
  582. I := I1;
  583. J := J1;
  584. K := 0;
  585. repeat
  586. if Values[Indexes[I]].X < Values[Indexes[J]].X then
  587. begin
  588. Temp[K] := Indexes[I];
  589. Inc(I);
  590. end
  591. else
  592. begin
  593. Temp[K] := Indexes[J];
  594. Inc(J);
  595. end;
  596. Inc(K);
  597. until (I > I2) or (J > J2);
  598. while I <= I2 do
  599. begin
  600. Temp[K] := Indexes[I];
  601. Inc(I); Inc(K);
  602. end;
  603. while J <= J2 do
  604. begin
  605. Temp[K] := Indexes[J];
  606. Inc(J); Inc(K);
  607. end;
  608. for I := 0 to K - 1 do
  609. begin
  610. Indexes[I + I1] := Temp[I];
  611. end;
  612. end;
  613. procedure Recurse(I1, I2: Integer);
  614. var
  615. I, IX: Integer;
  616. begin
  617. if I1 = I2 then
  618. Indexes[I1] := I1
  619. else if Indexes[I1] = Indexes[I2] then
  620. begin
  621. if Values[I1].X <= Values[I2].X then
  622. begin
  623. for I := I1 to I2 do Indexes[I] := I;
  624. end
  625. else
  626. begin
  627. IX := I1 + I2;
  628. for I := I1 to I2 do Indexes[I] := IX - I;
  629. end;
  630. end
  631. else
  632. begin
  633. IX := (I1 + I2) div 2;
  634. Recurse(I1, IX);
  635. Recurse(IX + 1, I2);
  636. Merge(I1, IX, IX + 1, I2);
  637. end;
  638. end;
  639. var
  640. I, Index, S: Integer;
  641. begin
  642. SetLength(Temp, Length(Values));
  643. SetLength(Indexes, Length(Values));
  644. Index := 0;
  645. S := Math.Sign(Values[1].X - Values[0].X);
  646. if S = 0 then S := 1;
  647. Indexes[0] := 0;
  648. Bounds.Left := Values[0].X;
  649. Bounds.Top := Values[0].Y;
  650. Bounds.Right := Bounds.Left;
  651. Bounds.Bottom := Bounds.Top;
  652. for I := 1 to High(Values) do
  653. begin
  654. if Math.Sign(Values[I].X - Values[I - 1].X) = -S then
  655. begin
  656. S := -S;
  657. Inc(Index);
  658. end;
  659. if Values[I].X < Bounds.Left then
  660. Bounds.Left := Values[I].X;
  661. if Values[I].Y < Bounds.Top then
  662. Bounds.Top := Values[I].Y;
  663. if Values[I].X > Bounds.Right then
  664. Bounds.Right := Values[I].X;
  665. if Values[I].Y > Bounds.Bottom then
  666. Bounds.Bottom := Values[I].Y;
  667. Indexes[I] := Index;
  668. end;
  669. Recurse(0, High(Values));
  670. end;
  671. function DelaunayTriangulation(Points: TArrayOfFloatPoint): TArrayOfTriangleVertexIndices;
  672. var
  673. Complete: array of Byte;
  674. Edges: array of array [0 .. 1] of Integer;
  675. ByteIndex, Bit: Byte;
  676. MaxVerticesCount, EdgeCount, MaxEdgeCount, MaxTriangleCount: Integer;
  677. // For super triangle
  678. ScaledDeltaMax: TFloat;
  679. Mid: TFloatPoint;
  680. Bounds: TFloatRect;
  681. // General Variables
  682. SortedVertexIndices: TArrayOfInteger;
  683. TriangleCount, VertexCount, I, J, K: Integer;
  684. CenterX, CenterY, RadSqr: TFloat;
  685. Inside: Boolean;
  686. const
  687. CSuperTriangleCount = 3; // -> super triangle
  688. CTolerance = 0.000001;
  689. function InCircle(Pt, Pt1, Pt2, Pt3: TFloatPoint): Boolean;
  690. // Return TRUE if the point Pt(x, y) lies inside the circumcircle made up by
  691. // points Pt1(x, y) Pt2(x, y) Pt3(x, y)
  692. // The circumcircle centre is returned in (CenterX, CenterY) and the radius r
  693. // NOTE: A point on the edge is inside the circumcircle
  694. var
  695. M1, M2, MX1, MY1, MX2, MY2: Double;
  696. DeltaX, DeltaY, DeltaRadSqr, AbsY1Y2, AbsY2Y3: Double;
  697. begin
  698. AbsY1Y2 := Abs(Pt1.Y - Pt2.Y);
  699. AbsY2Y3 := Abs(Pt2.Y - Pt3.Y);
  700. // Check for coincident points
  701. if (AbsY1Y2 < CTolerance) and (AbsY2Y3 < CTolerance) then
  702. begin
  703. Result := False;
  704. Exit;
  705. end;
  706. if AbsY1Y2 < CTolerance then
  707. begin
  708. M2 := -(Pt3.X - Pt2.X) / (Pt3.Y - Pt2.Y);
  709. MX2 := (Pt2.X + Pt3.X) * 0.5;
  710. MY2 := (Pt2.Y + Pt3.Y) * 0.5;
  711. CenterX := (Pt2.X + Pt1.X) * 0.5;
  712. CenterY := M2 * (CenterX - MX2) + MY2;
  713. end
  714. else if AbsY2Y3 < CTolerance then
  715. begin
  716. M1 := -(Pt2.X - Pt1.X) / (Pt2.Y - Pt1.Y);
  717. MX1 := (Pt1.X + Pt2.X) * 0.5;
  718. MY1 := (Pt1.Y + Pt2.Y) * 0.5;
  719. CenterX := (Pt3.X + Pt2.X) * 0.5;
  720. CenterY := M1 * (CenterX - MX1) + MY1;
  721. end
  722. else
  723. begin
  724. M1 := -(Pt2.X - Pt1.X) / (Pt2.Y - Pt1.Y);
  725. M2 := -(Pt3.X - Pt2.X) / (Pt3.Y - Pt2.Y);
  726. MX1 := (Pt1.X + Pt2.X) * 0.5;
  727. MX2 := (Pt2.X + Pt3.X) * 0.5;
  728. MY1 := (Pt1.Y + Pt2.Y) * 0.5;
  729. MY2 := (Pt2.Y + Pt3.Y) * 0.5;
  730. CenterX := (M1 * MX1 - M2 * Mx2 + My2 - MY1) / (M1 - M2);
  731. if (AbsY1Y2 > AbsY2Y3) then
  732. CenterY := M1 * (CenterX - MX1) + MY1
  733. else
  734. CenterY := M2 * (CenterX - MX2) + MY2;
  735. end;
  736. DeltaX := Pt2.X - CenterX;
  737. DeltaY := Pt2.Y - CenterY;
  738. RadSqr := DeltaX * DeltaX + DeltaY * DeltaY;
  739. DeltaX := Pt.X - CenterX;
  740. DeltaY := Pt.Y - CenterY;
  741. DeltaRadSqr := Sqr(DeltaX) + Sqr(DeltaY);
  742. Result := (DeltaRadSqr - RadSqr) <= CTolerance;
  743. end;
  744. begin
  745. VertexCount := Length(Points);
  746. MaxVerticesCount := VertexCount + CSuperTriangleCount;
  747. // Sort points by x value and find maximum and minimum vertex bounds.
  748. FastMergeSortX(Points, SortedVertexIndices, Bounds);
  749. // set dynamic array sizes
  750. SetLength(Points, MaxVerticesCount);
  751. MaxTriangleCount := 2 * (MaxVerticesCount - 1);
  752. SetLength(Result, MaxTriangleCount);
  753. MaxEdgeCount := 3 * (MaxVerticesCount - 1);
  754. SetLength(Edges, MaxEdgeCount);
  755. SetLength(Complete, (MaxTriangleCount + 7) shr 3);
  756. // This is to allow calculation of the bounding triangle
  757. with Bounds do
  758. begin
  759. ScaledDeltaMax := 30 * Max(Right - Left, Bottom - Top);
  760. Mid := FloatPoint((Left + Right) * 0.5, (Top + Bottom) * 0.5);
  761. end;
  762. // Set up the super triangle
  763. // This is a triangle which encompasses all the sample points. The super
  764. // triangle coordinates are added to the end of the vertex list. The super
  765. // triangle is the first triangle in the triangle list.
  766. Points[VertexCount] := FloatPoint(Mid.X - ScaledDeltaMax, Mid.Y - ScaledDeltaMax);
  767. Points[VertexCount + 1] := FloatPoint(Mid.X + ScaledDeltaMax, Mid.Y);
  768. Points[VertexCount + 2] := FloatPoint(Mid.X, Mid.Y + ScaledDeltaMax);
  769. Result[0, 0] := VertexCount;
  770. Result[0, 1] := VertexCount + 1;
  771. Result[0, 2] := VertexCount + 2;
  772. Complete[0] := 0;
  773. TriangleCount := 1;
  774. // Include each point one at a time into the existing mesh
  775. for I := 0 to VertexCount - 1 do
  776. begin
  777. EdgeCount := 0;
  778. // Set up the edge buffer.
  779. // If the point [x, y] lies inside the circumcircle then the hree edges of
  780. // that triangle are added to the edge buffer.
  781. J := 0;
  782. repeat
  783. if Complete[J shr 3] and (1 shl (J and $7)) = 0 then
  784. begin
  785. Inside := InCircle(Points[SortedVertexIndices[I]],
  786. Points[Result[J, 0]], Points[Result[J, 1]], Points[Result[J, 2]]);
  787. ByteIndex := J shr 3;
  788. Bit := 1 shl (J and $7);
  789. if (CenterX < Points[SortedVertexIndices[I]].X) and
  790. ((Sqr(Points[SortedVertexIndices[I]].X - CenterX)) > RadSqr) then
  791. Complete[ByteIndex] := Complete[ByteIndex] or Bit
  792. else
  793. if Inside then
  794. begin
  795. Edges[EdgeCount + 0, 0] := Result[J, 0];
  796. Edges[EdgeCount + 0, 1] := Result[J, 1];
  797. Edges[EdgeCount + 1, 0] := Result[J, 1];
  798. Edges[EdgeCount + 1, 1] := Result[J, 2];
  799. Edges[EdgeCount + 2, 0] := Result[J, 2];
  800. Edges[EdgeCount + 2, 1] := Result[J, 0];
  801. EdgeCount := EdgeCount + 3;
  802. Assert(EdgeCount <= MaxEdgeCount);
  803. TriangleCount := TriangleCount - 1;
  804. Result[J] := Result[TriangleCount];
  805. Complete[ByteIndex] := (Complete[ByteIndex] and (not Bit))
  806. or (Complete[TriangleCount shr 3] and Bit);
  807. Continue;
  808. end;
  809. end;
  810. J := J + 1;
  811. until J >= TriangleCount;
  812. // Tag multiple edges
  813. // Note: if all triangles are specified anticlockwise then all
  814. // interior edges are opposite pointing in direction.
  815. for J := 0 to EdgeCount - 2 do
  816. begin
  817. if (Edges[J, 0] <> -1) or (Edges[J, 1] <> -1) then
  818. begin
  819. for K := J + 1 to EdgeCount - 1 do
  820. begin
  821. if (Edges[K, 0] <> -1) or (Edges[K, 1] <> -1) then
  822. begin
  823. if (Edges[J, 0] = Edges[K, 1]) and
  824. (Edges[J, 1] = Edges[K, 0]) then
  825. begin
  826. Edges[J, 0] := -1;
  827. Edges[J, 1] := -1;
  828. Edges[K, 1] := -1;
  829. Edges[K, 0] := -1;
  830. end;
  831. end;
  832. end;
  833. end;
  834. end;
  835. // Form new triangles for the current point.
  836. // Skipping over any tagged edges. All edges are arranged in clockwise
  837. // order.
  838. for J := 0 to EdgeCount - 1 do
  839. begin
  840. if (Edges[J, 0] <> -1) or (Edges[J, 1] <> -1) then
  841. begin
  842. Result[TriangleCount, 0] := Edges[J, 0];
  843. Result[TriangleCount, 1] := Edges[J, 1];
  844. Result[TriangleCount, 2] := SortedVertexIndices[I];
  845. ByteIndex := TriangleCount shr 3;
  846. Bit := 1 shl (TriangleCount and $7);
  847. Complete[ByteIndex] := Complete[ByteIndex] and not Bit;
  848. Inc(TriangleCount);
  849. Assert(TriangleCount <= MaxTriangleCount);
  850. end;
  851. end;
  852. end;
  853. // Remove triangles with supertriangle vertices
  854. // These are triangles which have a vertex number greater than VertexCount
  855. I := 0;
  856. repeat
  857. if (Result[I, 0] >= VertexCount) or
  858. (Result[I, 1] >= VertexCount) or
  859. (Result[I, 2] >= VertexCount) then
  860. begin
  861. TriangleCount := TriangleCount - 1;
  862. Result[I, 0] := Result[TriangleCount, 0];
  863. Result[I, 1] := Result[TriangleCount, 1];
  864. Result[I, 2] := Result[TriangleCount, 2];
  865. I := I - 1;
  866. end;
  867. I := I + 1;
  868. until I >= TriangleCount;
  869. SetLength(Points, Length(Points) - 3);
  870. SetLength(Result, TriangleCount);
  871. end;
  872. function BuildArc(const P: TFloatPoint; StartAngle, EndAngle, Radius: TFloat;
  873. Steps: Integer): TArrayOfFloatPoint;
  874. var
  875. I: Integer;
  876. C, D: TFloatPoint;
  877. begin
  878. SetLength(Result, Steps);
  879. SinCos(StartAngle, Radius, C.Y, C.X);
  880. Result[0] := OffsetPoint(P, C);
  881. GR32_Math.SinCos((EndAngle - StartAngle) / (Steps - 1), D.Y, D.X);
  882. for I := 1 to Steps - 1 do
  883. begin
  884. C := FloatPoint(C.X * D.X - C.Y * D.Y, C.Y * D.X + C.X * D.Y);
  885. Result[I] := OffsetPoint(P, C);
  886. end;
  887. end;
  888. function BuildArc(const P: TFloatPoint; StartAngle, EndAngle, Radius: TFloat): TArrayOfFloatPoint;
  889. const
  890. MINSTEPS = 6;
  891. SQUAREDMINSTEPS = Sqr(MINSTEPS);
  892. var
  893. Temp: TFloat;
  894. Steps: Integer;
  895. begin
  896. // The code below was previously:
  897. //
  898. // Steps := Max(MINSTEPS, System.Round(Sqrt(Abs(Radius)) *
  899. // Abs(EndAngle - StartAngle)));
  900. //
  901. // However, for small radii, the square root calculation is performed with
  902. // the result that the output is set to 6 anyway. In this case (only a few
  903. // drawing operations), the performance spend for this calculation is dominant
  904. // for large radii (when a lot of CPU intensive drawing takes place), the
  905. // more expensive float point comparison (Temp < SQUAREDMINSTEPS) is not very
  906. // significant
  907. Temp := Abs(Radius) * Sqr(EndAngle - StartAngle);
  908. if Temp < SQUAREDMINSTEPS then
  909. Steps := 6
  910. else
  911. Steps := Round(Sqrt(Temp));
  912. Result := BuildArc(P, StartAngle, EndAngle, Radius, Steps);
  913. end;
  914. function BuildArc(const P: TFixedPoint; StartAngle, EndAngle, Radius: TFloat;
  915. Steps: Integer): TArrayOfFixedPoint;
  916. var
  917. I: Integer;
  918. C, D: TFloatPoint;
  919. begin
  920. SetLength(Result, Steps);
  921. SinCos(StartAngle, Radius, C.Y, C.X);
  922. Result[0] := OffsetPoint(P, C);
  923. GR32_Math.SinCos((EndAngle - StartAngle) / (Steps - 1), D.Y, D.X);
  924. for I := 1 to Steps - 1 do
  925. begin
  926. C := FloatPoint(C.X * D.X - C.Y * D.Y, C.Y * D.X + C.X * D.Y);
  927. Result[I] := OffsetPoint(P, FixedPoint(C));
  928. end;
  929. end;
  930. function BuildArc(const P: TFixedPoint; StartAngle, EndAngle, Radius: TFloat): TArrayOfFixedPoint;
  931. const
  932. MINSTEPS = 6;
  933. SQUAREDMINSTEPS = Sqr(MINSTEPS);
  934. var
  935. Temp: TFloat;
  936. Steps: Integer;
  937. begin
  938. // The code below was previously:
  939. //
  940. // Steps := Clamp(System.Round(Sqrt(Abs(Radius)) *
  941. // Abs(EndAngle - StartAngle)), MINSTEPS, $100000);
  942. //
  943. // However, for small radii, the square root calculation is performed with
  944. // the result that the output is set to 6 anyway. In this case (only a few
  945. // drawing operations), the performance spend for this calculation is dominant
  946. // for large radii (when a lot of CPU intensive drawing takes place), the
  947. // more expensive float point comparison (Temp < SQUAREDMINSTEPS) is not very
  948. // significant
  949. Temp := Abs(Radius) * Sqr(EndAngle - StartAngle);
  950. if Temp < SQUAREDMINSTEPS then
  951. Steps := MINSTEPS
  952. else
  953. Steps := Clamp(Round(Sqrt(Temp)), $100000);
  954. Result := BuildArc(P, StartAngle, EndAngle, Radius, Steps);
  955. end;
  956. function Line(const P1, P2: TFloatPoint): TArrayOfFloatPoint;
  957. begin
  958. SetLength(Result, 2);
  959. Result[0] := P1;
  960. Result[1] := P2;
  961. end;
  962. function Line(const X1, Y1, X2, Y2: TFloat): TArrayOfFloatPoint; overload;
  963. begin
  964. SetLength(Result, 2);
  965. Result[0] := FloatPoint(X1, Y1);
  966. Result[1] := FloatPoint(X2, Y2);
  967. end;
  968. function VertLine(const X, Y1, Y2: TFloat): TArrayOfFloatPoint;
  969. begin
  970. SetLength(Result, 2);
  971. Result[0] := FloatPoint(X, Y1);
  972. Result[1] := FloatPoint(X, Y2);
  973. end;
  974. function HorzLine(const X1, Y, X2: TFloat): TArrayOfFloatPoint;
  975. begin
  976. SetLength(Result, 2);
  977. Result[0] := FloatPoint(X1, Y);
  978. Result[1] := FloatPoint(X2, Y);
  979. end;
  980. function CalculateCircleSteps(Radius: TFloat): Cardinal;
  981. var
  982. AbsRadius: TFloat;
  983. begin
  984. AbsRadius := Abs(Radius);
  985. Result := Trunc(Pi / (ArcCos(AbsRadius / (AbsRadius + 0.125))));
  986. end;
  987. function Circle(const P: TFloatPoint; const Radius: TFloat;
  988. Steps: Integer): TArrayOfFloatPoint;
  989. var
  990. I: Integer;
  991. M: TFloat;
  992. C, D: TFloatPoint;
  993. begin
  994. if Steps <= 0 then
  995. Steps := CalculateCircleSteps(Radius);
  996. SetLength(Result, Steps);
  997. M := 2 * System.Pi / Steps;
  998. // first item
  999. Result[0].X := Radius + P.X;
  1000. Result[0].Y := P.Y;
  1001. // calculate complex offset
  1002. GR32_Math.SinCos(M, C.Y, C.X);
  1003. D.X := Radius * C.X;
  1004. D.Y := Radius * C.Y;
  1005. // second item
  1006. Result[1].X := D.X + P.X;
  1007. Result[1].Y := D.Y + P.Y;
  1008. // other items
  1009. for I := 2 to Steps - 1 do
  1010. begin
  1011. D := FloatPoint(D.X * C.X - D.Y * C.Y, D.Y * C.X + D.X * C.Y);
  1012. Result[I].X := D.X + P.X;
  1013. Result[I].Y := D.Y + P.Y;
  1014. end;
  1015. end;
  1016. function Circle(const P: TFloatPoint; const Radius: TFloat): TArrayOfFloatPoint;
  1017. begin
  1018. Result := Circle(P, Radius, CalculateCircleSteps(Radius));
  1019. end;
  1020. function Circle(const X, Y, Radius: TFloat; Steps: Integer): TArrayOfFloatPoint;
  1021. begin
  1022. Result := Circle(FloatPoint(X, Y), Radius, Steps);
  1023. end;
  1024. function Circle(const X, Y, Radius: TFloat): TArrayOfFloatPoint;
  1025. begin
  1026. Result := Circle(FloatPoint(X, Y), Radius, CalculateCircleSteps(Radius));
  1027. end;
  1028. function Circle(const R: TRect): TArrayOfFloatPoint;
  1029. begin
  1030. Result := Circle(
  1031. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1032. Min(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)));
  1033. end;
  1034. function Circle(const R: TRect; Steps: Integer): TArrayOfFloatPoint;
  1035. begin
  1036. Result := Circle(
  1037. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1038. Min(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)), Steps);
  1039. end;
  1040. function Circle(const R: TFloatRect): TArrayOfFloatPoint;
  1041. begin
  1042. Result := Circle(
  1043. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1044. Min(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)));
  1045. end;
  1046. function Circle(const R: TFloatRect; Steps: Integer): TArrayOfFloatPoint;
  1047. begin
  1048. Result := Circle(
  1049. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1050. Min(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)), Steps);
  1051. end;
  1052. function Pie(const P: TFloatPoint; const Radius: TFloat;
  1053. const Angle, Offset: TFloat; Steps: Integer): TArrayOfFloatPoint;
  1054. var
  1055. I: Integer;
  1056. C, D: TFloatPoint;
  1057. begin
  1058. SetLength(Result, Steps + 2);
  1059. Result[0] := P;
  1060. // calculate initial position
  1061. GR32_Math.SinCos(Offset, Radius, D.Y, D.X);
  1062. Result[1].X := D.X + P.X;
  1063. Result[1].Y := D.Y + P.Y;
  1064. // calculate complex offset
  1065. GR32_Math.SinCos(Angle / Steps, C.Y, C.X);
  1066. // other items
  1067. for I := 2 to Steps + 1 do
  1068. begin
  1069. D := FloatPoint(D.X * C.X - D.Y * C.Y, D.Y * C.X + D.X * C.Y);
  1070. Result[I].X := D.X + P.X;
  1071. Result[I].Y := D.Y + P.Y;
  1072. end;
  1073. end;
  1074. function Pie(const P: TFloatPoint; const Radius: TFloat;
  1075. const Angle: TFloat; const Offset: TFloat = 0): TArrayOfFloatPoint;
  1076. begin
  1077. Result := Pie(P, Radius, Angle, Offset, CalculateCircleSteps(Radius));
  1078. end;
  1079. function Pie(const P: TFloatPoint; const Radius: TFloat;
  1080. const Angle: TFloat; Steps: Integer): TArrayOfFloatPoint;
  1081. begin
  1082. Result := Pie(P, Radius, Angle, 0, Steps);
  1083. end;
  1084. function Pie(const X, Y, Radius: TFloat; const Angle: TFloat;
  1085. const Offset: TFloat = 0): TArrayOfFloatPoint;
  1086. begin
  1087. Result := Pie(FloatPoint(X, Y), Radius, Angle, Offset, CalculateCircleSteps(Radius));
  1088. end;
  1089. function Pie(const X, Y, Radius: TFloat; const Angle, Offset: TFloat;
  1090. Steps: Integer): TArrayOfFloatPoint;
  1091. begin
  1092. Result := Pie(FloatPoint(X, Y), Radius, Angle, Offset, Steps);
  1093. end;
  1094. function Pie(const X, Y, Radius: TFloat; const Angle: TFloat;
  1095. Steps: Integer): TArrayOfFloatPoint;
  1096. begin
  1097. Result := Pie(FloatPoint(X, Y), Radius, Angle, 0, Steps);
  1098. end;
  1099. function Ellipse(const P, R: TFloatPoint; Steps: Integer): TArrayOfFloatPoint;
  1100. var
  1101. I: Integer;
  1102. M: TFloat;
  1103. C, D: TFloatPoint;
  1104. begin
  1105. SetLength(Result, Steps);
  1106. M := 2 * System.Pi / Steps;
  1107. // first item
  1108. Result[0].X := R.X + P.X;
  1109. Result[0].Y := P.Y;
  1110. // calculate complex offset
  1111. GR32_Math.SinCos(M, C.Y, C.X);
  1112. D := C;
  1113. // second item
  1114. Result[1].X := R.X * D.X + P.X;
  1115. Result[1].Y := R.Y * D.Y + P.Y;
  1116. // other items
  1117. for I := 2 to Steps - 1 do
  1118. begin
  1119. D := FloatPoint(D.X * C.X - D.Y * C.Y, D.Y * C.X + D.X * C.Y);
  1120. Result[I].X := R.X * D.X + P.X;
  1121. Result[I].Y := R.Y * D.Y + P.Y;
  1122. end;
  1123. end;
  1124. function Ellipse(const P, R: TFloatPoint): TArrayOfFloatPoint;
  1125. begin
  1126. Result := Ellipse(P, R, CalculateCircleSteps(Min(R.X, R.Y)));
  1127. end;
  1128. function Ellipse(const X, Y, Rx, Ry: TFloat; Steps: Integer): TArrayOfFloatPoint;
  1129. begin
  1130. Result := Ellipse(FloatPoint(X, Y), FloatPoint(Rx, Ry), Steps);
  1131. end;
  1132. function Ellipse(const X, Y, Rx, Ry: TFloat): TArrayOfFloatPoint;
  1133. begin
  1134. Result := Ellipse(FloatPoint(X, Y), FloatPoint(Rx, Ry),
  1135. CalculateCircleSteps(Min(Rx, Ry)));
  1136. end;
  1137. function Ellipse(const R: TRect): TArrayOfFloatPoint;
  1138. begin
  1139. Result := Ellipse(
  1140. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1141. FloatPoint(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)));
  1142. end;
  1143. function Ellipse(const R: TFloatRect): TArrayOfFloatPoint;
  1144. begin
  1145. Result := Ellipse(
  1146. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1147. FloatPoint(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)));
  1148. end;
  1149. function Ellipse(const R: TRect; Steps: Integer): TArrayOfFloatPoint;
  1150. begin
  1151. Result := Ellipse(
  1152. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1153. FloatPoint(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)), Steps);
  1154. end;
  1155. function Ellipse(const R: TFloatRect; Steps: Integer): TArrayOfFloatPoint;
  1156. begin
  1157. Result := Ellipse(
  1158. FloatPoint(0.5 * (R.Right + R.Left), 0.5 * (R.Bottom + R.Top)),
  1159. FloatPoint(0.5 * (R.Right - R.Left), 0.5 * (R.Bottom - R.Top)), Steps);
  1160. end;
  1161. function Star(const X, Y, Radius: TFloat; Vertices: Integer = 5;
  1162. Rotation: TFloat = 0): TArrayOfFloatPoint;
  1163. var
  1164. Alpha, Ratio: TFloat;
  1165. begin
  1166. Alpha := Pi * (Vertices - 2 * ((Vertices - 1) shr 1)) / Vertices;
  1167. Ratio := Sin(Alpha * 0.5) / Sin( Alpha * 0.5 + Pi / Vertices);
  1168. Result := Star(X, Y, Ratio * Radius, Radius, Vertices, Rotation);
  1169. end;
  1170. function Star(const P: TFloatPoint; const Radius: TFloat; Vertices: Integer = 5;
  1171. Rotation: TFloat = 0): TArrayOfFloatPoint;
  1172. var
  1173. Alpha, Ratio: TFloat;
  1174. begin
  1175. Alpha := Pi * (Vertices - 2 * ((Vertices - 1) shr 1)) / Vertices;
  1176. Ratio := Sin(Alpha * 0.5) / Sin(Alpha * 0.5 + Pi / Vertices);
  1177. Result := Star(P, Ratio * Radius, Radius, Vertices, Rotation);
  1178. end;
  1179. function Star(const X, Y, InnerRadius, OuterRadius: TFloat;
  1180. Vertices: Integer = 5; Rotation: TFloat = 0): TArrayOfFloatPoint;
  1181. begin
  1182. Result := Star(FloatPoint(X, Y), InnerRadius, OuterRadius, Vertices, Rotation);
  1183. end;
  1184. function Star(const P: TFloatPoint; const InnerRadius, OuterRadius: TFloat;
  1185. Vertices: Integer = 5; Rotation: TFloat = 0): TArrayOfFloatPoint;
  1186. var
  1187. I: Integer;
  1188. M: TFloat;
  1189. C, D: TFloatPoint;
  1190. begin
  1191. SetLength(Result, 2 * Vertices);
  1192. M := System.Pi / Vertices;
  1193. // calculate complex offset
  1194. GR32_Math.SinCos(M, C.Y, C.X);
  1195. // first item
  1196. if Rotation = 0 then
  1197. begin
  1198. Result[0].X := OuterRadius + P.X;
  1199. Result[0].Y := P.Y;
  1200. D := C;
  1201. end
  1202. else
  1203. begin
  1204. GR32_Math.SinCos(Rotation, D.Y, D.X);
  1205. Result[0].X := OuterRadius * D.X + P.X;
  1206. Result[0].Y := OuterRadius * D.Y + P.Y;
  1207. D := FloatPoint(D.X * C.X - D.Y * C.Y, D.Y * C.X + D.X * C.Y);
  1208. end;
  1209. // second item
  1210. Result[1].X := InnerRadius * D.X + P.X;
  1211. Result[1].Y := InnerRadius * D.Y + P.Y;
  1212. // other items
  1213. for I := 2 to (2 * Vertices) - 1 do
  1214. begin
  1215. D := FloatPoint(D.X * C.X - D.Y * C.Y, D.Y * C.X + D.X * C.Y);
  1216. if I mod 2 = 0 then
  1217. begin
  1218. Result[I].X := OuterRadius * D.X + P.X;
  1219. Result[I].Y := OuterRadius * D.Y + P.Y;
  1220. end
  1221. else
  1222. begin
  1223. Result[I].X := InnerRadius * D.X + P.X;
  1224. Result[I].Y := InnerRadius * D.Y + P.Y;
  1225. end;
  1226. end;
  1227. end;
  1228. function Rectangle(const R: TFloatRect): TArrayOfFloatPoint;
  1229. begin
  1230. SetLength(Result, 4);
  1231. Result[0] := R.TopLeft;
  1232. Result[1] := FloatPoint(R.Right, R.Top);
  1233. Result[2] := R.BottomRight;
  1234. Result[3] := FloatPoint(R.Left, R.Bottom);
  1235. end;
  1236. function RoundRect(const R: TFloatRect; const Radius: TFloat): TArrayOfFloatPoint;
  1237. var
  1238. R2: TFloatRect;
  1239. begin
  1240. R2 := R;
  1241. GR32.InflateRect(R2, -Radius, -Radius);
  1242. Result := Grow(Rectangle(R2), Radius, jsRound, True);
  1243. end;
  1244. function BuildNormals(const Points: TArrayOfFloatPoint): TArrayOfFloatPoint;
  1245. const
  1246. EPSILON = 1E-4;
  1247. var
  1248. I, Count, NextI: Integer;
  1249. dx, dy, f: Double;
  1250. begin
  1251. Count := Length(Points);
  1252. SetLength(Result, Count);
  1253. I := 0;
  1254. NextI := 1;
  1255. while I < Count do
  1256. begin
  1257. if NextI >= Count then NextI := 0;
  1258. dx := Points[NextI].X - Points[I].X;
  1259. dy := Points[NextI].Y - Points[I].Y;
  1260. f := GR32_Math.Hypot(dx, dy);
  1261. if (f > EPSILON) then
  1262. begin
  1263. f := 1 / f;
  1264. dx := dx * f;
  1265. dy := dy * f;
  1266. end;
  1267. Result[I].X := dy;
  1268. Result[I].Y := -dx;
  1269. Inc(I);
  1270. Inc(NextI);
  1271. end;
  1272. end;
  1273. function BuildNormals(const Points: TArrayOfFixedPoint): TArrayOfFixedPoint;
  1274. var
  1275. I, Count, NextI: Integer;
  1276. dx, dy, f: TFixed;
  1277. begin
  1278. Count := Length(Points);
  1279. SetLength(Result, Count);
  1280. I := 0;
  1281. NextI := 1;
  1282. while I < Count do
  1283. begin
  1284. if NextI >= Count then NextI := 0;
  1285. dx := Points[NextI].X - Points[I].X;
  1286. dy := Points[NextI].Y - Points[I].Y;
  1287. f := GR32_Math.Hypot(dx, dy);
  1288. if (f <> 0) then
  1289. begin
  1290. dx := FixedDiv(dx, f);
  1291. dy := FixedDiv(dy, f);
  1292. end;
  1293. Result[I].X := dy;
  1294. Result[I].Y := -dx;
  1295. Inc(I);
  1296. Inc(NextI);
  1297. end;
  1298. end;
  1299. function Grow(const Points: TArrayOfFloatPoint; const Normals: TArrayOfFloatPoint;
  1300. const Delta: TFloat; JoinStyle: TJoinStyle; Closed: Boolean; MiterLimit: TFloat): TArrayOfFloatPoint; overload;
  1301. const
  1302. BUFFSIZEINCREMENT = 128;
  1303. MINDISTPIXEL = 1.414; // just a little bit smaller than sqrt(2),
  1304. // -> set to about 2.5 for a similar output with the previous version
  1305. var
  1306. I, L, H: Integer;
  1307. ResSize, BuffSize: Integer;
  1308. PX, PY: TFloat;
  1309. AngleInv, RMin: TFloat;
  1310. A, B, Dm: TFloatPoint;
  1311. procedure AddPoint(const LongDeltaX, LongDeltaY: TFloat);
  1312. begin
  1313. if ResSize = BuffSize then
  1314. begin
  1315. Inc(BuffSize, BUFFSIZEINCREMENT);
  1316. SetLength(Result, BuffSize);
  1317. end;
  1318. Result[ResSize] := FloatPoint(PX + LongDeltaX, PY + LongDeltaY);
  1319. Inc(ResSize);
  1320. end;
  1321. procedure AddMitered(const X1, Y1, X2, Y2: TFloat);
  1322. var
  1323. R, CX, CY: TFloat;
  1324. begin
  1325. CX := X1 + X2;
  1326. CY := Y1 + Y2;
  1327. R := X1 * CX + Y1 * CY; //(1 - cos(Θ)) (range: 0 <= R <= 2)
  1328. if R < RMin then
  1329. begin
  1330. AddPoint(Delta * X1, Delta * Y1);
  1331. AddPoint(Delta * X2, Delta * Y2);
  1332. end
  1333. else
  1334. begin
  1335. R := Delta / R;
  1336. AddPoint(CX * R, CY * R)
  1337. end;
  1338. end;
  1339. procedure AddBevelled(const X1, Y1, X2, Y2: TFloat);
  1340. var
  1341. R: TFloat;
  1342. begin
  1343. R := X1 * Y2 - X2 * Y1; // cross product
  1344. if R * Delta <= 0 then // ie angle is concave
  1345. AddMitered(X1, Y1, X2, Y2)
  1346. else
  1347. begin
  1348. AddPoint(Delta * X1, Delta * Y1);
  1349. AddPoint(Delta * X2, Delta * Y2);
  1350. end;
  1351. end;
  1352. procedure AddRoundedJoin(const X1, Y1, X2, Y2: TFloat);
  1353. var
  1354. sinA, cosA, A, d: TFloat;
  1355. steps: Integer;
  1356. ii, m,n: Integer;
  1357. C, C2, C3: TFloatPoint;
  1358. begin
  1359. sinA := X1 * Y2 - X2 * Y1;
  1360. cosA := X1 * X2 + Y1 * Y2;
  1361. A := ArcTan2(sinA, cosA);
  1362. steps := Round(Abs(A * AngleInv));
  1363. if sinA < 0 then
  1364. Dm.Y := -Abs(Dm.Y) else
  1365. Dm.Y := Abs(Dm.Y);
  1366. if sinA * Delta < 0 then // ie angle is concave
  1367. begin
  1368. A := Delta / (cosA +1);
  1369. //C = offset pt of concave vertex ...
  1370. C.X := PX + (X1 + X2) * A;
  1371. C.Y := PY + (Y1 + Y2) * A;
  1372. if (I = 0) then m := H else m := I -1;
  1373. if I = H then n := 0 else n := I +1;
  1374. A := Min(SqrDistance(Points[m], Points[I]),
  1375. SqrDistance(Points[n], Points[I]));
  1376. if SqrDistance(C, Points[I]) > A then
  1377. begin
  1378. //there's no room to draw anything ...
  1379. //now get the perpendic. offset from pt2 ...
  1380. C2.X := X1 * Delta;
  1381. C2.Y := Y1 * Delta;
  1382. C3.X := X2 * Delta;
  1383. C3.Y := Y2 * Delta;
  1384. //this will create a self-intersection but it also ensures that
  1385. //the offset will be maintained beyond this intersection ...
  1386. AddPoint(C2.X, C2.Y);
  1387. AddPoint(C3.X, C3.Y);
  1388. Exit;
  1389. end;
  1390. A := Sqrt(A);
  1391. //get the point on the both edges that's same distance from
  1392. //the concave vertex as its closest adjacent vertex.
  1393. //nb: using unit normals as unit vectors here ...
  1394. C2.X := PX + Y1 * A;
  1395. C2.Y := PY - X1 * A;
  1396. C3.X := PX - Y2 * A;
  1397. C3.Y := PY + X2 * A;
  1398. //now Delta offset these points ...
  1399. C2.X := C2.X + X1 * Delta;
  1400. C2.Y := C2.Y + Y1 * Delta;
  1401. C3.X := C3.X + X2 * Delta;
  1402. C3.Y := C3.Y + Y2 * Delta;
  1403. //this will do Delta/MiterLimit radius rounding of concavities ...
  1404. if SqrDistance(C2, C3) < Sqr(Delta *2/MiterLimit) then
  1405. d := Sqrt(SqrDistance(C2, C3))/2 else
  1406. d := Delta/MiterLimit;
  1407. //move point(PX,PY) across the offset path so the
  1408. //rounding path will curve around this new point ...
  1409. A := (d + Delta) / (cosA +1);
  1410. PX := PX + (X1 + X2) * A;
  1411. PY := PY + (Y1 + Y2) * A;
  1412. C2.X := -X1 * d;
  1413. C2.Y := -Y1 * d;
  1414. AddPoint(C2.X, C2.Y);
  1415. for ii := 1 to steps -1 do
  1416. begin
  1417. C2 := FloatPoint(
  1418. C2.X * Dm.X - Dm.Y * C2.Y,
  1419. C2.X * Dm.Y + C2.Y * Dm.X);
  1420. AddPoint(C2.X, C2.Y);
  1421. end;
  1422. end
  1423. else
  1424. begin
  1425. C.X := X1 * Delta;
  1426. C.Y := Y1 * Delta;
  1427. AddPoint(C.X, C.Y);
  1428. for ii := 1 to steps - 1 do
  1429. begin
  1430. C := FloatPoint(
  1431. C.X * Dm.X - C.Y * Dm.Y,
  1432. C.Y * Dm.X + C.X * Dm.Y);
  1433. AddPoint(C.X, C.Y);
  1434. end;
  1435. end;
  1436. end;
  1437. procedure AddJoin(const X, Y, X1, Y1, X2, Y2: TFloat);
  1438. begin
  1439. PX := X;
  1440. PY := Y;
  1441. if (JoinStyle <> jsRoundEx) and ((X1 * Y2 - X2 * Y1) * Delta < 0) then
  1442. begin
  1443. AddPoint(Delta * X1, Delta * Y1);
  1444. AddPoint(Delta * X2, Delta * Y2);
  1445. end else
  1446. case JoinStyle of
  1447. jsMiter: AddMitered(A.X, A.Y, B.X, B.Y);
  1448. jsBevel: AddBevelled(A.X, A.Y, B.X, B.Y);
  1449. jsRound: AddRoundedJoin(A.X, A.Y, B.X, B.Y);
  1450. jsRoundEx: AddRoundedJoin(A.X, A.Y, B.X, B.Y);
  1451. end;
  1452. end;
  1453. begin
  1454. Result := nil;
  1455. if Length(Points) <= 1 then Exit;
  1456. RMin := 2 / Sqr(MiterLimit);
  1457. H := High(Points) - Ord(not Closed);
  1458. while (H >= 0) and (Normals[H].X = 0) and (Normals[H].Y = 0) do Dec(H);
  1459. {** all normals zeroed => Exit }
  1460. if H < 0 then Exit;
  1461. L := 0;
  1462. while (Normals[L].X = 0) and (Normals[L].Y = 0) do Inc(L);
  1463. if Closed then
  1464. A := Normals[H]
  1465. else
  1466. A := Normals[L];
  1467. ResSize := 0;
  1468. BuffSize := BUFFSIZEINCREMENT;
  1469. SetLength(Result, BuffSize);
  1470. // prepare
  1471. if JoinStyle in [jsRound, jsRoundEx] then
  1472. begin
  1473. Dm.X := 1 - 0.5 * Min(3, Sqr(MINDISTPIXEL / Abs(Delta)));
  1474. Dm.Y := Sqrt(1 - Sqr(Dm.X));
  1475. AngleInv := 1 / ArcCos(Dm.X);
  1476. end;
  1477. for I := L to H do
  1478. begin
  1479. B := Normals[I];
  1480. if (B.X = 0) and (B.Y = 0) then Continue;
  1481. with Points[I] do AddJoin(X, Y, A.X, A.Y, B.X, B.Y);
  1482. A := B;
  1483. end;
  1484. if not Closed then
  1485. with Points[High(Points)] do AddJoin(X, Y, A.X, A.Y, A.X, A.Y);
  1486. SetLength(Result, ResSize);
  1487. end;
  1488. function Grow(const Points: TArrayOfFloatPoint;
  1489. const Delta: TFloat; JoinStyle: TJoinStyle; Closed: Boolean;
  1490. MiterLimit: TFloat): TArrayOfFloatPoint; overload;
  1491. var
  1492. Normals: TArrayOfFloatPoint;
  1493. begin
  1494. Normals := BuildNormals(Points);
  1495. Result := Grow(Points, Normals, Delta, JoinStyle, Closed, MiterLimit);
  1496. end;
  1497. // Converts an array of points in TFixed format to an array of points in TFloat format
  1498. function FixedPointToFloatPoint(const Points: TArrayOfFixedPoint)
  1499. : TArrayOfFloatPoint;
  1500. var
  1501. Index: Integer;
  1502. begin
  1503. if Length(Points) > 0 then
  1504. begin
  1505. SetLength(Result, Length(Points));
  1506. for Index := 0 to Length(Points) - 1 do
  1507. begin
  1508. Result[Index].X := Points[Index].X * FixedToFloat;
  1509. Result[Index].Y := Points[Index].Y * FixedToFloat;
  1510. end;
  1511. end;
  1512. end;
  1513. // Converts an array of array of points in TFixed format to an array of array of points in TFloat format
  1514. function FixedPointToFloatPoint(const Points: TArrayOfArrayOfFixedPoint)
  1515. : TArrayOfArrayOfFloatPoint;
  1516. var
  1517. Index, PointIndex: Integer;
  1518. begin
  1519. if Length(Points) > 0 then
  1520. begin
  1521. SetLength(Result, Length(Points));
  1522. for Index := 0 to Length(Points) - 1 do
  1523. begin
  1524. SetLength(Result[Index], Length(Points[Index]));
  1525. for PointIndex := 0 to Length(Points[Index]) - 1 do
  1526. begin
  1527. Result[Index, PointIndex].X := Points[Index, PointIndex].X * FixedToFloat;
  1528. Result[Index, PointIndex].Y := Points[Index, PointIndex].Y * FixedToFloat;
  1529. end;
  1530. end;
  1531. end;
  1532. end;
  1533. // Converts an array of points in TFixed format to an array of points in TFloat format
  1534. function FloatPointToFixedPoint(const Points: TArrayOfFloatPoint)
  1535. : TArrayOfFixedPoint;
  1536. var
  1537. Index: Integer;
  1538. begin
  1539. if Length(Points) > 0 then
  1540. begin
  1541. SetLength(Result, Length(Points));
  1542. for Index := 0 to Length(Points) - 1 do
  1543. begin
  1544. Result[Index].X := Fixed(Points[Index].X);
  1545. Result[Index].Y := Fixed(Points[Index].Y);
  1546. end;
  1547. end;
  1548. end;
  1549. // Converts an array of array of points in TFixed format to an array of array of points in TFloat format
  1550. function FloatPointToFixedPoint(const Points: TArrayOfArrayOfFloatPoint)
  1551. : TArrayOfArrayOfFixedPoint;
  1552. var
  1553. Index, PointIndex: Integer;
  1554. begin
  1555. if Length(Points) > 0 then
  1556. begin
  1557. SetLength(Result, Length(Points));
  1558. for Index := 0 to Length(Points) - 1 do
  1559. begin
  1560. SetLength(Result[Index], Length(Points[Index]));
  1561. for PointIndex := 0 to Length(Points[Index]) - 1 do
  1562. begin
  1563. Result[Index, PointIndex].X := Fixed(Points[Index, PointIndex].X);
  1564. Result[Index, PointIndex].Y := Fixed(Points[Index, PointIndex].Y);
  1565. end;
  1566. end;
  1567. end;
  1568. end;
  1569. function Grow(const Points: TArrayOfFixedPoint; const Normals: TArrayOfFixedPoint;
  1570. const Delta: TFixed; JoinStyle: TJoinStyle = jsMiter;
  1571. Closed: Boolean = True; MiterLimit: TFixed = DEFAULT_MITER_LIMIT_FIXED): TArrayOfFixedPoint; overload;
  1572. var
  1573. tmp: TArrayOfFloatPoint;
  1574. begin
  1575. tmp := Grow(FixedPointToFloatPoint(Points), FixedPointToFloatPoint(Normals),
  1576. Delta * FixedToFloat, JoinStyle, Closed, MiterLimit * FixedToFloat);
  1577. result := FloatPointToFixedPoint(tmp);
  1578. end;
  1579. function Grow(const Points: TArrayOfFixedPoint;
  1580. const Delta: TFixed; JoinStyle: TJoinStyle = jsMiter;
  1581. Closed: Boolean = True; MiterLimit: TFixed = DEFAULT_MITER_LIMIT_FIXED): TArrayOfFixedPoint; overload;
  1582. var
  1583. Normals: TArrayOfFixedPoint;
  1584. begin
  1585. Normals := BuildNormals(Points);
  1586. Result := Grow(Points, Normals, Delta, JoinStyle, Closed, MiterLimit);
  1587. end;
  1588. function ReversePolygon(const Points: TArrayOfFloatPoint): TArrayOfFloatPoint;
  1589. var
  1590. I, L: Integer;
  1591. begin
  1592. L := Length(Points);
  1593. SetLength(Result, L);
  1594. Dec(L);
  1595. for I := 0 to L do
  1596. Result[I] := Points[L - I];
  1597. end;
  1598. function ReversePolygon(const Points: TArrayOfFixedPoint): TArrayOfFixedPoint;
  1599. var
  1600. I, L: Integer;
  1601. begin
  1602. L := Length(Points);
  1603. SetLength(Result, L);
  1604. Dec(L);
  1605. for I := 0 to L do
  1606. Result[I] := Points[L - I];
  1607. end;
  1608. function BuildLineEnd(const P, N: TFloatPoint; const W: TFloat;
  1609. EndStyle: TEndStyle): TArrayOfFloatPoint; overload;
  1610. var
  1611. a1, a2: TFloat;
  1612. begin
  1613. case EndStyle of
  1614. esButt:
  1615. begin
  1616. Result := nil;
  1617. end;
  1618. esSquare:
  1619. begin
  1620. SetLength(Result, 2);
  1621. Result[0].X := P.X + (N.X - N.Y) * W;
  1622. Result[0].Y := P.Y + (N.Y + N.X) * W;
  1623. Result[1].X := P.X - (N.X + N.Y) * W;
  1624. Result[1].Y := P.Y - (N.Y - N.X) * W;
  1625. end;
  1626. esRound:
  1627. begin
  1628. a1 := ArcTan2(N.Y, N.X);
  1629. a2 := ArcTan2(-N.Y, -N.X);
  1630. if a2 < a1 then a2 := a2 + TWOPI;
  1631. Result := BuildArc(P, a1, a2, W);
  1632. end;
  1633. end;
  1634. end;
  1635. function BuildLineEnd(const P, N: TFixedPoint; const W: TFixed;
  1636. EndStyle: TEndStyle): TArrayOfFixedPoint; overload;
  1637. var
  1638. a1, a2: TFloat;
  1639. begin
  1640. case EndStyle of
  1641. esButt:
  1642. begin
  1643. Result := nil;
  1644. end;
  1645. esSquare:
  1646. begin
  1647. SetLength(Result, 2);
  1648. Result[0].X := P.X + (N.X - N.Y) * W;
  1649. Result[0].Y := P.Y + (N.Y + N.X) * W;
  1650. Result[1].X := P.X - (N.X + N.Y) * W;
  1651. Result[1].Y := P.Y - (N.Y - N.X) * W;
  1652. end;
  1653. esRound:
  1654. begin
  1655. a1 := ArcTan2(N.Y, N.X);
  1656. a2 := ArcTan2(-N.Y, -N.X);
  1657. if a2 < a1 then a2 := a2 + TWOPI;
  1658. Result := BuildArc(P, a1, a2, W);
  1659. end;
  1660. end;
  1661. end;
  1662. function BuildPolyline(const Points: TArrayOfFloatPoint; StrokeWidth: TFloat;
  1663. JoinStyle: TJoinStyle; EndStyle: TEndStyle; MiterLimit: TFloat): TArrayOfFloatPoint;
  1664. var
  1665. L, H: Integer;
  1666. Normals: TArrayOfFloatPoint;
  1667. P1, P2, E1, E2: TArrayOfFloatPoint;
  1668. V: TFloat;
  1669. P: PFloatPoint;
  1670. begin
  1671. Result := nil;
  1672. V := StrokeWidth * 0.5;
  1673. Normals := BuildNormals(Points);
  1674. H := High(Points) - 1;
  1675. while (H >= 0) and (Normals[H].X = 0) and (Normals[H].Y = 0) do Dec(H);
  1676. if H < 0 then Exit;
  1677. L := 0;
  1678. while (Normals[L].X = 0) and (Normals[L].Y = 0) do Inc(L);
  1679. P1 := Grow(Points, Normals, V, JoinStyle, False, MiterLimit);
  1680. P2 := ReversePolygon(Grow(Points, Normals, -V, JoinStyle, False, MiterLimit));
  1681. E1 := BuildLineEnd(Points[0], Normals[L], -V, EndStyle);
  1682. E2 := BuildLineEnd(Points[High(Points)], Normals[H], V, EndStyle);
  1683. SetLength(Result, Length(P1) + Length(P2) + Length(E1) + Length(E2));
  1684. P := @Result[0];
  1685. Move(E1[0], P^, Length(E1) * SizeOf(TFloatPoint)); Inc(P, Length(E1));
  1686. Move(P1[0], P^, Length(P1) * SizeOf(TFloatPoint)); Inc(P, Length(P1));
  1687. Move(E2[0], P^, Length(E2) * SizeOf(TFloatPoint)); Inc(P, Length(E2));
  1688. Move(P2[0], P^, Length(P2) * SizeOf(TFloatPoint));
  1689. end;
  1690. function BuildPolyPolyLine(const Points: TArrayOfArrayOfFloatPoint;
  1691. Closed: Boolean; StrokeWidth: TFloat; JoinStyle: TJoinStyle;
  1692. EndStyle: TEndStyle; MiterLimit: TFloat): TArrayOfArrayOfFloatPoint;
  1693. var
  1694. I: Integer;
  1695. P1, P2: TArrayOfFloatPoint;
  1696. Dst: TArrayOfArrayOfFloatPoint;
  1697. Normals: TArrayOfFloatPoint;
  1698. HalfStrokeWidth: TFloat;
  1699. begin
  1700. if Closed then
  1701. begin
  1702. SetLength(Dst, Length(Points) * 2);
  1703. HalfStrokeWidth := StrokeWidth * 0.5;
  1704. for I := 0 to High(Points) do
  1705. begin
  1706. Normals := BuildNormals(Points[I]);
  1707. P1 := Grow(Points[I], Normals, HalfStrokeWidth, JoinStyle, True, MiterLimit);
  1708. P2 := Grow(Points[I], Normals, -HalfStrokeWidth, JoinStyle, True, MiterLimit);
  1709. Dst[I * 2] := P1;
  1710. Dst[I * 2 + 1] := ReversePolygon(P2);
  1711. end;
  1712. end
  1713. else
  1714. begin
  1715. SetLength(Dst, Length(Points));
  1716. for I := 0 to High(Points) do
  1717. Dst[I] := BuildPolyline(Points[I], StrokeWidth, JoinStyle, EndStyle, MiterLimit);
  1718. end;
  1719. Result := Dst;
  1720. end;
  1721. function BuildPolyline(const Points: TArrayOfFixedPoint; StrokeWidth: TFixed;
  1722. JoinStyle: TJoinStyle; EndStyle: TEndStyle; MiterLimit: TFixed): TArrayOfFixedPoint;
  1723. var
  1724. L, H: Integer;
  1725. Normals: TArrayOfFixedPoint;
  1726. P1, P2, E1, E2: TArrayOfFixedPoint;
  1727. V: TFixed;
  1728. P: PFixedPoint;
  1729. begin
  1730. Result := nil;
  1731. V := StrokeWidth shr 1;
  1732. Normals := BuildNormals(Points);
  1733. H := High(Points) - 1;
  1734. while (H >= 0) and (Normals[H].X = 0) and (Normals[H].Y = 0) do Dec(H);
  1735. if H < 0 then Exit;
  1736. L := 0;
  1737. while (Normals[L].X = 0) and (Normals[L].Y = 0) do Inc(L);
  1738. P1 := Grow(Points, Normals, V, JoinStyle, False, MiterLimit);
  1739. P2 := ReversePolygon(Grow(Points, Normals, -V, JoinStyle, False, MiterLimit));
  1740. E1 := BuildLineEnd(Points[0], Normals[L], -V, EndStyle);
  1741. E2 := BuildLineEnd(Points[High(Points)], Normals[H], V, EndStyle);
  1742. SetLength(Result, Length(P1) + Length(P2) + Length(E1) + Length(E2));
  1743. P := @Result[0];
  1744. Move(E1[0], P^, Length(E1) * SizeOf(TFixedPoint)); Inc(P, Length(E1));
  1745. Move(P1[0], P^, Length(P1) * SizeOf(TFixedPoint)); Inc(P, Length(P1));
  1746. Move(E2[0], P^, Length(E2) * SizeOf(TFixedPoint)); Inc(P, Length(E2));
  1747. Move(P2[0], P^, Length(P2) * SizeOf(TFixedPoint));
  1748. end;
  1749. function BuildPolyPolyLine(const Points: TArrayOfArrayOfFixedPoint;
  1750. Closed: Boolean; StrokeWidth: TFixed; JoinStyle: TJoinStyle;
  1751. EndStyle: TEndStyle; MiterLimit: TFixed): TArrayOfArrayOfFixedPoint;
  1752. var
  1753. I: Integer;
  1754. P1, P2: TArrayOfFixedPoint;
  1755. Dst: TArrayOfArrayOfFixedPoint;
  1756. Normals: TArrayOfFixedPoint;
  1757. HalfStrokeWidth: TFixed;
  1758. begin
  1759. if Closed then
  1760. begin
  1761. SetLength(Dst, Length(Points) * 2);
  1762. HalfStrokeWidth := StrokeWidth shr 1;
  1763. for I := 0 to High(Points) do
  1764. begin
  1765. Normals := BuildNormals(Points[I]);
  1766. P1 := Grow(Points[I], Normals, HalfStrokeWidth, JoinStyle, True, MiterLimit);
  1767. P2 := Grow(Points[I], Normals, -HalfStrokeWidth, JoinStyle, True, MiterLimit);
  1768. Dst[I * 2] := P1;
  1769. Dst[I * 2 + 1] := ReversePolygon(P2);
  1770. end;
  1771. end
  1772. else
  1773. begin
  1774. SetLength(Dst, Length(Points));
  1775. for I := 0 to High(Points) do
  1776. Dst[I] := BuildPolyline(Points[I], StrokeWidth, JoinStyle, EndStyle);
  1777. end;
  1778. Result := Dst;
  1779. end;
  1780. function BuildDashedLine(const Points: TArrayOfFloatPoint;
  1781. const DashArray: TArrayOfFloat; DashOffset: TFloat = 0;
  1782. Closed: Boolean = False): TArrayOfArrayOfFloatPoint;
  1783. const
  1784. EPSILON = 1E-4;
  1785. var
  1786. I, J, DashIndex, len1, len2: Integer;
  1787. Offset, Dist, v: TFloat;
  1788. Delta: TFloatPoint;
  1789. procedure AddPoint(X, Y: TFloat);
  1790. var
  1791. K: Integer;
  1792. begin
  1793. K := Length(Result[J]);
  1794. SetLength(Result[J], K + 1);
  1795. Result[J][K].X := X;
  1796. Result[J][K].Y := Y;
  1797. end;
  1798. procedure AddDash(I: Integer);
  1799. begin
  1800. if i = 0 then
  1801. begin
  1802. Delta.X := Points[0].X - Points[High(Points)].X;
  1803. Delta.Y := Points[0].Y - Points[High(Points)].Y;
  1804. end else
  1805. begin
  1806. Delta.X := Points[I].X - Points[I - 1].X;
  1807. Delta.Y := Points[I].Y - Points[I - 1].Y;
  1808. end;
  1809. Dist := GR32_Math.Hypot(Delta.X, Delta.Y);
  1810. Offset := Offset + Dist;
  1811. if (Dist > EPSILON) then
  1812. begin
  1813. Dist := 1 / Dist;
  1814. Delta.X := Delta.X * Dist;
  1815. Delta.Y := Delta.Y * Dist;
  1816. end;
  1817. while Offset > DashOffset do
  1818. begin
  1819. v := Offset - DashOffset;
  1820. AddPoint(Points[I].X - v * Delta.X, Points[I].Y - v * Delta.Y);
  1821. DashIndex := (DashIndex + 1) mod Length(DashArray);
  1822. DashOffset := DashOffset + DashArray[DashIndex];
  1823. if Odd(DashIndex) then
  1824. begin
  1825. Inc(J);
  1826. SetLength(Result, J + 1);
  1827. end;
  1828. end;
  1829. if not Odd(DashIndex) then
  1830. AddPoint(Points[I].X, Points[I].Y);
  1831. end;
  1832. begin
  1833. Result := nil;
  1834. if (Length(Points) = 0) then
  1835. Exit;
  1836. if (Length(DashArray) = 0) then
  1837. begin
  1838. Setlength(Result, 1);
  1839. Result[0] := Points;
  1840. Exit;
  1841. end;
  1842. DashIndex := -1;
  1843. Offset := 0;
  1844. V := 0;
  1845. for I := 0 to High(DashArray) do
  1846. V := V + DashArray[I];
  1847. DashOffset := Wrap(DashOffset, V);
  1848. DashOffset := DashOffset - V;
  1849. while (DashOffset < 0) and (DashIndex < High(DashArray)) do
  1850. begin
  1851. Inc(DashIndex);
  1852. DashOffset := DashOffset + DashArray[DashIndex];
  1853. end;
  1854. J := 0;
  1855. // note to self: second dimension might not be zero by default!
  1856. SetLength(Result, 1, 0);
  1857. if not Odd(DashIndex) then
  1858. AddPoint(Points[0].X, Points[0].Y);
  1859. for I := 1 to High(Points) do
  1860. AddDash(I);
  1861. if Closed then
  1862. begin
  1863. AddDash(0);
  1864. len1 := Length(Result[0]);
  1865. len2 := Length(Result[J]);
  1866. // Only merge if the first and last points are contributing on a dash
  1867. {$IFNDEF FPC}
  1868. if (len1 > 0) and (len2 > 0) and (Result[0][0] = Result[J][len2 - 1]) then
  1869. {$ELSE}
  1870. if (len1 > 0) and (len2 > 0) and (Result[0][0].X = Result[J][len2 - 1].X) and (Result[0][0].Y = Result[J][len2 - 1].Y) then
  1871. {$ENDIF}
  1872. begin
  1873. SetLength(Result[0], len1 + len2 -1);
  1874. Move(Result[0][0], Result[0][len2 - 1], SizeOf(TFloatPoint) * len1);
  1875. Move(Result[J][0], Result[0][0], SizeOf(TFloatPoint) * len2);
  1876. SetLength(Result, J);
  1877. Dec(J);
  1878. end;
  1879. end;
  1880. if (J >= 0) and (Length(Result[J]) = 0) then SetLength(Result, J);
  1881. end;
  1882. function BuildDashedLine(const Points: TArrayOfFixedPoint;
  1883. const DashArray: TArrayOfFixed; DashOffset: TFixed = 0;
  1884. Closed: Boolean = False): TArrayOfArrayOfFixedPoint;
  1885. var
  1886. I, J, DashIndex, Len1, Len2: Integer;
  1887. Offset, Dist, v: TFixed;
  1888. Delta: TFixedPoint;
  1889. procedure AddPoint(X, Y: TFixed);
  1890. var
  1891. K: Integer;
  1892. begin
  1893. K := Length(Result[J]);
  1894. SetLength(Result[J], K + 1);
  1895. Result[J][K].X := X;
  1896. Result[J][K].Y := Y;
  1897. end;
  1898. procedure AddDash(I: Integer);
  1899. begin
  1900. if i = 0 then
  1901. begin
  1902. Delta.X := Points[0].X - Points[High(Points)].X;
  1903. Delta.Y := Points[0].Y - Points[High(Points)].Y;
  1904. end else
  1905. begin
  1906. Delta.X := Points[I].X - Points[I - 1].X;
  1907. Delta.Y := Points[I].Y - Points[I - 1].Y;
  1908. end;
  1909. Dist := GR32_Math.Hypot(Delta.X, Delta.Y);
  1910. Offset := Offset + Dist;
  1911. if (Dist > 0) then
  1912. begin
  1913. Delta.X := FixedDiv(Delta.X, Dist);
  1914. Delta.Y := FixedDiv(Delta.Y, Dist);
  1915. end;
  1916. while Offset > DashOffset do
  1917. begin
  1918. v := Offset - DashOffset;
  1919. AddPoint(Points[I].X - FixedMul(v, Delta.X), Points[I].Y - FixedMul(v,
  1920. Delta.Y));
  1921. DashIndex := (DashIndex + 1) mod Length(DashArray);
  1922. DashOffset := DashOffset + DashArray[DashIndex];
  1923. if Odd(DashIndex) then
  1924. begin
  1925. Inc(J);
  1926. SetLength(Result, J + 1);
  1927. end;
  1928. end;
  1929. if not Odd(DashIndex) then
  1930. AddPoint(Points[I].X, Points[I].Y);
  1931. end;
  1932. begin
  1933. Result := nil;
  1934. if (Length(Points) = 0) then
  1935. Exit;
  1936. if (Length(DashArray) = 0) then
  1937. begin
  1938. Setlength(Result, 1);
  1939. Result[0] := Points;
  1940. Exit;
  1941. end;
  1942. DashIndex := -1;
  1943. Offset := 0;
  1944. V := 0;
  1945. for I := 0 to High(DashArray) do
  1946. V := V + DashArray[I];
  1947. DashOffset := Wrap(DashOffset, V);
  1948. DashOffset := DashOffset - V;
  1949. while DashOffset < 0 do
  1950. begin
  1951. Inc(DashIndex);
  1952. DashOffset := DashOffset + DashArray[DashIndex];
  1953. end;
  1954. J := 0;
  1955. // note to self: second dimension might not be zero by default!
  1956. SetLength(Result, 1, 0);
  1957. if not Odd(DashIndex) then
  1958. AddPoint(Points[0].X, Points[0].Y);
  1959. for I := 1 to High(Points) do
  1960. AddDash(I);
  1961. if Closed then
  1962. begin
  1963. AddDash(0);
  1964. Len1 := Length(Result[0]);
  1965. Len2 := Length(Result[J]);
  1966. if (Len1 > 0) and (Len2 > 0) then
  1967. begin
  1968. SetLength(Result[0], len1 + len2 -1);
  1969. Move(Result[0][0], Result[0][len2 - 1], SizeOf(TFixedPoint) * Len1);
  1970. Move(Result[J][0], Result[0][0], SizeOf(TFixedPoint) * Len2);
  1971. SetLength(Result, J);
  1972. Dec(J);
  1973. end;
  1974. end;
  1975. if (J >= 0) and (Length(Result[J]) = 0) then SetLength(Result, J);
  1976. end;
  1977. function InterpolateX(X: TFloat; const P1, P2: TFloatPoint): TFloatPoint; overload;
  1978. var
  1979. W: Double;
  1980. begin
  1981. W := (X - P1.X) / (P2.X - P1.X);
  1982. Result.X := X;
  1983. Result.Y := P1.Y + W * (P2.Y - P1.Y);
  1984. end;
  1985. function InterpolateY(Y: TFloat; const P1, P2: TFloatPoint): TFloatPoint; overload;
  1986. var
  1987. W: Double;
  1988. begin
  1989. W := (Y - P1.Y) / (P2.Y - P1.Y);
  1990. Result.Y := Y;
  1991. Result.X := P1.X + W * (P2.X - P1.X);
  1992. end;
  1993. function GetCode(const P: TFloatPoint; const R: TFloatRect): Integer; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  1994. begin
  1995. Result := Ord(P.X >= R.Left) or
  1996. (Ord(P.X <= R.Right) shl 1) or
  1997. (Ord(P.Y >= R.Top) shl 2) or
  1998. (Ord(P.Y <= R.Bottom) shl 3);
  1999. end;
  2000. function ClipPolygon(const Points: TArrayOfFloatPoint; const ClipRect: TFloatRect): TArrayOfFloatPoint;
  2001. type
  2002. TInterpolateProc = function(X: TFloat; const P1, P2: TFloatPoint): TFloatPoint;
  2003. const
  2004. SAFEOVERSIZE = 5;
  2005. POPCOUNT: array [0..15] of Integer =
  2006. (0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
  2007. var
  2008. I, J, K, L, N: Integer;
  2009. X, Y, Z, Code, Count: Integer;
  2010. Codes: PByteArray;
  2011. NextIndex: PIntegerArray;
  2012. Temp: PFloatPointArray;
  2013. label
  2014. ExitProc;
  2015. procedure AddPoint(Index: Integer; const P: TFloatPoint);
  2016. begin
  2017. Temp[K] := P;
  2018. Codes[K] := GetCode(P, ClipRect);
  2019. Inc(K);
  2020. Inc(Count);
  2021. end;
  2022. function ClipEdges(Mask: Integer; V: TFloat; Interpolate: TInterpolateProc): Boolean;
  2023. var
  2024. I, NextI, StopIndex: Integer;
  2025. begin
  2026. I := 0;
  2027. while (I < K) and (Codes[I] and Mask = 0) do Inc(I);
  2028. Result := I = K;
  2029. if Result then { all points outside }
  2030. begin
  2031. ClipPolygon := nil;
  2032. Result := True;
  2033. Exit;
  2034. end;
  2035. StopIndex := I;
  2036. repeat
  2037. NextI := NextIndex[I];
  2038. if Codes[NextI] and Mask = 0 then { inside -> outside }
  2039. begin
  2040. NextIndex[I] := K;
  2041. NextIndex[K] := K + 1;
  2042. AddPoint(I, Interpolate(V, Temp[I], Temp[NextI]));
  2043. while Codes[NextI] and Mask = 0 do
  2044. begin
  2045. Dec(Count);
  2046. Codes[NextI] := 0;
  2047. I := NextI;
  2048. NextI := NextIndex[I];
  2049. end;
  2050. { outside -> inside }
  2051. NextIndex[I] := K;
  2052. NextIndex[K] := NextI;
  2053. AddPoint(I, Interpolate(V, Temp[I], Temp[NextI]));
  2054. end;
  2055. I := NextI;
  2056. until I = StopIndex;
  2057. end;
  2058. begin
  2059. N := Length(Points);
  2060. {$IFDEF USESTACKALLOC}
  2061. Codes := StackAlloc(N * SAFEOVERSIZE * SizeOf(Byte));
  2062. {$ELSE}
  2063. GetMem(Codes, N * SAFEOVERSIZE * SizeOf(Byte));
  2064. {$ENDIF}
  2065. X := 15;
  2066. Y := 0;
  2067. for I := 0 to N - 1 do
  2068. begin
  2069. Code := GetCode(Points[I], ClipRect);
  2070. Codes[I] := Code;
  2071. X := X and Code;
  2072. Y := Y or Code;
  2073. end;
  2074. if X = 15 then { all points inside }
  2075. begin
  2076. Result := Points;
  2077. end
  2078. else if Y <> 15 then { all points outside }
  2079. begin
  2080. Result := nil;
  2081. end
  2082. else
  2083. begin
  2084. Count := N;
  2085. Z := Codes[N - 1];
  2086. for I := 0 to N - 1 do
  2087. begin
  2088. Code := Codes[I];
  2089. Inc(Count, POPCOUNT[Z xor Code]);
  2090. Z := Code;
  2091. end;
  2092. {$IFDEF USESTACKALLOC}
  2093. Temp := StackAlloc(Count * SizeOf(TFloatPoint));
  2094. NextIndex := StackAlloc(Count * SizeOf(TFloatPoint));
  2095. {$ELSE}
  2096. GetMem(Temp, Count * SizeOf(TFloatPoint));
  2097. GetMem(NextIndex, Count * SizeOf(TFloatPoint));
  2098. {$ENDIF}
  2099. Move(Points[0], Temp[0], N * SizeOf(TFloatPoint));
  2100. for I := 0 to N - 2 do NextIndex[I] := I + 1;
  2101. NextIndex[N - 1] := 0;
  2102. Count := N;
  2103. K := N;
  2104. if X and 1 = 0 then if ClipEdges(1, ClipRect.Left, InterpolateX) then goto ExitProc;
  2105. if X and 2 = 0 then if ClipEdges(2, ClipRect.Right, InterpolateX) then goto ExitProc;
  2106. if X and 4 = 0 then if ClipEdges(4, ClipRect.Top, InterpolateY) then goto ExitProc;
  2107. if X and 8 = 0 then if ClipEdges(8, ClipRect.Bottom, InterpolateY) then goto ExitProc;
  2108. SetLength(Result, Count);
  2109. { start with first point inside the clipping rectangle }
  2110. I := 0;
  2111. while Codes[I] = 0 do
  2112. I := NextIndex[I];
  2113. J := I;
  2114. L := 0;
  2115. repeat
  2116. Result[L] := Temp[I];
  2117. Inc(L);
  2118. I := NextIndex[I];
  2119. until I = J;
  2120. ExitProc:
  2121. {$IFDEF USESTACKALLOC}
  2122. StackFree(NextIndex);
  2123. StackFree(Temp);
  2124. {$ELSE}
  2125. FreeMem(NextIndex);
  2126. FreeMem(Temp);
  2127. {$ENDIF}
  2128. end;
  2129. {$IFDEF USESTACKALLOC}
  2130. StackFree(Codes);
  2131. {$ELSE}
  2132. FreeMem(Codes);
  2133. {$ENDIF}
  2134. end;
  2135. function InterpolateX(X: TFixed; const P1, P2: TFixedPoint): TFixedPoint; overload;
  2136. var
  2137. W: TFixed;
  2138. begin
  2139. W := FixedDiv(X - P1.X, P2.X - P1.X);
  2140. Result.X := X;
  2141. Result.Y := P1.Y + FixedMul(W, P2.Y - P1.Y);
  2142. end;
  2143. function InterpolateY(Y: TFixed; const P1, P2: TFixedPoint): TFixedPoint; overload;
  2144. var
  2145. W: TFixed;
  2146. begin
  2147. W := FixedDiv(Y - P1.Y, P2.Y - P1.Y);
  2148. Result.Y := Y;
  2149. Result.X := P1.X + FixedMul(W, P2.X - P1.X);
  2150. end;
  2151. function GetCode(const P: TFixedPoint; const R: TFixedRect): Integer; overload; {$IFDEF USEINLINING}inline;{$ENDIF}
  2152. begin
  2153. Result := Ord(P.X >= R.Left) or
  2154. (Ord(P.X <= R.Right) shl 1) or
  2155. (Ord(P.Y >= R.Top) shl 2) or
  2156. (Ord(P.Y <= R.Bottom) shl 3);
  2157. end;
  2158. function ClipPolygon(const Points: TArrayOfFixedPoint; const ClipRect: TFixedRect): TArrayOfFixedPoint;
  2159. type
  2160. TInterpolateProc = function(X: TFixed; const P1, P2: TFixedPoint): TFixedPoint;
  2161. const
  2162. SAFEOVERSIZE = 5;
  2163. POPCOUNT: array [0..15] of Integer =
  2164. (0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
  2165. var
  2166. I, J, K, L, N: Integer;
  2167. X, Y, Z, Code, Count: Integer;
  2168. Codes: PByteArray;
  2169. NextIndex: PIntegerArray;
  2170. Temp: PFixedPointArray;
  2171. label
  2172. ExitProc;
  2173. procedure AddPoint(Index: Integer; const P: TFixedPoint);
  2174. begin
  2175. Temp[K] := P;
  2176. Codes[K] := GetCode(P, ClipRect);
  2177. Inc(K);
  2178. Inc(Count);
  2179. end;
  2180. function ClipEdges(Mask: Integer; V: TFixed; Interpolate: TInterpolateProc): Boolean;
  2181. var
  2182. I, NextI, StopIndex: Integer;
  2183. begin
  2184. I := 0;
  2185. while (I < K) and (Codes[I] and Mask = 0) do Inc(I);
  2186. Result := I = K;
  2187. if Result then { all points outside }
  2188. begin
  2189. ClipPolygon := nil;
  2190. Result := True;
  2191. Exit;
  2192. end;
  2193. StopIndex := I;
  2194. repeat
  2195. NextI := NextIndex[I];
  2196. if Codes[NextI] and Mask = 0 then { inside -> outside }
  2197. begin
  2198. NextIndex[I] := K;
  2199. NextIndex[K] := K + 1;
  2200. AddPoint(I, Interpolate(V, Temp[I], Temp[NextI]));
  2201. while Codes[NextI] and Mask = 0 do
  2202. begin
  2203. Dec(Count);
  2204. Codes[NextI] := 0;
  2205. I := NextI;
  2206. NextI := NextIndex[I];
  2207. end;
  2208. { outside -> inside }
  2209. NextIndex[I] := K;
  2210. NextIndex[K] := NextI;
  2211. AddPoint(I, Interpolate(V, Temp[I], Temp[NextI]));
  2212. end;
  2213. I := NextI;
  2214. until I = StopIndex;
  2215. end;
  2216. begin
  2217. N := Length(Points);
  2218. {$IFDEF USESTACKALLOC}
  2219. Codes := StackAlloc(N * SAFEOVERSIZE * SizeOf(Byte));
  2220. {$ELSE}
  2221. GetMem(Codes, N * SAFEOVERSIZE * SizeOf(Byte));
  2222. {$ENDIF}
  2223. X := 15;
  2224. Y := 0;
  2225. for I := 0 to N - 1 do
  2226. begin
  2227. Code := GetCode(Points[I], ClipRect);
  2228. Codes[I] := Code;
  2229. X := X and Code;
  2230. Y := Y or Code;
  2231. end;
  2232. if X = 15 then { all points inside }
  2233. begin
  2234. Result := Points;
  2235. end
  2236. else if Y <> 15 then { all points outside }
  2237. begin
  2238. Result := nil;
  2239. end
  2240. else
  2241. begin
  2242. Count := N;
  2243. Z := Codes[N - 1];
  2244. for I := 0 to N - 1 do
  2245. begin
  2246. Code := Codes[I];
  2247. Inc(Count, POPCOUNT[Z xor Code]);
  2248. Z := Code;
  2249. end;
  2250. {$IFDEF USESTACKALLOC}
  2251. Temp := StackAlloc(Count * SizeOf(TFixedPoint));
  2252. NextIndex := StackAlloc(Count * SizeOf(TFixedPoint));
  2253. {$ELSE}
  2254. GetMem(Temp, Count * SizeOf(TFixedPoint));
  2255. GetMem(NextIndex, Count * SizeOf(TFixedPoint));
  2256. {$ENDIF}
  2257. Move(Points[0], Temp[0], N * SizeOf(TFixedPoint));
  2258. for I := 0 to N - 2 do NextIndex[I] := I + 1;
  2259. NextIndex[N - 1] := 0;
  2260. Count := N;
  2261. K := N;
  2262. if X and 1 = 0 then if ClipEdges(1, ClipRect.Left, InterpolateX) then goto ExitProc;
  2263. if X and 2 = 0 then if ClipEdges(2, ClipRect.Right, InterpolateX) then goto ExitProc;
  2264. if X and 4 = 0 then if ClipEdges(4, ClipRect.Top, InterpolateY) then goto ExitProc;
  2265. if X and 8 = 0 then if ClipEdges(8, ClipRect.Bottom, InterpolateY) then goto ExitProc;
  2266. SetLength(Result, Count);
  2267. { start with first point inside the clipping rectangle }
  2268. I := 0;
  2269. while Codes[I] = 0 do
  2270. I := NextIndex[I];
  2271. J := I;
  2272. L := 0;
  2273. repeat
  2274. Result[L] := Temp[I];
  2275. Inc(L);
  2276. I := NextIndex[I];
  2277. until I = J;
  2278. ExitProc:
  2279. {$IFDEF USESTACKALLOC}
  2280. StackFree(NextIndex);
  2281. StackFree(Temp);
  2282. {$ELSE}
  2283. FreeMem(NextIndex);
  2284. FreeMem(Temp);
  2285. {$ENDIF}
  2286. end;
  2287. {$IFDEF USESTACKALLOC}
  2288. StackFree(Codes);
  2289. {$ELSE}
  2290. FreeMem(Codes);
  2291. {$ENDIF}
  2292. end;
  2293. function CatPolyPolygon(const P1, P2: TArrayOfArrayOfFloatPoint): TArrayOfArrayOfFloatPoint;
  2294. var
  2295. L1, L2: Integer;
  2296. begin
  2297. L1 := Length(P1);
  2298. L2 := Length(P2);
  2299. SetLength(Result, L1 + L2);
  2300. Move(P1[0], Result[0], L1 * SizeOf(TFloatPoint));
  2301. Move(P2[0], Result[L1], L2 * SizeOf(TFloatPoint));
  2302. end;
  2303. function CatPolyPolygon(const P1, P2: TArrayOfArrayOfFixedPoint): TArrayOfArrayOfFixedPoint; overload;
  2304. var
  2305. L1, L2: Integer;
  2306. begin
  2307. L1 := Length(P1);
  2308. L2 := Length(P2);
  2309. SetLength(Result, L1 + L2);
  2310. Move(P1[0], Result[0], L1 * SizeOf(TFixedPoint));
  2311. Move(P2[0], Result[L1], L2 * SizeOf(TFixedPoint));
  2312. end;
  2313. function PolygonBounds(const Points: TArrayOfFloatPoint): TFloatRect;
  2314. var
  2315. I: Integer;
  2316. begin
  2317. Assert(Length(Points) > 0);
  2318. Result.Left := Points[0].X;
  2319. Result.Top := Points[0].Y;
  2320. Result.Right := Points[0].X;
  2321. Result.Bottom := Points[0].Y;
  2322. for I := 1 to High(Points) do
  2323. begin
  2324. Result.Left := Min(Result.Left, Points[I].X);
  2325. Result.Right := Max(Result.Right, Points[I].X);
  2326. Result.Top := Min(Result.Top, Points[I].Y);
  2327. Result.Bottom := Max(Result.Bottom, Points[I].Y);
  2328. end;
  2329. end;
  2330. function PolygonBounds(const Points: TArrayOfFixedPoint): TFixedRect;
  2331. var
  2332. I: Integer;
  2333. begin
  2334. Assert(Length(Points) > 0);
  2335. Result.Left := Points[0].X;
  2336. Result.Top := Points[0].Y;
  2337. Result.Right := Points[0].X;
  2338. Result.Bottom := Points[0].Y;
  2339. for I := 1 to High(Points) do
  2340. begin
  2341. Result.Left := Min(Result.Left, Points[I].X);
  2342. Result.Right := Max(Result.Right, Points[I].X);
  2343. Result.Top := Min(Result.Top, Points[I].Y);
  2344. Result.Bottom := Max(Result.Bottom, Points[I].Y);
  2345. end;
  2346. end;
  2347. function PolypolygonBounds(const Points: TArrayOfArrayOfFloatPoint): TFloatRect;
  2348. var
  2349. i: Integer;
  2350. R: TFloatRect;
  2351. AnyValid: boolean;
  2352. begin
  2353. Assert(Length(Points) > 0);
  2354. AnyValid := False;
  2355. for i := 0 to High(Points) do
  2356. begin
  2357. if (Length(Points[i]) = 0) then
  2358. continue;
  2359. if (not AnyValid) then
  2360. begin
  2361. Result := PolygonBounds(Points[i]);
  2362. AnyValid := True;
  2363. end else
  2364. begin
  2365. R := PolygonBounds(Points[i]);
  2366. if (Result.Left > R.Left) then
  2367. Result.Left := R.Left;
  2368. if (Result.Right < R.Right) then
  2369. Result.Right := R.Right;
  2370. if (Result.Top > R.Top) then
  2371. Result.Top := R.Top;
  2372. if (Result.Bottom < R.Bottom) then
  2373. Result.Bottom := R.Bottom;
  2374. end;
  2375. end;
  2376. Assert(AnyValid);
  2377. end;
  2378. function PolypolygonBounds(const Points: TArrayOfArrayOfFixedPoint): TFixedRect;
  2379. var
  2380. i: Integer;
  2381. R: TFixedRect;
  2382. AnyValid: boolean;
  2383. begin
  2384. Assert(Length(Points) > 0);
  2385. AnyValid := False;
  2386. for i := 0 to High(Points) do
  2387. begin
  2388. if (Length(Points[i]) = 0) then
  2389. continue;
  2390. if (not AnyValid) then
  2391. begin
  2392. Result := PolygonBounds(Points[i]);
  2393. AnyValid := True;
  2394. end else
  2395. begin
  2396. R := PolygonBounds(Points[i]);
  2397. if (Result.Left > R.Left) then
  2398. Result.Left := R.Left;
  2399. if (Result.Right < R.Right) then
  2400. Result.Right := R.Right;
  2401. if (Result.Top > R.Top) then
  2402. Result.Top := R.Top;
  2403. if (Result.Bottom < R.Bottom) then
  2404. Result.Bottom := R.Bottom;
  2405. end;
  2406. end;
  2407. Assert(AnyValid);
  2408. end;
  2409. // Scales to a polygon (TArrayOfFloatPoint)
  2410. function ScalePolygon(const Points: TArrayOfFloatPoint; ScaleX, ScaleY: TFloat): TArrayOfFloatPoint;
  2411. var
  2412. I, L: Integer;
  2413. begin
  2414. L := Length(Points);
  2415. SetLength(Result, L);
  2416. for I := 0 to L - 1 do
  2417. begin
  2418. Result[I].X := Points[I].X * ScaleX;
  2419. Result[I].Y := Points[I].Y * ScaleY;
  2420. end;
  2421. end;
  2422. // Scales to a polygon (TArrayOfFixedPoint)
  2423. function ScalePolygon(const Points: TArrayOfFixedPoint; ScaleX, ScaleY: TFixed): TArrayOfFixedPoint;
  2424. var
  2425. I, L: Integer;
  2426. begin
  2427. L := Length(Points);
  2428. SetLength(Result, L);
  2429. for I := 0 to L - 1 do
  2430. begin
  2431. Result[I].X := FixedMul(Points[I].X, ScaleX);
  2432. Result[I].Y := FixedMul(Points[I].Y, ScaleY);
  2433. end;
  2434. end;
  2435. // Scales all sub polygons in a complex polygon (TArrayOfArrayOfFloatPoint)
  2436. function ScalePolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  2437. ScaleX, ScaleY: TFloat): TArrayOfArrayOfFloatPoint;
  2438. var
  2439. I, L: Integer;
  2440. begin
  2441. L := Length(Points);
  2442. SetLength(Result, L);
  2443. for I := 0 to L - 1 do
  2444. Result[I] := ScalePolygon(Points[I], ScaleX, ScaleY);
  2445. end;
  2446. // Scales all sub polygons in a complex polygon (TArrayOfArrayOfFixedPoint)
  2447. function ScalePolyPolygon(const Points: TArrayOfArrayOfFixedPoint;
  2448. ScaleX, ScaleY: TFixed): TArrayOfArrayOfFixedPoint;
  2449. var
  2450. I, L: Integer;
  2451. begin
  2452. L := Length(Points);
  2453. SetLength(Result, L);
  2454. for I := 0 to L - 1 do
  2455. Result[I] := ScalePolygon(Points[I], ScaleX, ScaleY);
  2456. end;
  2457. // Scales a polygon (TArrayOfFloatPoint)
  2458. procedure ScalePolygonInplace(const Points: TArrayOfFloatPoint; ScaleX, ScaleY: TFloat);
  2459. var
  2460. I: Integer;
  2461. begin
  2462. for I := 0 to Length(Points) - 1 do
  2463. begin
  2464. Points[I].X := Points[I].X * ScaleX;
  2465. Points[I].Y := Points[I].Y * ScaleY;
  2466. end;
  2467. end;
  2468. // Scales a polygon (TArrayOfFixedPoint)
  2469. procedure ScalePolygonInplace(const Points: TArrayOfFixedPoint; ScaleX, ScaleY: TFixed);
  2470. var
  2471. I: Integer;
  2472. begin
  2473. for I := 0 to Length(Points) - 1 do
  2474. begin
  2475. Points[I].X := FixedMul(Points[I].X, ScaleX);
  2476. Points[I].Y := FixedMul(Points[I].Y, ScaleY);
  2477. end;
  2478. end;
  2479. // Scales all sub polygons in a complex polygon (TArrayOfArrayOfFloatPoint)
  2480. procedure ScalePolyPolygonInplace(const Points: TArrayOfArrayOfFloatPoint;
  2481. ScaleX, ScaleY: TFloat);
  2482. var
  2483. I: Integer;
  2484. begin
  2485. for I := 0 to Length(Points) - 1 do
  2486. ScalePolygonInplace(Points[I], ScaleX, ScaleY);
  2487. end;
  2488. // Scales all sub polygons in a complex polygon (TArrayOfArrayOfFixedPoint)
  2489. procedure ScalePolyPolygonInplace(const Points: TArrayOfArrayOfFixedPoint;
  2490. ScaleX, ScaleY: TFixed);
  2491. var
  2492. I: Integer;
  2493. begin
  2494. for I := 0 to Length(Points) - 1 do
  2495. ScalePolygonInplace(Points[I], ScaleX, ScaleY);
  2496. end;
  2497. // Translates a polygon (TArrayOfFloatPoint)
  2498. function TranslatePolygon(const Points: TArrayOfFloatPoint;
  2499. OffsetX, OffsetY: TFloat): TArrayOfFloatPoint;
  2500. var
  2501. I, Len: Integer;
  2502. begin
  2503. Len := Length(Points);
  2504. SetLength(Result, Len);
  2505. for I := 0 to Len - 1 do
  2506. begin
  2507. Result[I].X := Points[I].X + OffsetX;
  2508. Result[I].Y := Points[I].Y + OffsetY;
  2509. end;
  2510. end;
  2511. // Translates a polygon (TArrayOfFixedPoint)
  2512. function TranslatePolygon(const Points: TArrayOfFixedPoint;
  2513. OffsetX, OffsetY: TFixed): TArrayOfFixedPoint;
  2514. var
  2515. I, Len: Integer;
  2516. begin
  2517. Len := Length(Points);
  2518. SetLength(Result, Len);
  2519. for I := 0 to Len - 1 do
  2520. begin
  2521. Result[I].X := Points[I].X + OffsetX;
  2522. Result[I].Y := Points[I].Y + OffsetY;
  2523. end;
  2524. end;
  2525. // Translates all sub polygons in a complex polygon (TArrayOfArrayOfFloatPoint)
  2526. function TranslatePolyPolygon(const Points: TArrayOfArrayOfFloatPoint; OffsetX,
  2527. OffsetY: TFloat): TArrayOfArrayOfFloatPoint;
  2528. var
  2529. I, L: Integer;
  2530. begin
  2531. L := Length(Points);
  2532. SetLength(Result, L);
  2533. for I := 0 to L - 1 do
  2534. Result[I] := TranslatePolygon(Points[I], OffsetX, OffsetY);
  2535. end;
  2536. // Translates all sub polygons in a complex polygon (TArrayOfArrayOfFixedPoint)
  2537. function TranslatePolyPolygon(const Points: TArrayOfArrayOfFixedPoint;
  2538. OffsetX, OffsetY: TFixed): TArrayOfArrayOfFixedPoint;
  2539. var
  2540. I, L: Integer;
  2541. begin
  2542. L := Length(Points);
  2543. SetLength(Result, L);
  2544. for I := 0 to L - 1 do
  2545. Result[I] := TranslatePolygon(Points[I], OffsetX, OffsetY);
  2546. end;
  2547. procedure TranslatePolygonInplace(const Points: TArrayOfFloatPoint;
  2548. OffsetX, OffsetY: TFloat);
  2549. var
  2550. I: Integer;
  2551. begin
  2552. for I := 0 to Length(Points) - 1 do
  2553. begin
  2554. Points[I].X := Points[I].X + OffsetX;
  2555. Points[I].Y := Points[I].Y + OffsetY;
  2556. end;
  2557. end;
  2558. procedure TranslatePolygonInplace(const Points: TArrayOfFixedPoint;
  2559. OffsetX, OffsetY: TFixed);
  2560. var
  2561. I: Integer;
  2562. begin
  2563. for I := 0 to Length(Points) - 1 do
  2564. begin
  2565. Points[I].X := Points[I].X + OffsetX;
  2566. Points[I].Y := Points[I].Y + OffsetY;
  2567. end;
  2568. end;
  2569. // Translates all sub polygons in a complex polygon (TArrayOfArrayOfFloatPoint)
  2570. procedure TranslatePolyPolygonInplace(const Points: TArrayOfArrayOfFloatPoint; OffsetX,
  2571. OffsetY: TFloat);
  2572. var
  2573. I: Integer;
  2574. begin
  2575. for I := 0 to Length(Points) - 1 do
  2576. TranslatePolygonInplace(Points[I], OffsetX, OffsetY);
  2577. end;
  2578. // Translates all sub polygons in a complex polygon (TArrayOfArrayOfFixedPoint)
  2579. procedure TranslatePolyPolygonInplace(const Points: TArrayOfArrayOfFixedPoint;
  2580. OffsetX, OffsetY: TFixed);
  2581. var
  2582. I: Integer;
  2583. begin
  2584. for I := 0 to Length(Points) - 1 do
  2585. TranslatePolygonInplace(Points[I], OffsetX, OffsetY);
  2586. end;
  2587. // Applies transformation to a polygon (TArrayOfFloatPoint)
  2588. function TransformPolygon(const Points: TArrayOfFloatPoint;
  2589. Transformation: TTransformation): TArrayOfFloatPoint;
  2590. var
  2591. I: Integer;
  2592. begin
  2593. SetLength(Result, Length(Points));
  2594. for I := 0 to High(Result) do
  2595. TTransformationAccess(Transformation).TransformFloat(Points[I].X,
  2596. Points[I].Y, Result[I].X, Result[I].Y);
  2597. end;
  2598. // Applies transformation to a polygon (TArrayOfFixedPoint)
  2599. function TransformPolygon(const Points: TArrayOfFixedPoint;
  2600. Transformation: TTransformation): TArrayOfFixedPoint;
  2601. var
  2602. I: Integer;
  2603. begin
  2604. SetLength(Result, Length(Points));
  2605. for I := 0 to High(Result) do
  2606. TTransformationAccess(Transformation).TransformFixed(Points[I].X,
  2607. Points[I].Y, Result[I].X, Result[I].Y);
  2608. end;
  2609. // Applies transformation to all sub polygons in a complex polygon
  2610. function TransformPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  2611. Transformation: TTransformation): TArrayOfArrayOfFloatPoint;
  2612. var
  2613. I: Integer;
  2614. begin
  2615. SetLength(Result, Length(Points));
  2616. TTransformationAccess(Transformation).PrepareTransform;
  2617. for I := 0 to High(Result) do
  2618. Result[I] := TransformPolygon(Points[I], Transformation);
  2619. end;
  2620. // Applies transformation to all sub polygons in a complex polygon
  2621. function TransformPolyPolygon(const Points: TArrayOfArrayOfFixedPoint;
  2622. Transformation: TTransformation): TArrayOfArrayOfFixedPoint;
  2623. var
  2624. I: Integer;
  2625. begin
  2626. SetLength(Result, Length(Points));
  2627. TTransformationAccess(Transformation).PrepareTransform;
  2628. for I := 0 to High(Result) do
  2629. Result[I] := TransformPolygon(Points[I], Transformation);
  2630. end;
  2631. function BuildPolygonF(const Data: array of TFloat): TArrayOfFloatPoint;
  2632. var
  2633. Index, Count: Integer;
  2634. begin
  2635. Count := Length(Data) div 2;
  2636. SetLength(Result, Count);
  2637. if Count = 0 then Exit;
  2638. for Index := 0 to Count - 1 do
  2639. begin
  2640. Result[Index].X := Data[Index * 2];
  2641. Result[Index].Y := Data[Index * 2 + 1];
  2642. end;
  2643. end;
  2644. function BuildPolygonX(const Data: array of TFixed): TArrayOfFixedPoint;
  2645. var
  2646. Index, Count: Integer;
  2647. begin
  2648. Count := Length(Data) div 2;
  2649. SetLength(Result, Count);
  2650. if Count = 0 then Exit;
  2651. for Index := 0 to Count - 1 do
  2652. begin
  2653. Result[Index].X := Data[Index * 2];
  2654. Result[Index].Y := Data[Index * 2 + 1];
  2655. end;
  2656. end;
  2657. // Copy data from Polygon to simple PolyPolygon (using 1 sub polygon only)
  2658. function PolyPolygon(const Points: TArrayOfFloatPoint)
  2659. : TArrayOfArrayOfFloatPoint;
  2660. begin
  2661. SetLength(Result, 1);
  2662. Result[0] := Points;
  2663. end;
  2664. function PolyPolygon(const Points: TArrayOfFixedPoint)
  2665. : TArrayOfArrayOfFixedPoint;
  2666. begin
  2667. SetLength(Result, 1);
  2668. Result[0] := Points;
  2669. end;
  2670. function PointToFloatPoint(const Points: TArrayOfPoint): TArrayOfFloatPoint;
  2671. var
  2672. Index: Integer;
  2673. begin
  2674. if Length(Points) > 0 then
  2675. begin
  2676. SetLength(Result, Length(Points));
  2677. for Index := 0 to Length(Points) - 1 do
  2678. begin
  2679. Result[Index].X := Points[Index].X;
  2680. Result[Index].Y := Points[Index].Y;
  2681. end;
  2682. end;
  2683. end;
  2684. function PointToFloatPoint(const Points: TArrayOfArrayOfPoint): TArrayOfArrayOfFloatPoint;
  2685. var
  2686. Index, PointIndex: Integer;
  2687. begin
  2688. if Length(Points) > 0 then
  2689. begin
  2690. SetLength(Result, Length(Points));
  2691. for Index := 0 to Length(Points) - 1 do
  2692. begin
  2693. SetLength(Result[Index], Length(Points[Index]));
  2694. for PointIndex := 0 to Length(Points[Index]) - 1 do
  2695. begin
  2696. Result[Index, PointIndex].X := Points[Index, PointIndex].X;
  2697. Result[Index, PointIndex].Y := Points[Index, PointIndex].Y;
  2698. end;
  2699. end;
  2700. end;
  2701. end;
  2702. function PointToFixedPoint(const Points: TArrayOfPoint): TArrayOfFixedPoint;
  2703. var
  2704. Index: Integer;
  2705. begin
  2706. if Length(Points) > 0 then
  2707. begin
  2708. SetLength(Result, Length(Points));
  2709. for Index := 0 to Length(Points) - 1 do
  2710. begin
  2711. Result[Index].X := Fixed(Points[Index].X);
  2712. Result[Index].Y := Fixed(Points[Index].Y);
  2713. end;
  2714. end;
  2715. end;
  2716. function PointToFixedPoint(const Points: TArrayOfArrayOfPoint): TArrayOfArrayOfFixedPoint;
  2717. var
  2718. Index, PointIndex: Integer;
  2719. begin
  2720. if Length(Points) > 0 then
  2721. begin
  2722. SetLength(Result, Length(Points));
  2723. for Index := 0 to Length(Points) - 1 do
  2724. begin
  2725. SetLength(Result[Index], Length(Points[Index]));
  2726. for PointIndex := 0 to Length(Points[Index]) - 1 do
  2727. begin
  2728. Result[Index, PointIndex].X := Fixed(Points[Index, PointIndex].X);
  2729. Result[Index, PointIndex].Y := Fixed(Points[Index, PointIndex].Y);
  2730. end;
  2731. end;
  2732. end;
  2733. end;
  2734. end.