GR32_Clipper2.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. unit GR32_Clipper2;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is GR32_Clipper
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Angus Johnson
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2012-2022
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. uses
  35. GR32, GR32_Polygons,
  36. Clipper, Clipper.Core, Clipper.Engine, Clipper.Offset;
  37. (*
  38. ** TFixed variants
  39. *)
  40. function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode; const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint): GR32.TArrayOfArrayOfFixedPoint; overload;
  41. function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
  42. function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
  43. function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
  44. function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint; overload;
  45. function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFixedPoint; Delta: double; JoinType: TJoinType; EndType: TEndType; MiterLimit: double = 2): GR32.TArrayOfArrayOfFixedPoint; overload;
  46. (*
  47. ** TFloat variants
  48. *)
  49. function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode; const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint): GR32.TArrayOfArrayOfFloatPoint; overload;
  50. function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
  51. function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
  52. function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
  53. function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint; FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint; overload;
  54. function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFloatPoint; Delta: double; JoinType: TJoinType; EndType: TEndType; MiterLimit: double = 2): GR32.TArrayOfArrayOfFloatPoint; overload;
  55. (*
  56. ** TPath64 conversion
  57. *)
  58. function FixedPointsToPath64(const PathFixed: GR32.TArrayOfFixedPoint): Clipper.TPath64;
  59. function FloatPointsToPath64(const PathFloat: GR32.TArrayOfFloatPoint): Clipper.TPath64;
  60. function FixedPointsToPaths64(const PathsFixed: GR32.TArrayOfArrayOfFixedPoint): Clipper.TPaths64;
  61. function FloatPointsToPaths64(const PathsFloat: GR32.TArrayOfArrayOfFloatPoint): Clipper.TPaths64;
  62. function Paths64ToFixedPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFixedPoint;
  63. function Paths64ToFloatPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFloatPoint;
  64. function FloatRect(const r: TRect64): GR32.TFloatRect;
  65. (*
  66. ** Convenience redeclarations
  67. *)
  68. type
  69. TClipper64 = Clipper.Engine.TClipper64;
  70. TPoint64 = Clipper.Core.TPoint64;
  71. TRect64 = Clipper.Core.TRect64;
  72. TPath64 = Clipper.Core.TPath64;
  73. TPaths64 = Clipper.Core.TPaths64;
  74. TPointD = Clipper.Core.TPointD;
  75. TRectD = Clipper.Core.TRectD;
  76. TPathD = Clipper.Core.TPathD;
  77. TPathsD = Clipper.Core.TPathsD;
  78. TFillRule = Clipper.Core.TFillRule;
  79. TPolyTree64 = Clipper.Engine.TPolyTree64;
  80. TPolyTreeD = Clipper.Engine.TPolyTreeD;
  81. TJoinType = Clipper.Offset.TJoinType;
  82. TEndType = Clipper.Offset.TEndType;
  83. TClipType = Clipper.Core.TClipType;
  84. (*
  85. ** Ditto consts
  86. *)
  87. const
  88. frEvenOdd = Clipper.Core.frEvenOdd;
  89. frNonZero = Clipper.Core.frNonZero;
  90. frPositive = Clipper.Core.frPositive;
  91. frNegative = Clipper.Core.frNegative;
  92. jtMiter = Clipper.Offset.jtMiter;
  93. jtBevel = Clipper.Offset.jtBevel;
  94. jtRound = Clipper.Offset.jtRound;
  95. jtSquare = Clipper.Offset.jtSquare;
  96. jtRoundEx = Clipper.Offset.jtRound; // Not implemented in Clipper2
  97. etPolygon = Clipper.Offset.etPolygon;
  98. etJoined = Clipper.Offset.etJoined;
  99. etButt = Clipper.Offset.etButt;
  100. etSquare = Clipper.Offset.etSquare;
  101. etRound = Clipper.Offset.etRound;
  102. ctNone = Clipper.Core.ctNone;
  103. ctIntersection = Clipper.Core.ctIntersection;
  104. ctUnion = Clipper.Core.ctUnion;
  105. ctDifference = Clipper.Core.ctDifference;
  106. ctXor = Clipper.Core.ctXor;
  107. (*
  108. ** Clipper float scale factor
  109. *)
  110. type
  111. ClipperFloat = {$ifdef RECORD_CLASS_VAR}record{$else}object{$endif}
  112. private
  113. class var
  114. FScale: Double;
  115. FInvScale: Double;
  116. FGrowScale: Double;
  117. FFixedGrowScale: Double;
  118. class procedure SetScale(Value: Double); static;
  119. public
  120. class property Scale: Double read FScale write SetScale;
  121. class property InvScale: Double read FInvScale; // 1 / Scale
  122. class property GrowScale: Double read FGrowScale; // Scale * OffsetFactor
  123. class property FixedGrowScale: Double read FFixedGrowScale; // Note: Does not apply scale since fixed values aren't scaled
  124. const
  125. // OffsetFactor specifies the ratio between Graphic32's traditional measure
  126. // of stroke width (or growth delta) and the measure Clipper2 uses.
  127. //
  128. // For example, in Graphics32, when we draw a line with a stroke width of 10,
  129. // we expect a line that is approximately 10 pixel wide. Prior to using
  130. // Clipper2 to offset polylines into polygons, we would simply inflate the
  131. // polyline with a growth delta of 10. With the current Clipper2 implementation
  132. // we instead has to inflate the polyline with a growth delta of 5.
  133. //
  134. // In some older versions of Clipper2, OffsetFactor was 1.
  135. OffsetFactor = 0.5;
  136. end;
  137. implementation
  138. function DoubleToInt64(val: double): Int64; {$IFDEF INLINE} inline; {$ENDIF}
  139. var
  140. exp: integer;
  141. i64: UInt64 absolute val;
  142. begin
  143. //https://en.wikipedia.org/wiki/Double-precision_floating-point_format
  144. Result := 0;
  145. if i64 = 0 then
  146. Exit;
  147. exp := Integer(Cardinal(i64 shr 52) and $7FF) - 1023;
  148. //nb: when exp == 1024 then val == INF or NAN.
  149. if exp < 0 then
  150. Exit;
  151. Result := ((i64 and $1FFFFFFFFFFFFF) shr (52 - exp)) or (UInt64(1) shl exp);
  152. if val < 0 then
  153. Result := -Result;
  154. end;
  155. function FloatRect(const r: TRect64): GR32.TFloatRect;
  156. begin
  157. Result.Left := DoubleToInt64(r.Left * ClipperFloat.Scale);
  158. Result.Top := DoubleToInt64(r.Top * ClipperFloat.Scale);
  159. Result.Right := DoubleToInt64(r.Right * ClipperFloat.Scale);
  160. Result.Bottom := DoubleToInt64(r.Bottom * ClipperFloat.Scale);
  161. end;
  162. function FixedPointsToPath64(const PathFixed: GR32.TArrayOfFixedPoint): Clipper.TPath64;
  163. var
  164. i, len: integer;
  165. begin
  166. len := Length(PathFixed);
  167. SetLength(Result, len);
  168. for i := 0 to len -1 do
  169. begin
  170. Result[i].X := PathFixed[i].X;
  171. Result[i].Y := PathFixed[i].Y;
  172. end;
  173. end;
  174. function FloatPointsToPath64(const PathFloat: GR32.TArrayOfFloatPoint): Clipper.TPath64;
  175. var
  176. i, len: integer;
  177. begin
  178. len := Length(PathFloat);
  179. SetLength(Result, len);
  180. for i := 0 to len -1 do
  181. begin
  182. Result[i].X := DoubleToInt64(PathFloat[i].X * ClipperFloat.Scale);
  183. Result[i].Y := DoubleToInt64(PathFloat[i].Y * ClipperFloat.Scale);
  184. end;
  185. end;
  186. function FixedPointsToPaths64(const PathsFixed: GR32.TArrayOfArrayOfFixedPoint): Clipper.TPaths64;
  187. var
  188. i, len: integer;
  189. begin
  190. len := Length(PathsFixed);
  191. SetLength(Result, len);
  192. for i := 0 to len -1 do
  193. Result[i] := FixedPointsToPath64(PathsFixed[i]);
  194. end;
  195. function FloatPointsToPaths64(const PathsFloat: GR32.TArrayOfArrayOfFloatPoint): Clipper.TPaths64;
  196. var
  197. i, len: integer;
  198. begin
  199. len := Length(PathsFloat);
  200. SetLength(Result, len);
  201. for i := 0 to len -1 do
  202. Result[i] := FloatPointsToPath64(PathsFloat[i]);
  203. end;
  204. function Path64ToFixedPoints(const Path: Clipper.TPath64): GR32.TArrayOfFixedPoint;
  205. var
  206. i, len: integer;
  207. begin
  208. len := Length(Path);
  209. SetLength(Result, len);
  210. for i := 0 to len -1 do
  211. begin
  212. Result[i].X := TFixed(Path[i].X);
  213. Result[i].Y := TFixed(Path[i].Y);
  214. end;
  215. end;
  216. function Path64ToFloatPoints(const Path: Clipper.TPath64): GR32.TArrayOfFloatPoint;
  217. var
  218. i, len: integer;
  219. begin
  220. len := Length(Path);
  221. SetLength(Result, len);
  222. for i := 0 to len -1 do
  223. begin
  224. Result[i].X := Path[i].X * ClipperFloat.InvScale;
  225. Result[i].Y := Path[i].Y * ClipperFloat.InvScale;
  226. end;
  227. end;
  228. function Paths64ToFixedPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFixedPoint;
  229. var
  230. i, len: integer;
  231. begin
  232. len := Length(Paths);
  233. SetLength(Result, len);
  234. for i := 0 to len -1 do
  235. Result[i] := Path64ToFixedPoints(Paths[i]);
  236. end;
  237. function Paths64ToFloatPoints(const Paths: Clipper.TPaths64): GR32.TArrayOfArrayOfFloatPoint;
  238. var
  239. i, len: integer;
  240. begin
  241. len := Length(Paths);
  242. SetLength(Result, len);
  243. for i := 0 to len -1 do
  244. Result[i] := Path64ToFloatPoints(Paths[i]);
  245. end;
  246. function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode;
  247. const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint):
  248. GR32.TArrayOfArrayOfFixedPoint;
  249. var
  250. sub, clp, sol: TPaths64;
  251. begin
  252. sub := FixedPointsToPaths64(Subject);
  253. clp := FixedPointsToPaths64(Clip);
  254. sol := Clipper.BooleanOp(ClipType, sub, clp, TFillRule(FillMode));
  255. Result := Paths64ToFixedPoints(sol);
  256. end;
  257. function GR32BoolOp(ClipType: TClipType; FillMode: TPolyFillMode;
  258. const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint):
  259. GR32.TArrayOfArrayOfFloatPoint;
  260. var
  261. sub, clp, sol: TPaths64;
  262. begin
  263. sub := FloatPointsToPaths64(Subject);
  264. clp := FloatPointsToPaths64(Clip);
  265. sol := Clipper.BooleanOp(ClipType, sub, clp, TFillRule(FillMode));
  266. Result := Paths64ToFloatPoints(sol);
  267. end;
  268. function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
  269. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
  270. begin
  271. Result := GR32BoolOp(ctIntersection, FillMode, Subject, Clip);
  272. end;
  273. function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
  274. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
  275. begin
  276. Result := GR32BoolOp(ctUnion, FillMode, Subject, Clip);
  277. end;
  278. function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
  279. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
  280. begin
  281. Result := GR32BoolOp(ctDifference, FillMode, Subject, Clip);
  282. end;
  283. function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFixedPoint;
  284. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFixedPoint;
  285. begin
  286. Result := GR32BoolOp(ctXor, FillMode, Subject, Clip);
  287. end;
  288. function GR32_Intersect(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
  289. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
  290. begin
  291. Result := GR32BoolOp(ctIntersection, FillMode, Subject, Clip);
  292. end;
  293. function GR32_Union(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
  294. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
  295. begin
  296. Result := GR32BoolOp(ctUnion, FillMode, Subject, Clip);
  297. end;
  298. function GR32_Difference(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
  299. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
  300. begin
  301. Result := GR32BoolOp(ctDifference, FillMode, Subject, Clip);
  302. end;
  303. function GR32_XOR(const Subject, Clip: GR32.TArrayOfArrayOfFloatPoint;
  304. FillMode: TPolyFillMode): GR32.TArrayOfArrayOfFloatPoint;
  305. begin
  306. Result := GR32BoolOp(ctXor, FillMode, Subject, Clip);
  307. end;
  308. function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFixedPoint;
  309. Delta: double; JoinType: TJoinType; EndType: TEndType;
  310. MiterLimit: double): GR32.TArrayOfArrayOfFixedPoint;
  311. var
  312. sub, sol: TPaths64;
  313. begin
  314. sub := FixedPointsToPaths64(Paths);
  315. sol := Clipper.InflatePaths(sub, Delta * ClipperFloat.FixedGrowScale, JoinType, EndType, MiterLimit);
  316. sol := RamerDouglasPeucker(sol, 1);
  317. Result := Paths64ToFixedPoints(sol);
  318. end;
  319. function GR32_Inflate(const Paths: GR32.TArrayOfArrayOfFloatPoint;
  320. Delta: double; JoinType: TJoinType; EndType: TEndType;
  321. MiterLimit: double = 2): GR32.TArrayOfArrayOfFloatPoint;
  322. var
  323. sub, sol: TPaths64;
  324. begin
  325. sub := FloatPointsToPaths64(Paths);
  326. sol := Clipper.InflatePaths(sub, Delta * ClipperFloat.GrowScale, JoinType, EndType, MiterLimit);
  327. sol := RamerDouglasPeucker(sol, 10);
  328. Result := Paths64ToFloatPoints(sol);
  329. end;
  330. { ClipperFloat }
  331. class procedure ClipperFloat.SetScale(Value: Double);
  332. begin
  333. if (Value <> 0) then
  334. begin
  335. FScale := Value;
  336. FInvScale := 1 / FScale;
  337. FGrowScale := FScale * OffsetFactor;
  338. FFixedGrowScale := OffsetFactor * FixedToFloat;
  339. end;
  340. end;
  341. initialization
  342. ClipperFloat.Scale := 100;
  343. // Guard against breaking change in Clipper TFillRule order
  344. Assert(Ord(TPolyFillMode.pfAlternate) = Ord(TFillRule.frEvenOdd));
  345. Assert(Ord(TPolyFillMode.pfWinding) = Ord(TFillRule.frNonZero));
  346. Assert(Ord(TPolyFillMode.pfEvenOdd) = Ord(TFillRule.frEvenOdd));
  347. Assert(Ord(TPolyFillMode.pfNonZero) = Ord(TFillRule.frNonZero));
  348. end.