Clipper.Core.pas 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070
  1. unit Clipper.Core;
  2. (*******************************************************************************
  3. * Author : Angus Johnson *
  4. * Date : 2 November 2022 *
  5. * Copyright : Angus Johnson 2010-2022 *
  6. * Purpose : Core Clipper Library module *
  7. * Contains structures and functions used throughout the library *
  8. * License : http://www.boost.org/LICENSE_1_0.txt *
  9. *******************************************************************************)
  10. {$I Clipper.inc}
  11. interface
  12. uses
  13. SysUtils, Math;
  14. type
  15. PPoint64 = ^TPoint64;
  16. TPoint64 = record
  17. X, Y: Int64;
  18. {$IFDEF USINGZ}
  19. Z: Int64;
  20. {$ENDIF}
  21. end;
  22. PPointD = ^TPointD;
  23. TPointD = record
  24. X, Y: double;
  25. {$IFDEF USINGZ}
  26. Z: Int64;
  27. {$ENDIF}
  28. end;
  29. // Path: a simple data structure representing a series of vertices, whether
  30. // open (poly-line) or closed (polygon). Paths may be simple or complex (self
  31. // intersecting). For simple polygons, consisting of a single non-intersecting
  32. // path, path orientation is unimportant. However, for complex polygons and
  33. // for overlapping polygons, various 'filling rules' define which regions will
  34. // be inside (filled) and which will be outside (unfilled).
  35. TPath64 = array of TPoint64;
  36. TPaths64 = array of TPath64;
  37. TArrayOfPaths = array of TPaths64;
  38. TPathD = array of TPointD;
  39. TPathsD = array of TPathD;
  40. TArrayOfPathsD = array of TPathsD;
  41. // The most commonly used filling rules for polygons are EvenOdd and NonZero.
  42. // https://en.wikipedia.org/wiki/Even-odd_rule
  43. // https://en.wikipedia.org/wiki/Nonzero-rule
  44. TFillRule = (frEvenOdd, frNonZero, frPositive, frNegative);
  45. TArrayOfBoolean = array of Boolean;
  46. TArrayOfInteger = array of Integer;
  47. TArrayOfDouble = array of double;
  48. TRect64 = {$IFDEF RECORD_METHODS}record{$ELSE}object{$ENDIF}
  49. private
  50. function GetWidth: Int64; {$IFDEF INLINING} inline; {$ENDIF}
  51. function GetHeight: Int64; {$IFDEF INLINING} inline; {$ENDIF}
  52. function GetIsEmpty: Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  53. function GetMidPoint: TPoint64; {$IFDEF INLINING} inline; {$ENDIF}
  54. public
  55. Left : Int64;
  56. Top : Int64;
  57. Right : Int64;
  58. Bottom : Int64;
  59. function Contains(const pt: TPoint64; inclusive: Boolean = false): Boolean; overload;
  60. function Contains(const rec: TRect64): Boolean; overload;
  61. function Intersect(const rec: TRect64): TRect64;
  62. function Intersects(const rec: TRect64): Boolean;
  63. function AsPath: TPath64;
  64. property Width: Int64 read GetWidth;
  65. property Height: Int64 read GetHeight;
  66. property IsEmpty: Boolean read GetIsEmpty;
  67. property MidPoint: TPoint64 read GetMidPoint;
  68. end;
  69. TRectD = {$ifdef RECORD_METHODS}record{$else}object{$endif}
  70. private
  71. function GetWidth: double; {$IFDEF INLINING} inline; {$ENDIF}
  72. function GetHeight: double; {$IFDEF INLINING} inline; {$ENDIF}
  73. function GetIsEmpty: Boolean; {$IFDEF INLINING} inline; {$ENDIF}
  74. function GetMidPoint: TPointD; {$IFDEF INLINING} inline; {$ENDIF}
  75. public
  76. Left : double;
  77. Top : double;
  78. Right : double;
  79. Bottom : double;
  80. function Contains(const pt: TPointD): Boolean; overload;
  81. function Contains(const rec: TRectD): Boolean; overload;
  82. function Intersects(const rec: TRectD): Boolean;
  83. function AsPath: TPathD;
  84. property Width: double read GetWidth;
  85. property Height: double read GetHeight;
  86. property IsEmpty: Boolean read GetIsEmpty;
  87. property MidPoint: TPointD read GetMidPoint;
  88. end;
  89. TClipType = (ctNone, ctIntersection, ctUnion, ctDifference, ctXor);
  90. TPointInPolygonResult = (pipInside, pipOutside, pipOn);
  91. EClipperLibException = class(Exception);
  92. function Area(const path: TPath64): Double; overload;
  93. function Area(const paths: TPaths64): Double; overload;
  94. {$IFDEF INLINING} inline; {$ENDIF}
  95. function Area(const path: TPathD): Double; overload;
  96. function Area(const paths: TPathsD): Double; overload;
  97. {$IFDEF INLINING} inline; {$ENDIF}
  98. function IsPositive(const path: TPath64): Boolean; overload;
  99. {$IFDEF INLINING} inline; {$ENDIF}
  100. function IsPositive(const path: TPathD): Boolean; overload;
  101. {$IFDEF INLINING} inline; {$ENDIF}
  102. function CrossProduct(const pt1, pt2, pt3: TPoint64): double; overload;
  103. {$IFDEF INLINING} inline; {$ENDIF}
  104. function CrossProduct(const pt1, pt2, pt3: TPointD): double; overload;
  105. {$IFDEF INLINING} inline; {$ENDIF}
  106. function CrossProduct(const vec1, vec2: TPointD): double; overload;
  107. {$IFDEF INLINING} inline; {$ENDIF}
  108. function CrossProduct(vec1x, vec1y, vec2x, vec2y: double): double; overload;
  109. {$IFDEF INLINING} inline; {$ENDIF}
  110. function DotProduct(const pt1, pt2, pt3: TPoint64): double;
  111. {$IFDEF INLINING} inline; {$ENDIF}
  112. function DistanceSqr(const pt1, pt2: TPoint64): double; overload;
  113. {$IFDEF INLINING} inline; {$ENDIF}
  114. function DistanceSqr(const pt1, pt2: TPointD): double; overload;
  115. {$IFDEF INLINING} inline; {$ENDIF}
  116. function DistanceFromLineSqrd(const pt, linePt1, linePt2: TPoint64): double; overload;
  117. function DistanceFromLineSqrd(const pt, linePt1, linePt2: TPointD): double; overload;
  118. function SegmentsIntersect(const s1a, s1b, s2a, s2b: TPoint64;
  119. inclusive: Boolean = false): boolean; {$IFDEF INLINING} inline; {$ENDIF}
  120. function PointsEqual(const pt1, pt2: TPoint64): Boolean; overload;
  121. {$IFDEF INLINING} inline; {$ENDIF}
  122. function PointsNearEqual(const pt1, pt2: TPointD): Boolean; overload;
  123. {$IFDEF INLINING} inline; {$ENDIF}
  124. function PointsNearEqual(const pt1, pt2: TPointD; distanceSqrd: double): Boolean; overload;
  125. {$IFDEF INLINING} inline; {$ENDIF}
  126. {$IFDEF USINGZ}
  127. function Point64(const X, Y: Int64; Z: Int64 = 0): TPoint64; overload;
  128. {$IFDEF INLINING} inline; {$ENDIF}
  129. function Point64(const X, Y: Double; Z: Int64 = 0): TPoint64; overload;
  130. {$IFDEF INLINING} inline; {$ENDIF}
  131. function PointD(const X, Y: Double; Z: Int64 = 0): TPointD; overload;
  132. {$IFDEF INLINING} inline; {$ENDIF}
  133. {$ELSE}
  134. function Point64(const X, Y: Int64): TPoint64; overload; {$IFDEF INLINING} inline; {$ENDIF}
  135. function Point64(const X, Y: Double): TPoint64; overload; {$IFDEF INLINING} inline; {$ENDIF}
  136. function PointD(const X, Y: Double): TPointD; overload; {$IFDEF INLINING} inline; {$ENDIF}
  137. {$ENDIF}
  138. function Negate(const pt: TPointD): TPointD; {$IFDEF INLINING} inline; {$ENDIF}
  139. function Point64(const pt: TPointD): TPoint64; overload; {$IFDEF INLINING} inline; {$ENDIF}
  140. function PointD(const pt: TPoint64): TPointD; overload;
  141. {$IFDEF INLINING} inline; {$ENDIF}
  142. function Rect64(const left, top, right, bottom: Int64): TRect64; overload;
  143. {$IFDEF INLINING} inline; {$ENDIF}
  144. function Rect64(const recD: TRectD): TRect64; overload;
  145. {$IFDEF INLINING} inline; {$ENDIF}
  146. function RectD(const left, top, right, bottom: double): TRectD; overload;
  147. {$IFDEF INLINING} inline; {$ENDIF}
  148. function RectD(const rec64: TRect64): TRectD; overload;
  149. {$IFDEF INLINING} inline; {$ENDIF}
  150. function GetBounds(const paths: TArrayOfPaths): TRect64; overload;
  151. function GetBounds(const paths: TPaths64): TRect64; overload;
  152. function GetBounds(const paths: TPathsD): TRectD; overload;
  153. function GetBounds(const path: TPath64): TRect64; overload;
  154. function GetBounds(const path: TPathD): TRectD; overload;
  155. function TranslatePoint(const pt: TPoint64; dx, dy: Int64): TPoint64; overload;
  156. function TranslatePoint(const pt: TPointD; dx, dy: double): TPointD; overload;
  157. procedure RotatePt(var pt: TPointD; const center: TPointD; sinA, cosA: double);
  158. procedure RotatePath(var path: TPathD; const center: TPointD; sinA, cosA: double);
  159. procedure InflateRect(var rec: TRect64; dx, dy: Int64); overload;
  160. {$IFDEF INLINING} inline; {$ENDIF}
  161. procedure InflateRect(var rec: TRectD; dx, dy: double); overload;
  162. {$IFDEF INLINING} inline; {$ENDIF}
  163. function UnionRect(const rec, rec2: TRect64): TRect64; overload;
  164. {$IFDEF INLINING} inline; {$ENDIF}
  165. function UnionRect(const rec, rec2: TRectD): TRectD; overload;
  166. {$IFDEF INLINING} inline; {$ENDIF}
  167. function RotateRect(const rec: TRect64; angleRad: double): TRect64; overload;
  168. function RotateRect(const rec: TRectD; angleRad: double): TRectD; overload;
  169. procedure OffsetRect(var rec: TRect64; dx, dy: Int64); overload;
  170. {$IFDEF INLINING} inline; {$ENDIF}
  171. procedure OffsetRect(var rec: TRectD; dx, dy: double); overload;
  172. {$IFDEF INLINING} inline; {$ENDIF}
  173. function ScaleRect(const rec: TRect64; scale: double): TRect64; overload;
  174. {$IFDEF INLINING} inline; {$ENDIF}
  175. function ScaleRect(const rec: TRectD; scale: double): TRectD; overload;
  176. {$IFDEF INLINING} inline; {$ENDIF}
  177. function ScalePoint(const pt: TPoint64; scale: double): TPointD;
  178. {$IFDEF INLINING} inline; {$ENDIF}
  179. function ScalePath(const path: TPath64; sx, sy: double): TPath64; overload;
  180. function ScalePath(const path: TPathD; sx, sy: double): TPath64; overload;
  181. function ScalePath(const path: TPath64; scale: double): TPath64; overload;
  182. function ScalePath(const path: TPathD; scale: double): TPath64; overload;
  183. function ScalePathD(const path: TPath64; sx, sy: double): TPathD; overload;
  184. function ScalePathD(const path: TPathD; sx, sy: double): TPathD; overload;
  185. function ScalePathD(const path: TPath64; scale: double): TPathD; overload;
  186. function ScalePathD(const path: TPathD; scale: double): TPathD; overload;
  187. function ScalePaths(const paths: TPaths64; sx, sy: double): TPaths64; overload;
  188. function ScalePaths(const paths: TPathsD; sx, sy: double): TPaths64; overload;
  189. function ScalePaths(const paths: TPaths64; scale: double): TPaths64; overload;
  190. function ScalePaths(const paths: TPathsD; scale: double): TPaths64; overload;
  191. function ScalePathsD(const paths: TPaths64; sx, sy: double): TPathsD; overload;
  192. function ScalePathsD(const paths: TPathsD; sx, sy: double): TPathsD; overload;
  193. function ScalePathsD(const paths: TPaths64; scale: double): TPathsD; overload;
  194. function ScalePathsD(const paths: TPathsD; scale: double): TPathsD; overload;
  195. function Path64(const pathD: TPathD): TPath64;
  196. function PathD(const path: TPath64): TPathD;
  197. function Paths64(const pathsD: TPathsD): TPaths64;
  198. function PathsD(const paths: TPaths64): TPathsD;
  199. function StripDuplicates(const path: TPath64; isClosedPath: Boolean = false): TPath64;
  200. function StripNearDuplicates(const path: TPathD;
  201. minLenSqrd: double; isClosedPath: Boolean): TPathD;
  202. function ValueBetween(val, end1, end2: Int64): Boolean;
  203. {$IFDEF INLINING} inline; {$ENDIF}
  204. function ValueEqualOrBetween(val, end1, end2: Int64): Boolean;
  205. {$IFDEF INLINING} inline; {$ENDIF}
  206. function ReversePath(const path: TPath64): TPath64; overload;
  207. {$IFDEF INLINING} inline; {$ENDIF}
  208. function ReversePath(const path: TPathD): TPathD; overload;
  209. function ReversePaths(const paths: TPaths64): TPaths64; overload;
  210. {$IFDEF INLINING} inline; {$ENDIF}
  211. function ReversePaths(const paths: TPathsD): TPathsD; overload;
  212. {$IFDEF INLINING} inline; {$ENDIF}
  213. procedure AppendPoint(var path: TPath64; const pt: TPoint64); overload;
  214. {$IFDEF INLINING} inline; {$ENDIF}
  215. procedure AppendPoint(var path: TPathD; const pt: TPointD); overload;
  216. {$IFDEF INLINING} inline; {$ENDIF}
  217. function AppendPoints(const path, extra: TPath64): TPath64;
  218. {$IFDEF INLINING} inline; {$ENDIF}
  219. procedure AppendPath(var paths: TPaths64; const extra: TPath64); overload;
  220. procedure AppendPath(var paths: TPathsD; const extra: TPathD); overload;
  221. procedure AppendPaths(var paths: TPaths64; const extra: TPaths64); overload;
  222. procedure AppendPaths(var paths: TPathsD; const extra: TPathsD); overload;
  223. function ArrayOfPathsToPaths(const ap: TArrayOfPaths): TPaths64;
  224. function GetIntersectPoint64(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPoint64;
  225. function GetIntersectPointD(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPointD; overload;
  226. function GetIntersectPointD(const ln1a, ln1b, ln2a, ln2b: TPointD): TPointD; overload;
  227. function GetClosestLineEnd(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPoint64; overload;
  228. function GetClosestLineEnd(const ln1a, ln1b, ln2a, ln2b: TPointD): TPointD; overload;
  229. function PointInPolygon(const pt: TPoint64; const polygon: TPath64): TPointInPolygonResult;
  230. function RamerDouglasPeucker(const path: TPath64; epsilon: double): TPath64; overload;
  231. function RamerDouglasPeucker(const paths: TPaths64; epsilon: double): TPaths64; overload;
  232. function RamerDouglasPeucker(const path: TPathD; epsilon: double): TPathD; overload;
  233. function RamerDouglasPeucker(const paths: TPathsD; epsilon: double): TPathsD; overload;
  234. procedure GetSinCos(angle: double; out sinA, cosA: double);
  235. function Ellipse(const rec: TRect64; steps: integer = 0): TPath64; overload;
  236. function Ellipse(const rec: TRectD; steps: integer = 0): TPathD; overload;
  237. procedure CheckPrecisionRange(var precision: integer);
  238. const
  239. MaxInt64 = 9223372036854775807;
  240. invalid64 = MaxInt64;
  241. invalidD = infinity;
  242. NullPointD : TPointD = (X: 0; Y: 0);
  243. NullRect64 : TRect64 = (left: 0; top: 0; right: 0; Bottom: 0);
  244. InvalidPt64 : TPoint64 = (X: invalid64; Y: invalid64);
  245. InvalidPtD : TPointD = (X: invalidD; Y: invalidD);
  246. NullRectD : TRectD = (left: 0; top: 0; right: 0; Bottom: 0);
  247. Tolerance : Double = 1.0E-12;
  248. implementation
  249. resourcestring
  250. rsClipper_PrecisonErr = 'The decimal rounding value is invalid';
  251. //------------------------------------------------------------------------------
  252. // TRect64 methods ...
  253. //------------------------------------------------------------------------------
  254. function TRect64.GetWidth: Int64;
  255. begin
  256. result := right - left;
  257. end;
  258. //------------------------------------------------------------------------------
  259. function TRect64.GetHeight: Int64;
  260. begin
  261. result := bottom - top;
  262. end;
  263. //------------------------------------------------------------------------------
  264. function TRect64.GetIsEmpty: Boolean;
  265. begin
  266. result := (bottom <= top) or (right <= left);
  267. end;
  268. //------------------------------------------------------------------------------
  269. function TRect64.GetMidPoint: TPoint64;
  270. begin
  271. result := Point64((Left + Right) div 2, (Top + Bottom) div 2);
  272. end;
  273. //------------------------------------------------------------------------------
  274. function TRect64.Contains(const pt: TPoint64; inclusive: Boolean = false): Boolean;
  275. begin
  276. if inclusive then
  277. result := (pt.X >= Left) and (pt.X <= Right) and
  278. (pt.Y >= Top) and (pt.Y <= Bottom)
  279. else
  280. result := (pt.X > Left) and (pt.X < Right) and
  281. (pt.Y > Top) and (pt.Y < Bottom);
  282. end;
  283. //------------------------------------------------------------------------------
  284. function TRect64.Contains(const rec: TRect64): Boolean;
  285. begin
  286. result := (rec.Left >= Left) and (rec.Right <= Right) and
  287. (rec.Top >= Top) and (rec.Bottom <= Bottom);
  288. end;
  289. //------------------------------------------------------------------------------
  290. function TRect64.Intersects(const rec: TRect64): Boolean;
  291. begin
  292. Result := (Max(Left, rec.Left) < Min(Right, rec.Right)) and
  293. (Max(Top, rec.Top) < Min(Bottom, rec.Bottom));
  294. end;
  295. //------------------------------------------------------------------------------
  296. function TRect64.Intersect(const rec: TRect64): TRect64;
  297. begin
  298. Result.Left := Max(Left, rec.Left);
  299. Result.Top := Max(Top, rec.Top);
  300. Result.Right := Min(Right, rec.Right);
  301. Result.Bottom := Min(Bottom, rec.Bottom);
  302. if IsEmpty then Result := NullRect64;
  303. end;
  304. //------------------------------------------------------------------------------
  305. function TRect64.AsPath: TPath64;
  306. begin
  307. SetLength(Result, 4);
  308. Result[0] := Point64(Left, Top);
  309. Result[1] := Point64(Right, Top);
  310. Result[2] := Point64(Right, Bottom);
  311. Result[3] := Point64(Left, Bottom);
  312. end;
  313. //------------------------------------------------------------------------------
  314. // TRectD methods ...
  315. //------------------------------------------------------------------------------
  316. function TRectD.GetWidth: double;
  317. begin
  318. result := right - left;
  319. end;
  320. //------------------------------------------------------------------------------
  321. function TRectD.GetHeight: double;
  322. begin
  323. result := bottom - top;
  324. end;
  325. //------------------------------------------------------------------------------
  326. function TRectD.GetIsEmpty: Boolean;
  327. begin
  328. result := (bottom <= top) or (right <= left);
  329. end;
  330. //------------------------------------------------------------------------------
  331. function TRectD.GetMidPoint: TPointD;
  332. begin
  333. result := PointD((Left + Right) *0.5, (Top + Bottom) *0.5);
  334. end;
  335. //------------------------------------------------------------------------------
  336. function TRectD.Contains(const pt: TPointD): Boolean;
  337. begin
  338. result := (pt.X > Left) and (pt.X < Right) and
  339. (pt.Y > Top) and (pt.Y < Bottom);
  340. end;
  341. //------------------------------------------------------------------------------
  342. function TRectD.Contains(const rec: TRectD): Boolean;
  343. begin
  344. result := (rec.Left >= Left) and (rec.Right <= Right) and
  345. (rec.Top >= Top) and (rec.Bottom <= Bottom);
  346. end;
  347. //------------------------------------------------------------------------------
  348. function TRectD.Intersects(const rec: TRectD): Boolean;
  349. begin
  350. Result := (Max(Left, rec.Left) < Min(Right, rec.Right)) and
  351. (Max(Top, rec.Top) < Min(Bottom, rec.Bottom));
  352. end;
  353. //------------------------------------------------------------------------------
  354. function TRectD.AsPath: TPathD;
  355. begin
  356. SetLength(Result, 4);
  357. Result[0] := PointD(Left, Top);
  358. Result[1] := PointD(Right, Top);
  359. Result[2] := PointD(Right, Bottom);
  360. Result[3] := PointD(Left, Bottom);
  361. end;
  362. //------------------------------------------------------------------------------
  363. // Miscellaneous Functions ...
  364. //------------------------------------------------------------------------------
  365. procedure CheckPrecisionRange(var precision: integer);
  366. begin
  367. if (precision < -8) or (precision > 8) then
  368. Raise EClipperLibException(rsClipper_PrecisonErr);
  369. end;
  370. //------------------------------------------------------------------------------
  371. procedure RaiseError(const msg: string); {$IFDEF INLINING} inline; {$ENDIF}
  372. begin
  373. raise EClipperLibException.Create(msg);
  374. end;
  375. //------------------------------------------------------------------------------
  376. function PointsEqual(const pt1, pt2: TPoint64): Boolean;
  377. begin
  378. Result := (pt1.X = pt2.X) and (pt1.Y = pt2.Y);
  379. end;
  380. //------------------------------------------------------------------------------
  381. function PointsNearEqual(const pt1, pt2: TPointD): Boolean;
  382. begin
  383. Result := (Abs(pt1.X - pt2.X) < Tolerance) and
  384. (Abs(pt1.Y - pt2.Y) < Tolerance);
  385. end;
  386. //------------------------------------------------------------------------------
  387. function PointsNearEqual(const pt1, pt2: TPointD; distanceSqrd: double): Boolean;
  388. begin
  389. Result := Sqr(pt1.X - pt2.X) + Sqr(pt1.Y - pt2.Y) < distanceSqrd;
  390. end;
  391. //------------------------------------------------------------------------------
  392. function StripDuplicates(const path: TPath64; isClosedPath: Boolean): TPath64;
  393. var
  394. i,j, len: integer;
  395. begin
  396. len := length(path);
  397. SetLength(Result, len);
  398. if len = 0 then Exit;
  399. Result[0] := path[0];
  400. j := 0;
  401. for i := 1 to len -1 do
  402. if not PointsEqual(Result[j], path[i]) then
  403. begin
  404. inc(j);
  405. Result[j] := path[i];
  406. end;
  407. if isClosedPath and PointsEqual(Result[0], path[j]) then dec(j);
  408. SetLength(Result, j +1);
  409. end;
  410. //------------------------------------------------------------------------------
  411. function StripNearDuplicates(const path: TPathD;
  412. minLenSqrd: double; isClosedPath: Boolean): TPathD;
  413. var
  414. i,j, len: integer;
  415. begin
  416. len := length(path);
  417. SetLength(Result, len);
  418. if len = 0 then Exit;
  419. Result[0] := path[0];
  420. j := 0;
  421. for i := 1 to len -1 do
  422. if not PointsNearEqual(Result[j], path[i], minLenSqrd) then
  423. begin
  424. inc(j);
  425. Result[j] := path[i];
  426. end;
  427. if isClosedPath and
  428. PointsNearEqual(Result[j], Result[0], minLenSqrd) then dec(j);
  429. SetLength(Result, j +1);
  430. end;
  431. //------------------------------------------------------------------------------
  432. function ValueBetween(val, end1, end2: Int64): Boolean;
  433. begin
  434. // nb: accommodates axis aligned between where end1 == end2
  435. Result := ((val <> end1) = (val <> end2)) and
  436. ((val > end1) = (val < end2));
  437. end;
  438. //------------------------------------------------------------------------------
  439. function ValueEqualOrBetween(val, end1, end2: Int64): Boolean;
  440. begin
  441. Result := (val = end1) or (val = end2) or
  442. ((val > end1) = (val < end2));
  443. end;
  444. //------------------------------------------------------------------------------
  445. function ScaleRect(const rec: TRect64; scale: double): TRect64;
  446. begin
  447. Result.Left := Round(rec.Left * scale);
  448. Result.Top := Round(rec.Top * scale);
  449. Result.Right := Round(rec.Right * scale);
  450. Result.Bottom := Round(rec.Bottom * scale);
  451. end;
  452. //------------------------------------------------------------------------------
  453. function ScaleRect(const rec: TRectD; scale: double): TRectD;
  454. begin
  455. Result.Left := rec.Left * scale;
  456. Result.Top := rec.Top * scale;
  457. Result.Right := rec.Right * scale;
  458. Result.Bottom := rec.Bottom * scale;
  459. end;
  460. //------------------------------------------------------------------------------
  461. function ScalePoint(const pt: TPoint64; scale: double): TPointD;
  462. begin
  463. Result.X := pt.X * scale;
  464. Result.Y := pt.Y * scale;
  465. {$IFDEF USINGZ}
  466. Result.Z := pt.Z;
  467. {$ENDIF}
  468. end;
  469. //------------------------------------------------------------------------------
  470. function ScalePath(const path: TPath64; sx, sy: double): TPath64;
  471. var
  472. i,len: integer;
  473. begin
  474. if sx = 0 then sx := 1;
  475. if sy = 0 then sy := 1;
  476. len := length(path);
  477. setlength(result, len);
  478. for i := 0 to len -1 do
  479. begin
  480. result[i].X := Round(path[i].X * sx);
  481. result[i].Y := Round(path[i].Y * sy);
  482. end;
  483. end;
  484. //------------------------------------------------------------------------------
  485. function ScalePath(const path: TPathD; sx, sy: double): TPath64;
  486. var
  487. i,j, len: integer;
  488. begin
  489. if sx = 0 then sx := 1;
  490. if sy = 0 then sy := 1;
  491. len := length(path);
  492. setlength(result, len);
  493. if len = 0 then Exit;
  494. j := 1;
  495. result[0].X := Round(path[0].X * sx);
  496. result[0].Y := Round(path[0].Y * sy);
  497. for i := 1 to len -1 do
  498. begin
  499. result[j].X := Round(path[i].X * sx);
  500. result[j].Y := Round(path[i].Y * sy);
  501. if (result[j].X <> result[j-1].X) or
  502. (result[j].Y <> result[j-1].Y) then inc(j);
  503. end;
  504. setlength(result, j);
  505. end;
  506. //------------------------------------------------------------------------------
  507. function ScalePath(const path: TPath64; scale: double): TPath64;
  508. var
  509. i,j, len: integer;
  510. begin
  511. len := length(path);
  512. setlength(result, len);
  513. if len = 0 then Exit;
  514. j := 1;
  515. result[0].X := Round(path[0].X * scale);
  516. result[0].Y := Round(path[0].Y * scale);
  517. for i := 1 to len -1 do
  518. begin
  519. result[j].X := Round(path[i].X * scale);
  520. result[j].Y := Round(path[i].Y * scale);
  521. if (result[j].X <> result[j-1].X) or
  522. (result[j].Y <> result[j-1].Y) then inc(j);
  523. end;
  524. setlength(result, j);
  525. end;
  526. //------------------------------------------------------------------------------
  527. function ScalePath(const path: TPathD; scale: double): TPath64;
  528. var
  529. i,len: integer;
  530. begin
  531. len := length(path);
  532. setlength(result, len);
  533. for i := 0 to len -1 do
  534. begin
  535. result[i].X := Round(path[i].X * scale);
  536. result[i].Y := Round(path[i].Y * scale);
  537. end;
  538. end;
  539. //------------------------------------------------------------------------------
  540. function ScalePaths(const paths: TPaths64; sx, sy: double): TPaths64;
  541. var
  542. i,len: integer;
  543. begin
  544. if sx = 0 then sx := 1;
  545. if sy = 0 then sy := 1;
  546. len := length(paths);
  547. setlength(result, len);
  548. for i := 0 to len -1 do
  549. result[i] := ScalePath(paths[i], sx, sy);
  550. end;
  551. //------------------------------------------------------------------------------
  552. function ScalePaths(const paths: TPathsD; sx, sy: double): TPaths64;
  553. var
  554. i,len: integer;
  555. begin
  556. if sx = 0 then sx := 1;
  557. if sy = 0 then sy := 1;
  558. len := length(paths);
  559. setlength(result, len);
  560. for i := 0 to len -1 do
  561. result[i] := ScalePath(paths[i], sx, sy);
  562. end;
  563. //------------------------------------------------------------------------------
  564. function ScalePathD(const path: TPath64; sx, sy: double): TPathD;
  565. var
  566. i: integer;
  567. begin
  568. setlength(result, length(path));
  569. for i := 0 to high(path) do
  570. begin
  571. result[i].X := path[i].X * sx;
  572. result[i].Y := path[i].Y * sy;
  573. end;
  574. end;
  575. //------------------------------------------------------------------------------
  576. function ScalePathD(const path: TPathD; sx, sy: double): TPathD;
  577. var
  578. i: integer;
  579. begin
  580. setlength(result, length(path));
  581. for i := 0 to high(path) do
  582. begin
  583. result[i].X := path[i].X * sx;
  584. result[i].Y := path[i].Y * sy;
  585. end;
  586. end;
  587. //------------------------------------------------------------------------------
  588. function ScalePathD(const path: TPath64; scale: double): TPathD;
  589. var
  590. i: integer;
  591. begin
  592. setlength(result, length(path));
  593. for i := 0 to high(path) do
  594. begin
  595. result[i].X := path[i].X * scale;
  596. result[i].Y := path[i].Y * scale;
  597. {$IFDEF USINGZ}
  598. result[i].Z := path[i].Z;
  599. {$ENDIF}
  600. end;
  601. end;
  602. //------------------------------------------------------------------------------
  603. function ScalePathD(const path: TPathD; scale: double): TPathD;
  604. var
  605. i: integer;
  606. begin
  607. setlength(result, length(path));
  608. for i := 0 to high(path) do
  609. begin
  610. result[i].X := path[i].X * scale;
  611. result[i].Y := path[i].Y * scale;
  612. {$IFDEF USINGZ}
  613. result[i].Z := path[i].Z;
  614. {$ENDIF}
  615. end;
  616. end;
  617. //------------------------------------------------------------------------------
  618. function ScalePathsD(const paths: TPaths64; sx, sy: double): TPathsD;
  619. var
  620. i,j: integer;
  621. begin
  622. if sx = 0 then sx := 1;
  623. if sy = 0 then sy := 1;
  624. setlength(result, length(paths));
  625. for i := 0 to high(paths) do
  626. begin
  627. setlength(result[i], length(paths[i]));
  628. for j := 0 to high(paths[i]) do
  629. begin
  630. result[i][j].X := (paths[i][j].X * sx);
  631. result[i][j].Y := (paths[i][j].Y * sy);
  632. end;
  633. end;
  634. end;
  635. //------------------------------------------------------------------------------
  636. function ScalePathsD(const paths: TPathsD; sx, sy: double): TPathsD;
  637. var
  638. i,j: integer;
  639. begin
  640. if sx = 0 then sx := 1;
  641. if sy = 0 then sy := 1;
  642. setlength(result, length(paths));
  643. for i := 0 to high(paths) do
  644. begin
  645. setlength(result[i], length(paths[i]));
  646. for j := 0 to high(paths[i]) do
  647. begin
  648. result[i][j].X := paths[i][j].X * sx;
  649. result[i][j].Y := paths[i][j].Y * sy;
  650. end;
  651. end;
  652. end;
  653. //------------------------------------------------------------------------------
  654. function ScalePaths(const paths: TPaths64; scale: double): TPaths64;
  655. var
  656. i,j: integer;
  657. begin
  658. setlength(result, length(paths));
  659. for i := 0 to high(paths) do
  660. begin
  661. setlength(result[i], length(paths[i]));
  662. for j := 0 to high(paths[i]) do
  663. begin
  664. result[i][j].X := Round(paths[i][j].X * scale);
  665. result[i][j].Y := Round(paths[i][j].Y * scale);
  666. {$IFDEF USINGZ}
  667. result[i][j].Z := paths[i][j].Z;
  668. {$ENDIF}
  669. end;
  670. end;
  671. end;
  672. //------------------------------------------------------------------------------
  673. function ScalePaths(const paths: TPathsD; scale: double): TPaths64;
  674. var
  675. i,j: integer;
  676. begin
  677. setlength(result, length(paths));
  678. for i := 0 to high(paths) do
  679. begin
  680. setlength(result[i], length(paths[i]));
  681. for j := 0 to high(paths[i]) do
  682. begin
  683. result[i][j].X := Round(paths[i][j].X * scale);
  684. result[i][j].Y := Round(paths[i][j].Y * scale);
  685. {$IFDEF USINGZ}
  686. result[i][j].Z := paths[i][j].Z;
  687. {$ENDIF}
  688. end;
  689. end;
  690. end;
  691. //------------------------------------------------------------------------------
  692. function ScalePathsD(const paths: TPaths64; scale: double): TPathsD; overload;
  693. var
  694. i,j: integer;
  695. begin
  696. setlength(result, length(paths));
  697. for i := 0 to high(paths) do
  698. begin
  699. setlength(result[i], length(paths[i]));
  700. for j := 0 to high(paths[i]) do
  701. begin
  702. result[i][j].X := paths[i][j].X * scale;
  703. result[i][j].Y := paths[i][j].Y * scale;
  704. {$IFDEF USINGZ}
  705. result[i][j].Z := paths[i][j].Z;
  706. {$ENDIF}
  707. end;
  708. end;
  709. end;
  710. //------------------------------------------------------------------------------
  711. function ScalePathsD(const paths: TPathsD; scale: double): TPathsD; overload;
  712. var
  713. i,j: integer;
  714. begin
  715. setlength(result, length(paths));
  716. for i := 0 to high(paths) do
  717. begin
  718. setlength(result[i], length(paths[i]));
  719. for j := 0 to high(paths[i]) do
  720. begin
  721. result[i][j].X := paths[i][j].X * scale;
  722. result[i][j].Y := paths[i][j].Y * scale;
  723. {$IFDEF USINGZ}
  724. result[i][j].Z := paths[i][j].Z;
  725. {$ENDIF}
  726. end;
  727. end;
  728. end;
  729. //------------------------------------------------------------------------------
  730. function Path64(const pathD: TPathD): TPath64;
  731. var
  732. i, len: integer;
  733. begin
  734. len := Length(pathD);
  735. setLength(Result, len);
  736. for i := 0 to len -1 do
  737. begin
  738. Result[i].X := Round(pathD[i].X);
  739. Result[i].Y := Round(pathD[i].Y);
  740. end;
  741. end;
  742. //------------------------------------------------------------------------------
  743. function PathD(const path: TPath64): TPathD;
  744. var
  745. i, len: integer;
  746. begin
  747. len := Length(path);
  748. setLength(Result, len);
  749. for i := 0 to len -1 do
  750. begin
  751. Result[i].X := path[i].X;
  752. Result[i].Y := path[i].Y;
  753. end;
  754. end;
  755. //------------------------------------------------------------------------------
  756. function Paths64(const pathsD: TPathsD): TPaths64;
  757. var
  758. i, len: integer;
  759. begin
  760. len := Length(pathsD);
  761. setLength(Result, len);
  762. for i := 0 to len -1 do
  763. Result[i] := Path64(pathsD[i]);
  764. end;
  765. //------------------------------------------------------------------------------
  766. function PathsD(const paths: TPaths64): TPathsD;
  767. var
  768. i, len: integer;
  769. begin
  770. len := Length(paths);
  771. setLength(Result, len);
  772. for i := 0 to len -1 do
  773. Result[i] := PathD(paths[i]);
  774. end;
  775. //------------------------------------------------------------------------------
  776. function ReversePath(const path: TPath64): TPath64;
  777. var
  778. i, highI: Integer;
  779. begin
  780. highI := high(path);
  781. SetLength(Result, highI +1);
  782. for i := 0 to highI do
  783. Result[i] := path[highI - i];
  784. end;
  785. //------------------------------------------------------------------------------
  786. function ReversePath(const path: TPathD): TPathD;
  787. var
  788. i, highI: Integer;
  789. begin
  790. highI := high(path);
  791. SetLength(Result, highI +1);
  792. for i := 0 to highI do
  793. Result[i] := path[highI - i];
  794. end;
  795. //------------------------------------------------------------------------------
  796. function ReversePaths(const paths: TPaths64): TPaths64;
  797. var
  798. i, j, highJ: Integer;
  799. begin
  800. i := length(paths);
  801. SetLength(Result, i);
  802. for i := 0 to i -1 do
  803. begin
  804. highJ := high(paths[i]);
  805. SetLength(Result[i], highJ+1);
  806. for j := 0 to highJ do
  807. Result[i][j] := paths[i][highJ - j];
  808. end;
  809. end;
  810. //------------------------------------------------------------------------------
  811. function ReversePaths(const paths: TPathsD): TPathsD;
  812. var
  813. i, j, highJ: Integer;
  814. begin
  815. i := length(paths);
  816. SetLength(Result, i);
  817. for i := 0 to i -1 do
  818. begin
  819. highJ := high(paths[i]);
  820. SetLength(Result[i], highJ+1);
  821. for j := 0 to highJ do
  822. Result[i][j] := paths[i][highJ - j];
  823. end;
  824. end;
  825. //------------------------------------------------------------------------------
  826. procedure AppendPoint(var path: TPath64; const pt: TPoint64);
  827. var
  828. len: Integer;
  829. begin
  830. len := length(path);
  831. SetLength(path, len +1);
  832. path[len] := pt;
  833. end;
  834. //------------------------------------------------------------------------------
  835. function AppendPoints(const path, extra: TPath64): TPath64;
  836. var
  837. len1, len2: Integer;
  838. begin
  839. len1 := length(path);
  840. len2 := length(extra);
  841. SetLength(Result, len1 + len2);
  842. if len1 > 0 then
  843. Move(path[0], Result[0], len1 * sizeOf(TPoint64));
  844. if len2 > 0 then
  845. Move(extra[0], Result[len1], len2 * sizeOf(TPoint64));
  846. end;
  847. //------------------------------------------------------------------------------
  848. procedure AppendPoint(var path: TPathD; const pt: TPointD);
  849. var
  850. len: Integer;
  851. begin
  852. len := length(path);
  853. SetLength(path, len +1);
  854. path[len] := pt;
  855. end;
  856. //------------------------------------------------------------------------------
  857. procedure AppendPath(var paths: TPaths64; const extra: TPath64);
  858. var
  859. len: Integer;
  860. begin
  861. len := length(paths);
  862. SetLength(paths, len +1);
  863. paths[len] := extra;
  864. end;
  865. //------------------------------------------------------------------------------
  866. procedure AppendPath(var paths: TPathsD; const extra: TPathD);
  867. var
  868. len: Integer;
  869. begin
  870. len := length(paths);
  871. SetLength(paths, len +1);
  872. paths[len] := extra;
  873. end;
  874. //------------------------------------------------------------------------------
  875. procedure AppendPaths(var paths: TPaths64; const extra: TPaths64);
  876. var
  877. i, len1, len2: Integer;
  878. begin
  879. len1 := length(paths);
  880. len2 := length(extra);
  881. SetLength(paths, len1 + len2);
  882. for i := 0 to len2 -1 do
  883. paths[len1 + i] := extra[i];
  884. end;
  885. //------------------------------------------------------------------------------
  886. procedure AppendPaths(var paths: TPathsD; const extra: TPathsD);
  887. var
  888. i, len1, len2: Integer;
  889. begin
  890. len1 := length(paths);
  891. len2 := length(extra);
  892. SetLength(paths, len1 + len2);
  893. for i := 0 to len2 -1 do
  894. paths[len1 + i] := extra[i];
  895. end;
  896. //------------------------------------------------------------------------------
  897. function ArrayOfPathsToPaths(const ap: TArrayOfPaths): TPaths64;
  898. var
  899. i,j,k, len, cnt: integer;
  900. begin
  901. cnt := 0;
  902. len := length(ap);
  903. for i := 0 to len -1 do
  904. inc(cnt, length(ap[i]));
  905. k := 0;
  906. setlength(result, cnt);
  907. for i := 0 to len -1 do
  908. for j := 0 to length(ap[i]) -1 do
  909. begin
  910. result[k] := ap[i][j];
  911. inc(k);
  912. end;
  913. end;
  914. //------------------------------------------------------------------------------
  915. {$IFDEF USINGZ}
  916. function Point64(const X, Y: Int64; Z: Int64): TPoint64;
  917. begin
  918. Result.X := X;
  919. Result.Y := Y;
  920. Result.Z := Z;
  921. end;
  922. //------------------------------------------------------------------------------
  923. function Point64(const X, Y: Double; Z: Int64): TPoint64;
  924. begin
  925. Result.X := Round(X);
  926. Result.Y := Round(Y);
  927. Result.Z := Z;
  928. end;
  929. //------------------------------------------------------------------------------
  930. function PointD(const X, Y: Double; Z: Int64): TPointD;
  931. begin
  932. Result.X := X;
  933. Result.Y := Y;
  934. Result.Z := Z;
  935. end;
  936. //------------------------------------------------------------------------------
  937. function Point64(const pt: TPointD): TPoint64;
  938. begin
  939. Result.X := Round(pt.X);
  940. Result.Y := Round(pt.Y);
  941. Result.Z := pt.Z;
  942. end;
  943. //------------------------------------------------------------------------------
  944. function PointD(const pt: TPoint64): TPointD;
  945. begin
  946. Result.X := pt.X;
  947. Result.Y := pt.Y;
  948. Result.Z := pt.Z;
  949. end;
  950. //------------------------------------------------------------------------------
  951. {$ELSE}
  952. function Point64(const X, Y: Int64): TPoint64;
  953. begin
  954. Result.X := X;
  955. Result.Y := Y;
  956. end;
  957. //------------------------------------------------------------------------------
  958. function Point64(const X, Y: Double): TPoint64;
  959. begin
  960. Result.X := Round(X);
  961. Result.Y := Round(Y);
  962. end;
  963. //------------------------------------------------------------------------------
  964. function PointD(const X, Y: Double): TPointD;
  965. begin
  966. Result.X := X;
  967. Result.Y := Y;
  968. end;
  969. //------------------------------------------------------------------------------
  970. function Point64(const pt: TPointD): TPoint64;
  971. begin
  972. Result.X := Round(pt.X);
  973. Result.Y := Round(pt.Y);
  974. end;
  975. //------------------------------------------------------------------------------
  976. function PointD(const pt: TPoint64): TPointD;
  977. begin
  978. Result.X := pt.X;
  979. Result.Y := pt.Y;
  980. end;
  981. //------------------------------------------------------------------------------
  982. {$ENDIF}
  983. function Negate(const pt: TPointD): TPointD;
  984. begin
  985. Result.X := -pt.X;
  986. Result.Y := -pt.Y;
  987. end;
  988. //------------------------------------------------------------------------------
  989. function Rect64(const left, top, right, bottom: Int64): TRect64;
  990. begin
  991. Result.Left := left;
  992. Result.Top := top;
  993. Result.Right := right;
  994. Result.Bottom := bottom;
  995. end;
  996. //------------------------------------------------------------------------------
  997. function Rect64(const recD: TRectD): TRect64;
  998. begin
  999. Result.Left := Floor(recD.left);
  1000. Result.Top := Floor(recD.top);
  1001. Result.Right := Ceil(recD.right);
  1002. Result.Bottom := Ceil(recD.bottom);
  1003. end;
  1004. //------------------------------------------------------------------------------
  1005. function RectD(const left, top, right, bottom: double): TRectD;
  1006. begin
  1007. Result.Left := left;
  1008. Result.Top := top;
  1009. Result.Right := right;
  1010. Result.Bottom := bottom;
  1011. end;
  1012. //------------------------------------------------------------------------------
  1013. function RectD(const rec64: TRect64): TRectD; overload;
  1014. begin
  1015. Result.Left := rec64.left;
  1016. Result.Top := rec64.top;
  1017. Result.Right := rec64.right;
  1018. Result.Bottom := rec64.bottom;
  1019. end;
  1020. //------------------------------------------------------------------------------
  1021. function GetBounds(const paths: TArrayOfPaths): TRect64; overload;
  1022. var
  1023. i,j,k: Integer;
  1024. p: PPoint64;
  1025. begin
  1026. Result := Rect64(MaxInt64, MaxInt64, -MaxInt64, -MaxInt64);
  1027. for i := 0 to High(paths) do
  1028. for j := 0 to High(paths[i]) do
  1029. if Assigned(paths[i][j]) then
  1030. begin
  1031. p := @paths[i][j][0];
  1032. for k := 0 to High(paths[i][j]) do
  1033. begin
  1034. if p.X < Result.Left then Result.Left := p.X;
  1035. if p.X > Result.Right then Result.Right := p.X;
  1036. if p.Y < Result.Top then Result.Top := p.Y;
  1037. if p.Y > Result.Bottom then Result.Bottom := p.Y;
  1038. inc(p);
  1039. end;
  1040. end;
  1041. if Result.Left > Result.Right then Result := NullRect64;
  1042. end;
  1043. //------------------------------------------------------------------------------
  1044. function GetBounds(const paths: TPaths64): TRect64;
  1045. var
  1046. i,j: Integer;
  1047. p: PPoint64;
  1048. begin
  1049. Result := Rect64(MaxInt64, MaxInt64, -MaxInt64, -MaxInt64);
  1050. for i := 0 to High(paths) do
  1051. if Assigned(paths[i]) then
  1052. begin
  1053. p := @paths[i][0];
  1054. for j := 0 to High(paths[i]) do
  1055. begin
  1056. if p.X < Result.Left then Result.Left := p.X;
  1057. if p.X > Result.Right then Result.Right := p.X;
  1058. if p.Y < Result.Top then Result.Top := p.Y;
  1059. if p.Y > Result.Bottom then Result.Bottom := p.Y;
  1060. inc(p);
  1061. end;
  1062. end;
  1063. if Result.Left > Result.Right then Result := NullRect64;
  1064. end;
  1065. //------------------------------------------------------------------------------
  1066. function GetBounds(const paths: TPathsD): TRectD;
  1067. var
  1068. i,j: Integer;
  1069. p: PPointD;
  1070. begin
  1071. Result := RectD(MaxDouble, MaxDouble, -MaxDouble, -MaxDouble);
  1072. for i := 0 to High(paths) do
  1073. if Assigned(paths[i]) then
  1074. begin
  1075. p := @paths[i][0];
  1076. for j := 0 to High(paths[i]) do
  1077. begin
  1078. if p.X < Result.Left then Result.Left := p.X;
  1079. if p.X > Result.Right then Result.Right := p.X;
  1080. if p.Y < Result.Top then Result.Top := p.Y;
  1081. if p.Y > Result.Bottom then Result.Bottom := p.Y;
  1082. inc(p);
  1083. end;
  1084. end;
  1085. if Result.Left >= Result.Right then Result := nullRectD;
  1086. end;
  1087. //------------------------------------------------------------------------------
  1088. function GetBounds(const path: TPath64): TRect64;
  1089. var
  1090. i, len: Integer;
  1091. p: PPoint64;
  1092. begin
  1093. len := Length(path);
  1094. if len = 0 then
  1095. begin
  1096. Result := NullRect64;
  1097. Exit;
  1098. end;
  1099. Result := Rect64(MaxInt64, MaxInt64, -MaxInt64, -MaxInt64);
  1100. p := @path[0];
  1101. for i := 0 to High(path) do
  1102. begin
  1103. if p.X < Result.Left then Result.Left := p.X;
  1104. if p.X > Result.Right then Result.Right := p.X;
  1105. if p.Y < Result.Top then Result.Top := p.Y;
  1106. if p.Y > Result.Bottom then Result.Bottom := p.Y;
  1107. inc(p);
  1108. end;
  1109. end;
  1110. //------------------------------------------------------------------------------
  1111. function GetBounds(const path: TPathD): TRectD;
  1112. var
  1113. i, len: Integer;
  1114. p: PPointD;
  1115. begin
  1116. len := Length(path);
  1117. if len = 0 then
  1118. begin
  1119. Result := NullRectD;
  1120. Exit;
  1121. end;
  1122. Result := RectD(infinity, infinity, -infinity, -infinity);
  1123. p := @path[0];
  1124. for i := 0 to High(path) do
  1125. begin
  1126. if p.X < Result.Left then Result.Left := p.X;
  1127. if p.X > Result.Right then Result.Right := p.X;
  1128. if p.Y < Result.Top then Result.Top := p.Y;
  1129. if p.Y > Result.Bottom then Result.Bottom := p.Y;
  1130. inc(p);
  1131. end;
  1132. end;
  1133. //------------------------------------------------------------------------------
  1134. function TranslatePoint(const pt: TPoint64; dx, dy: Int64): TPoint64;
  1135. begin
  1136. Result.X := pt.X + dx;
  1137. Result.Y := pt.Y + dy;
  1138. end;
  1139. //------------------------------------------------------------------------------
  1140. function TranslatePoint(const pt: TPointD; dx, dy: double): TPointD;
  1141. begin
  1142. Result.X := pt.X + dx;
  1143. Result.Y := pt.Y + dy;
  1144. end;
  1145. //------------------------------------------------------------------------------
  1146. procedure InflateRect(var rec: TRect64; dx, dy: Int64);
  1147. begin
  1148. dec(rec.Left, dx);
  1149. inc(rec.Right, dx);
  1150. dec(rec.Top, dy);
  1151. inc(rec.Bottom, dy);
  1152. end;
  1153. //------------------------------------------------------------------------------
  1154. procedure InflateRect(var rec: TRectD; dx, dy: double);
  1155. begin
  1156. rec.Left := rec.Left - dx;
  1157. rec.Right := rec.Right + dx;
  1158. rec.Top := rec.Top - dy;
  1159. rec.Bottom := rec.Bottom + dy;
  1160. end;
  1161. //------------------------------------------------------------------------------
  1162. procedure RotatePt(var pt: TPointD; const center: TPointD; sinA, cosA: double);
  1163. var
  1164. tmpX, tmpY: double;
  1165. begin
  1166. tmpX := pt.X-center.X;
  1167. tmpY := pt.Y-center.Y;
  1168. pt.X := tmpX * cosA - tmpY * sinA + center.X;
  1169. pt.Y := tmpX * sinA + tmpY * cosA + center.Y;
  1170. end;
  1171. //------------------------------------------------------------------------------
  1172. procedure RotatePath(var path: TPathD; const center: TPointD; sinA, cosA: double);
  1173. var
  1174. i: integer;
  1175. begin
  1176. for i := 0 to High(path) do
  1177. RotatePt(path[i], center, sinA, cosA);
  1178. end;
  1179. //------------------------------------------------------------------------------
  1180. function RotateRect(const rec: TRectD; angleRad: double): TRectD;
  1181. var
  1182. i: integer;
  1183. sinA, cosA: double;
  1184. cp: TPointD;
  1185. pts: TPathD;
  1186. begin
  1187. setLength(pts, 4);
  1188. sinA := Sin(-angleRad);
  1189. cosA := cos(-angleRad);
  1190. cp.X := (rec.Right + rec.Left) / 2;
  1191. cp.Y := (rec.Bottom + rec.Top) / 2;
  1192. pts[0] := PointD(rec.Left, rec.Top);
  1193. pts[1] := PointD(rec.Right, rec.Top);
  1194. pts[2] := PointD(rec.Left, rec.Bottom);
  1195. pts[3] := PointD(rec.Right, rec.Bottom);
  1196. for i := 0 to 3 do RotatePt(pts[i], cp, sinA, cosA);
  1197. result.Left := pts[0].X;
  1198. result.Right := result.Left;
  1199. result.Top := pts[0].Y;
  1200. result.Bottom := result.Top;
  1201. for i := 1 to 3 do
  1202. begin
  1203. if pts[i].X < result.Left then result.Left := pts[i].X;
  1204. if pts[i].Y < result.Top then result.Top := pts[i].Y;
  1205. if pts[i].X > result.Right then result.Right := pts[i].X;
  1206. if pts[i].Y > result.Bottom then result.Bottom := pts[i].Y;
  1207. end;
  1208. end;
  1209. //------------------------------------------------------------------------------
  1210. function RotateRect(const rec: TRect64; angleRad: double): TRect64;
  1211. var
  1212. recD: TRectD;
  1213. begin
  1214. recD := RectD(rec.Left, rec.Top, rec.Right, rec.Bottom);
  1215. recD := RotateRect(recD, angleRad);
  1216. result.Left := Floor(recD.Left);
  1217. result.Top := Floor(recD.Top);
  1218. result.Right := Ceil(recD.Right);
  1219. result.Bottom := Ceil(recD.Bottom);
  1220. end;
  1221. //------------------------------------------------------------------------------
  1222. procedure OffsetRect(var rec: TRect64; dx, dy: Int64);
  1223. begin
  1224. inc(rec.Left, dx); inc(rec.Top, dy);
  1225. inc(rec.Right, dx); inc(rec.Bottom, dy);
  1226. end;
  1227. //------------------------------------------------------------------------------
  1228. procedure OffsetRect(var rec: TRectD; dx, dy: double);
  1229. begin
  1230. rec.Left := rec.Left + dx;
  1231. rec.Right := rec.Right + dx;
  1232. rec.Top := rec.Top + dy;
  1233. rec.Bottom := rec.Bottom + dy;
  1234. end;
  1235. //------------------------------------------------------------------------------
  1236. function UnionRect(const rec, rec2: TRect64): TRect64;
  1237. begin
  1238. // nb: don't use rec.IsEmpty as this will
  1239. // reject open axis-aligned flat paths
  1240. if (rec.Width <= 0) and (rec.Height <= 0) then result := rec2
  1241. else if (rec2.Width <= 0) and (rec2.Height <= 0) then result := rec
  1242. else
  1243. begin
  1244. result.Left := min(rec.Left, rec2.Left);
  1245. result.Right := max(rec.Right, rec2.Right);
  1246. result.Top := min(rec.Top, rec2.Top);
  1247. result.Bottom := max(rec.Bottom, rec2.Bottom);
  1248. end;
  1249. end;
  1250. //------------------------------------------------------------------------------
  1251. function UnionRect(const rec, rec2: TRectD): TRectD;
  1252. begin
  1253. // nb: don't use rec.IsEmpty as this will
  1254. // reject open axis-aligned flat paths
  1255. if (rec.Width <= 0) and (rec.Height <= 0) then result := rec2
  1256. else if (rec2.Width <= 0) and (rec2.Height <= 0) then result := rec
  1257. else
  1258. begin
  1259. result.Left := min(rec.Left, rec2.Left);
  1260. result.Right := max(rec.Right, rec2.Right);
  1261. result.Top := min(rec.Top, rec2.Top);
  1262. result.Bottom := max(rec.Bottom, rec2.Bottom);
  1263. end;
  1264. end;
  1265. //------------------------------------------------------------------------------
  1266. function Area(const path: TPath64): Double;
  1267. var
  1268. i, highI: Integer;
  1269. d: double;
  1270. p1,p2: PPoint64;
  1271. begin
  1272. // shoelace formula
  1273. Result := 0.0;
  1274. highI := High(path);
  1275. if highI < 2 then Exit;
  1276. p1 := @path[highI];
  1277. p2 := @path[0];
  1278. for i := 0 to highI do
  1279. begin
  1280. d := (p1.Y + p2.Y); // needed for Delphi7
  1281. Result := Result + d * (p1.X - p2.X);
  1282. p1 := p2; inc(p2);
  1283. end;
  1284. Result := Result * 0.5;
  1285. end;
  1286. //------------------------------------------------------------------------------
  1287. function Area(const paths: TPaths64): Double;
  1288. var
  1289. i: integer;
  1290. begin
  1291. Result := 0;
  1292. for i := 0 to High(paths) do
  1293. Result := Result + Area(paths[i]);
  1294. end;
  1295. //------------------------------------------------------------------------------
  1296. function Area(const path: TPathD): Double;
  1297. var
  1298. i, highI: Integer;
  1299. p1,p2: PPointD;
  1300. begin
  1301. // https://en.wikipedia.org/wiki/Shoelace_formula
  1302. Result := 0.0;
  1303. highI := High(path);
  1304. if highI < 2 then Exit;
  1305. p1 := @path[highI];
  1306. p2 := @path[0];
  1307. for i := 0 to highI do
  1308. begin
  1309. Result := Result + (p1.Y + p2.Y) * (p1.X - p2.X);
  1310. p1 := p2; inc(p2);
  1311. end;
  1312. Result := Result * 0.5;
  1313. end;
  1314. //------------------------------------------------------------------------------
  1315. function Area(const paths: TPathsD): Double;
  1316. var
  1317. i: integer;
  1318. begin
  1319. Result := 0;
  1320. for i := 0 to High(paths) do
  1321. Result := Result + Area(paths[i]);
  1322. end;
  1323. //------------------------------------------------------------------------------
  1324. function IsPositive(const path: TPath64): Boolean;
  1325. begin
  1326. Result := (Area(path) >= 0);
  1327. end;
  1328. //------------------------------------------------------------------------------
  1329. function IsPositive(const path: TPathD): Boolean;
  1330. begin
  1331. Result := (Area(path) >= 0);
  1332. end;
  1333. //------------------------------------------------------------------------------
  1334. function CrossProduct(const pt1, pt2, pt3: TPoint64): double;
  1335. begin
  1336. result := CrossProduct(
  1337. pt2.X - pt1.X, pt2.Y - pt1.Y,
  1338. pt3.X - pt2.X, pt3.Y - pt2.Y);
  1339. end;
  1340. //------------------------------------------------------------------------------
  1341. function CrossProduct(const pt1, pt2, pt3: TPointD): double;
  1342. begin
  1343. result := CrossProduct(
  1344. pt2.X - pt1.X, pt2.Y - pt1.Y,
  1345. pt3.X - pt2.X, pt3.Y - pt2.Y);
  1346. end;
  1347. //------------------------------------------------------------------------------
  1348. function CrossProduct(const vec1, vec2: TPointD): double;
  1349. begin
  1350. result := (vec1.X * vec2.Y - vec1.Y * vec2.X);
  1351. end;
  1352. //------------------------------------------------------------------------------
  1353. function CrossProduct(vec1x, vec1y, vec2x, vec2y: double): double;
  1354. begin
  1355. result := (vec1x * vec2y - vec1y * vec2x);
  1356. end;
  1357. //------------------------------------------------------------------------------
  1358. function DotProduct(const pt1, pt2, pt3: TPoint64): double;
  1359. var
  1360. x1,x2,y1,y2: double; // avoids potential int overflow
  1361. begin
  1362. x1 := pt2.X - pt1.X;
  1363. y1 := pt2.Y - pt1.Y;
  1364. x2 := pt3.X - pt2.X;
  1365. y2 := pt3.Y - pt2.Y;
  1366. result := (x1 * x2 + y1 * y2);
  1367. end;
  1368. //------------------------------------------------------------------------------
  1369. function SqrInt64(val: Int64): double; {$IFDEF INLINING} inline; {$ENDIF}
  1370. begin
  1371. Result := val; // force conversion
  1372. Result := Result * Result;
  1373. end;
  1374. //------------------------------------------------------------------------------
  1375. function DistanceSqr(const pt1, pt2: TPoint64): double;
  1376. begin
  1377. Result := SqrInt64(pt1.X - pt2.X) + SqrInt64(pt1.Y - pt2.Y);
  1378. end;
  1379. //------------------------------------------------------------------------------
  1380. function DistanceSqr(const pt1, pt2: TPointD): double;
  1381. begin
  1382. Result := Sqr(pt1.X - pt2.X) + Sqr(pt1.Y - pt2.Y);
  1383. end;
  1384. //------------------------------------------------------------------------------
  1385. function DistanceFromLineSqrd(const pt, linePt1, linePt2: TPoint64): double;
  1386. var
  1387. a,b,c: double;
  1388. begin
  1389. // perpendicular distance of point (x0,y0) = (a*x0 + b*y0 + C)/Sqrt(a*a + b*b)
  1390. // where ax + by +c = 0 is the equation of the line
  1391. // see https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
  1392. a := (linePt1.Y - linePt2.Y);
  1393. b := (linePt2.X - linePt1.X);
  1394. c := a * linePt1.X + b * linePt1.Y;
  1395. c := a * pt.x + b * pt.y - c;
  1396. Result := (c * c) / (a * a + b * b);
  1397. end;
  1398. //---------------------------------------------------------------------------
  1399. function DistanceFromLineSqrd(const pt, linePt1, linePt2: TPointD): double;
  1400. var
  1401. a,b,c: double;
  1402. begin
  1403. a := (linePt1.Y - linePt2.Y);
  1404. b := (linePt2.X - linePt1.X);
  1405. c := a * linePt1.X + b * linePt1.Y;
  1406. c := a * pt.x + b * pt.y - c;
  1407. Result := (c * c) / (a * a + b * b);
  1408. end;
  1409. //---------------------------------------------------------------------------
  1410. function CleanPath(const path: TPath64): TPath64;
  1411. var
  1412. i,j, len: integer;
  1413. prev: TPoint64;
  1414. begin
  1415. Result := nil;
  1416. len := Length(path);
  1417. while (len > 2) and
  1418. (CrossProduct(path[len-2], path[len-1], path[0]) = 0) do dec(len);
  1419. SetLength(Result, len);
  1420. if (len < 2) then Exit;
  1421. prev := path[len -1];
  1422. j := 0;
  1423. for i := 0 to len -2 do
  1424. begin
  1425. if CrossProduct(prev, path[i], path[i+1]) = 0 then Continue;
  1426. Result[j] := path[i];
  1427. inc(j);
  1428. prev := path[i];
  1429. end;
  1430. Result[j] := path[len -1];
  1431. SetLength(Result, j+1);
  1432. end;
  1433. //------------------------------------------------------------------------------
  1434. function SegmentsIntersect(const s1a, s1b, s2a, s2b: TPoint64;
  1435. inclusive: Boolean): boolean;
  1436. var
  1437. res1, res2, res3, res4: double;
  1438. begin
  1439. if inclusive then //result can include segments that only touch
  1440. begin
  1441. Result := false;
  1442. res1 := CrossProduct(s1a, s2a, s2b);
  1443. res2 := CrossProduct(s1b, s2a, s2b);
  1444. if (res1 * res2 > 0) then Exit;
  1445. res3 := CrossProduct(s2a, s1a, s1b);
  1446. res4 := CrossProduct(s2b, s1a, s1b);
  1447. if (res3 * res4 > 0) then Exit;
  1448. Result := (res1 <> 0) or (res2 <> 0) or
  1449. (res3 <> 0) or (res4 <> 0); // ensures not collinear
  1450. end else
  1451. result := (CrossProduct(s1a, s2a, s2b) * CrossProduct(s1b, s2a, s2b) < 0) and
  1452. (CrossProduct(s2a, s1a, s1b) * CrossProduct(s2b, s1a, s1b) < 0);
  1453. end;
  1454. //------------------------------------------------------------------------------
  1455. function GetClosestLineEnd(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPoint64;
  1456. var
  1457. i, idx: integer;
  1458. d: array[0..3] of double;
  1459. begin
  1460. d[0] := DistanceSqr(ln1a, ln2a);
  1461. if (d[0] = 0) then begin Result := ln1a; Exit; end;
  1462. d[1] := DistanceSqr(ln1b, ln2b);
  1463. if (d[1] = 0) then begin Result := ln1b; Exit; end;
  1464. d[2] := DistanceSqr(ln1a, ln2b);
  1465. if (d[2] = 0) then begin Result := ln1a; Exit; end;
  1466. d[3] := DistanceSqr(ln1b, ln2a);
  1467. if (d[3] = 0) then begin Result := ln1b; Exit; end;
  1468. idx := 0;
  1469. for i := 1 to 3 do
  1470. if (d[i] < d[idx]) then idx := i;
  1471. case idx of
  1472. 1,2: Result := ln1b;
  1473. else Result := ln1a;
  1474. end;
  1475. end;
  1476. //------------------------------------------------------------------------------
  1477. function GetIntersectPoint64(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPoint64;
  1478. var
  1479. x, m1,b1,m2,b2: double;
  1480. begin
  1481. // see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
  1482. if (ln1B.X = ln1A.X) then
  1483. begin
  1484. if (ln2B.X = ln2A.X) then exit; // parallel lines
  1485. m2 := (ln2B.Y - ln2A.Y)/(ln2B.X - ln2A.X);
  1486. b2 := ln2A.Y - m2 * ln2A.X;
  1487. Result.X := ln1A.X;
  1488. Result.Y := Round(m2*ln1A.X + b2);
  1489. end
  1490. else if (ln2B.X = ln2A.X) then
  1491. begin
  1492. m1 := (ln1B.Y - ln1A.Y)/(ln1B.X - ln1A.X);
  1493. b1 := ln1A.Y - m1 * ln1A.X;
  1494. Result.X := ln2A.X;
  1495. Result.Y := Round(m1*ln2A.X + b1);
  1496. end else
  1497. begin
  1498. m1 := (ln1B.Y - ln1A.Y)/(ln1B.X - ln1A.X);
  1499. b1 := ln1A.Y - m1 * ln1A.X;
  1500. m2 := (ln2B.Y - ln2A.Y)/(ln2B.X - ln2A.X);
  1501. b2 := ln2A.Y - m2 * ln2A.X;
  1502. if Abs(m1 - m2) < 1.0E-5 then
  1503. begin
  1504. Result := GetClosestLineEnd(ln1a, ln1b, ln2a, ln2b);
  1505. end else
  1506. begin
  1507. x := (b2 - b1)/(m1 - m2);
  1508. Result.X := Round(x);
  1509. Result.Y := Round(m1 * x + b1);
  1510. end;
  1511. end;
  1512. end;
  1513. //------------------------------------------------------------------------------
  1514. function GetIntersectPointD(const ln1a, ln1b, ln2a, ln2b: TPoint64): TPointD;
  1515. var
  1516. m1,b1,m2,b2: double;
  1517. begin
  1518. // see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
  1519. if (ln1B.X = ln1A.X) then
  1520. begin
  1521. if (ln2B.X = ln2A.X) then exit; // parallel lines
  1522. m2 := (ln2B.Y - ln2A.Y)/(ln2B.X - ln2A.X);
  1523. b2 := ln2A.Y - m2 * ln2A.X;
  1524. Result.X := ln1A.X;
  1525. Result.Y := m2*ln1A.X + b2;
  1526. end
  1527. else if (ln2B.X = ln2A.X) then
  1528. begin
  1529. m1 := (ln1B.Y - ln1A.Y)/(ln1B.X - ln1A.X);
  1530. b1 := ln1A.Y - m1 * ln1A.X;
  1531. Result.X := ln2A.X;
  1532. Result.Y := m1*ln2A.X + b1;
  1533. end else
  1534. begin
  1535. m1 := (ln1B.Y - ln1A.Y)/(ln1B.X - ln1A.X);
  1536. b1 := ln1A.Y - m1 * ln1A.X;
  1537. m2 := (ln2B.Y - ln2A.Y)/(ln2B.X - ln2A.X);
  1538. b2 := ln2A.Y - m2 * ln2A.X;
  1539. if Abs(m1 - m2) < 1.0E-5 then
  1540. begin
  1541. Result := PointD(GetClosestLineEnd(ln1a, ln1b, ln2a, ln2b));
  1542. end else
  1543. begin
  1544. Result.X := (b2 - b1)/(m1 - m2);
  1545. Result.Y := m1 * Result.X + b1;
  1546. end;
  1547. end;
  1548. end;
  1549. //------------------------------------------------------------------------------
  1550. function GetClosestLineEnd(const ln1a, ln1b, ln2a, ln2b: TPointD): TPointD;
  1551. var
  1552. i, idx: integer;
  1553. d: array[0..3] of double;
  1554. begin
  1555. d[0] := DistanceSqr(ln1a, ln2a);
  1556. if (d[0] = 0) then begin Result := ln1a; Exit; end;
  1557. d[1] := DistanceSqr(ln1b, ln2b);
  1558. if (d[1] = 0) then begin Result := ln1b; Exit; end;
  1559. d[2] := DistanceSqr(ln1a, ln2b);
  1560. if (d[2] = 0) then begin Result := ln1a; Exit; end;
  1561. d[3] := DistanceSqr(ln1b, ln2a);
  1562. if (d[3] = 0) then begin Result := ln1b; Exit; end;
  1563. idx := 0;
  1564. for i := 1 to 3 do
  1565. if (d[i] < d[idx]) then idx := i;
  1566. case idx of
  1567. 1,2: Result := ln1b;
  1568. else Result := ln1a;
  1569. end;
  1570. end;
  1571. //------------------------------------------------------------------------------
  1572. function GetIntersectPointD(const ln1a, ln1b, ln2a, ln2b: TPointD): TPointD; overload;
  1573. var
  1574. m1,b1,m2,b2: double;
  1575. begin
  1576. // see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
  1577. if (ln1B.X = ln1A.X) then
  1578. begin
  1579. if (ln2B.X = ln2A.X) then exit; // parallel lines
  1580. m2 := (ln2B.Y - ln2A.Y)/(ln2B.X - ln2A.X);
  1581. b2 := ln2A.Y - m2 * ln2A.X;
  1582. Result.X := ln1A.X;
  1583. Result.Y := m2*ln1A.X + b2;
  1584. end
  1585. else if (ln2B.X = ln2A.X) then
  1586. begin
  1587. m1 := (ln1B.Y - ln1A.Y)/(ln1B.X - ln1A.X);
  1588. b1 := ln1A.Y - m1 * ln1A.X;
  1589. Result.X := ln2A.X;
  1590. Result.Y := m1*ln2A.X + b1;
  1591. end else
  1592. begin
  1593. m1 := (ln1B.Y - ln1A.Y)/(ln1B.X - ln1A.X);
  1594. b1 := ln1A.Y - m1 * ln1A.X;
  1595. m2 := (ln2B.Y - ln2A.Y)/(ln2B.X - ln2A.X);
  1596. b2 := ln2A.Y - m2 * ln2A.X;
  1597. if Abs(m1 - m2) < 1.0E-5 then
  1598. begin
  1599. Result := GetClosestLineEnd(ln1a, ln1b, ln2a, ln2b);
  1600. end else
  1601. begin
  1602. Result.X := (b2 - b1)/(m1 - m2);
  1603. Result.Y := m1 * Result.X + b1;
  1604. end;
  1605. end;
  1606. end;
  1607. //------------------------------------------------------------------------------
  1608. function PointInPolygon(const pt: TPoint64;
  1609. const polygon: TPath64): TPointInPolygonResult;
  1610. var
  1611. i, len, val: Integer;
  1612. isAbove: Boolean;
  1613. d: Double; // used to avoid integer overflow
  1614. curr, prev, first, stop: PPoint64;
  1615. begin
  1616. result := pipOutside;
  1617. len := Length(polygon);
  1618. if len < 3 then Exit;
  1619. i := len -1;
  1620. first := @polygon[0];
  1621. while (i >= 0) and (polygon[i].Y = pt.Y) do dec(i);
  1622. if i < 0 then Exit;
  1623. isAbove := polygon[i].Y < pt.Y;
  1624. Result := pipOn;
  1625. stop := @polygon[len -1];
  1626. inc(stop); // stop is just past the last point
  1627. curr := first;
  1628. val := 0;
  1629. while (curr <> stop) do
  1630. begin
  1631. if isAbove then
  1632. begin
  1633. while (curr <> stop) and (curr.Y < pt.Y) do inc(curr);
  1634. if (curr = stop) then break;
  1635. end else
  1636. begin
  1637. while (curr <> stop) and (curr.Y > pt.Y) do inc(curr);
  1638. if (curr = stop) then break;
  1639. end;
  1640. if curr = first then
  1641. prev := stop else
  1642. prev := curr;
  1643. dec(prev);
  1644. if (curr.Y = pt.Y) then
  1645. begin
  1646. if (curr.X = pt.X) or ((curr.Y = prev.Y) and
  1647. ((pt.X < prev.X) <> (pt.X < curr.X))) then Exit;
  1648. inc(curr);
  1649. Continue;
  1650. end;
  1651. if (pt.X < curr.X) and (pt.X < prev.X) then
  1652. // we're only interested in edges crossing on the left
  1653. else if((pt.X > prev.X) and (pt.X > curr.X)) then
  1654. val := 1 - val // toggle val
  1655. else
  1656. begin
  1657. d := CrossProduct(prev^, curr^, pt);
  1658. if d = 0 then Exit; // ie point on path
  1659. if (d < 0) = isAbove then val := 1 - val;
  1660. end;
  1661. isAbove := not isAbove;
  1662. inc(curr);
  1663. end;
  1664. if val = 0 then
  1665. result := pipOutside else
  1666. result := pipInside;
  1667. end;
  1668. //------------------------------------------------------------------------------
  1669. procedure GetSinCos(angle: double; out sinA, cosA: double);
  1670. {$IFDEF INLINE} inline; {$ENDIF}
  1671. {$IFNDEF FPC}
  1672. var s, c: extended;
  1673. {$ENDIF}
  1674. begin
  1675. {$IFDEF FPC}
  1676. Math.SinCos(angle, sinA, cosA);
  1677. {$ELSE}
  1678. Math.SinCos(angle, s, c);
  1679. sinA := s; cosA := c;
  1680. {$ENDIF}
  1681. end;
  1682. //------------------------------------------------------------------------------
  1683. function Ellipse(const rec: TRect64; steps: integer): TPath64;
  1684. begin
  1685. Result := Path64(Ellipse(RectD(rec), steps));
  1686. end;
  1687. //------------------------------------------------------------------------------
  1688. function Ellipse(const rec: TRectD; steps: integer): TPathD;
  1689. var
  1690. i: Integer;
  1691. sinA, cosA: double;
  1692. centre, radius, delta: TPointD;
  1693. begin
  1694. result := nil;
  1695. if rec.IsEmpty then Exit;
  1696. with rec do
  1697. begin
  1698. centre := rec.MidPoint;
  1699. radius := PointD(Width * 0.5, Height * 0.5);
  1700. end;
  1701. if (steps < 3) then
  1702. steps := Ceil(PI * sqrt(rec.width + rec.height));
  1703. GetSinCos(2 * Pi / Steps, sinA, cosA);
  1704. delta.x := cosA; delta.y := sinA;
  1705. SetLength(Result, Steps);
  1706. Result[0] := PointD(centre.X + radius.X, centre.Y);
  1707. for i := 1 to steps -1 do
  1708. begin
  1709. Result[i] := PointD(centre.X + radius.X * delta.x,
  1710. centre.Y + radius.y * delta.y);
  1711. delta := PointD(delta.X * cosA - delta.Y * sinA,
  1712. delta.Y * cosA + delta.X * sinA);
  1713. end; // rotates clockwise
  1714. end;
  1715. //------------------------------------------------------------------------------
  1716. function PerpendicDistFromLineSqrd(const pt, line1, line2: TPoint64): double; overload;
  1717. var
  1718. a,b,c,d: double;
  1719. begin
  1720. a := pt.X - line1.X;
  1721. b := pt.Y - line1.Y;
  1722. c := line2.X - line1.X;
  1723. d := line2.Y - line1.Y;
  1724. if (c = 0) and (d = 0) then
  1725. result := 0 else
  1726. result := Sqr(a * d - c * b) / (c * c + d * d);
  1727. end;
  1728. //------------------------------------------------------------------------------
  1729. procedure RDP(const path: TPath64; startIdx, endIdx: integer;
  1730. epsilonSqrd: double; var boolArray: TArrayOfBoolean); overload;
  1731. var
  1732. i, idx: integer;
  1733. d, maxD: double;
  1734. begin
  1735. idx := 0;
  1736. maxD := 0;
  1737. while (endIdx > startIdx) and
  1738. PointsEqual(path[startIdx], path[endIdx]) do
  1739. begin
  1740. boolArray[endIdx] := false;
  1741. dec(endIdx);
  1742. end;
  1743. for i := startIdx +1 to endIdx -1 do
  1744. begin
  1745. // PerpendicDistFromLineSqrd - avoids expensive Sqrt()
  1746. d := PerpendicDistFromLineSqrd(path[i], path[startIdx], path[endIdx]);
  1747. if d <= maxD then Continue;
  1748. maxD := d;
  1749. idx := i;
  1750. end;
  1751. if maxD < epsilonSqrd then Exit;
  1752. boolArray[idx] := true;
  1753. if idx > startIdx + 1 then RDP(path, startIdx, idx, epsilonSqrd, boolArray);
  1754. if endIdx > idx + 1 then RDP(path, idx, endIdx, epsilonSqrd, boolArray);
  1755. end;
  1756. //------------------------------------------------------------------------------
  1757. function PerpendicDistFromLineSqrd(const pt, line1, line2: TPointD): double; overload;
  1758. var
  1759. a,b,c,d: double;
  1760. begin
  1761. a := pt.X - line1.X;
  1762. b := pt.Y - line1.Y;
  1763. c := line2.X - line1.X;
  1764. d := line2.Y - line1.Y;
  1765. if (c = 0) and (d = 0) then
  1766. result := 0 else
  1767. result := Sqr(a * d - c * b) / (c * c + d * d);
  1768. end;
  1769. //------------------------------------------------------------------------------
  1770. procedure RDP(const path: TPathD; startIdx, endIdx: integer;
  1771. epsilonSqrd: double; var boolArray: TArrayOfBoolean); overload;
  1772. var
  1773. i, idx: integer;
  1774. d, maxD: double;
  1775. begin
  1776. idx := 0;
  1777. maxD := 0;
  1778. while (endIdx > startIdx) and
  1779. PointsNearEqual(path[startIdx], path[endIdx]) do
  1780. begin
  1781. boolArray[endIdx] := false;
  1782. dec(endIdx);
  1783. end;
  1784. for i := startIdx +1 to endIdx -1 do
  1785. begin
  1786. // PerpendicDistFromLineSqrd - avoids expensive Sqrt()
  1787. d := PerpendicDistFromLineSqrd(path[i], path[startIdx], path[endIdx]);
  1788. if d <= maxD then Continue;
  1789. maxD := d;
  1790. idx := i;
  1791. end;
  1792. if maxD < epsilonSqrd then Exit;
  1793. boolArray[idx] := true;
  1794. if idx > startIdx + 1 then RDP(path, startIdx, idx, epsilonSqrd, boolArray);
  1795. if endIdx > idx + 1 then RDP(path, idx, endIdx, epsilonSqrd, boolArray);
  1796. end;
  1797. //------------------------------------------------------------------------------
  1798. function RamerDouglasPeucker(const path: TPath64; epsilon: double): TPath64;
  1799. var
  1800. i,j, len: integer;
  1801. boolArray: TArrayOfBoolean;
  1802. begin
  1803. len := length(path);
  1804. if len < 5 then
  1805. begin
  1806. result := Copy(path, 0, len);
  1807. Exit;
  1808. end;
  1809. SetLength(boolArray, len); // already zero initialized
  1810. boolArray[0] := true;
  1811. boolArray[len -1] := true;
  1812. RDP(path, 0, len -1, Sqr(epsilon), boolArray);
  1813. j := 0;
  1814. SetLength(Result, len);
  1815. for i := 0 to len -1 do
  1816. if boolArray[i] then
  1817. begin
  1818. Result[j] := path[i];
  1819. inc(j);
  1820. end;
  1821. SetLength(Result, j);
  1822. end;
  1823. //------------------------------------------------------------------------------
  1824. function RamerDouglasPeucker(const paths: TPaths64; epsilon: double): TPaths64;
  1825. var
  1826. i, len: integer;
  1827. begin
  1828. len := Length(paths);
  1829. SetLength(Result, len);
  1830. for i := 0 to len -1 do
  1831. Result[i] := RamerDouglasPeucker(paths[i], epsilon);
  1832. end;
  1833. //------------------------------------------------------------------------------
  1834. function RamerDouglasPeucker(const path: TPathD; epsilon: double): TPathD; overload;
  1835. var
  1836. i,j, len: integer;
  1837. boolArray: TArrayOfBoolean;
  1838. begin
  1839. len := length(path);
  1840. if len < 5 then
  1841. begin
  1842. result := Copy(path, 0, len);
  1843. Exit;
  1844. end;
  1845. SetLength(boolArray, len); // already zero initialized
  1846. boolArray[0] := true;
  1847. boolArray[len -1] := true;
  1848. RDP(path, 0, len -1, Sqr(epsilon), boolArray);
  1849. j := 0;
  1850. SetLength(Result, len);
  1851. for i := 0 to len -1 do
  1852. if boolArray[i] then
  1853. begin
  1854. Result[j] := path[i];
  1855. inc(j);
  1856. end;
  1857. SetLength(Result, j);
  1858. end;
  1859. //------------------------------------------------------------------------------
  1860. function RamerDouglasPeucker(const paths: TPathsD; epsilon: double): TPathsD; overload;
  1861. var
  1862. i, len: integer;
  1863. begin
  1864. len := Length(paths);
  1865. SetLength(Result, len);
  1866. for i := 0 to len -1 do
  1867. Result[i] := RamerDouglasPeucker(paths[i], epsilon);
  1868. end;
  1869. //------------------------------------------------------------------------------
  1870. end.