2
0

Clipper.pas 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. unit Clipper;
  2. (*******************************************************************************
  3. * Author : Angus Johnson *
  4. * Date : 7 May 2024 *
  5. * Website : http://www.angusj.com *
  6. * Copyright : Angus Johnson 2010-2024 *
  7. * Purpose : This module provides a simple interface to the Clipper Library *
  8. * License : http://www.boost.org/LICENSE_1_0.txt *
  9. *******************************************************************************)
  10. interface
  11. {$I Clipper.inc}
  12. uses
  13. Math, SysUtils, Classes,
  14. Clipper.Core, Clipper.Engine, Clipper.Offset, Clipper.RectClip;
  15. // A number of structures defined in other units are redeclared here
  16. // so those units won't also need to be declared in your own units clauses.
  17. type
  18. TClipper = Clipper.Engine.TClipper64;
  19. TClipper64 = Clipper.Engine.TClipper64;
  20. TPoint64 = Clipper.Core.TPoint64;
  21. TRect64 = Clipper.Core.TRect64;
  22. TPath64 = Clipper.Core.TPath64;
  23. TPaths64 = Clipper.Core.TPaths64;
  24. TPointD = Clipper.Core.TPointD;
  25. TRectD = Clipper.Core.TRectD;
  26. TPathD = Clipper.Core.TPathD;
  27. TPathsD = Clipper.Core.TPathsD;
  28. TFillRule = Clipper.Core.TFillRule;
  29. TPolyTree64 = Clipper.Engine.TPolyTree64;
  30. TPolyTreeD = Clipper.Engine.TPolyTreeD;
  31. TJoinType = Clipper.Offset.TJoinType;
  32. TEndType = Clipper.Offset.TEndType;
  33. TArrayOfInt64 = array of Int64;
  34. const
  35. frEvenOdd = Clipper.Core.frEvenOdd;
  36. frNonZero = Clipper.Core.frNonZero;
  37. frPositive = Clipper.Core.frPositive;
  38. frNegative = Clipper.Core.frNegative;
  39. jtBevel = Clipper.Offset.jtBevel;
  40. jtSquare = Clipper.Offset.jtSquare;
  41. jtRound = Clipper.Offset.jtRound;
  42. jtMiter = Clipper.Offset.jtMiter;
  43. etPolygon = Clipper.Offset.etPolygon;
  44. etJoined = Clipper.Offset.etJoined;
  45. etButt = Clipper.Offset.etButt;
  46. etSquare = Clipper.Offset.etSquare;
  47. etRound = Clipper.Offset.etRound;
  48. ctNone = Clipper.Core.ctNone;
  49. ctIntersection = Clipper.Core.ctIntersection;
  50. ctUnion = Clipper.Core.ctUnion;
  51. ctDifference = Clipper.Core.ctDifference;
  52. ctXor = Clipper.Core.ctXor;
  53. function BooleanOp(clipType: TClipType;
  54. const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64; overload;
  55. function BooleanOp(clipType: TClipType; const subjects, clips:
  56. TPathsD; fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
  57. procedure BooleanOp(clipType: TClipType; const subjects, clips: TPaths64;
  58. fillRule: TFillRule; polytree: TPolyTree64); overload;
  59. function Intersect(const subjects, clips: TPaths64;
  60. fillRule: TFillRule): TPaths64; overload;
  61. function Union(const subjects, clips: TPaths64;
  62. fillRule: TFillRule): TPaths64; overload;
  63. function Union(const subjects: TPaths64;
  64. fillRule: TFillRule): TPaths64; overload;
  65. function Difference(const subjects, clips: TPaths64;
  66. fillRule: TFillRule): TPaths64; overload;
  67. function XOR_(const subjects, clips: TPaths64;
  68. fillRule: TFillRule): TPaths64; overload;
  69. function Intersect(const subjects, clips: TPathsD;
  70. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
  71. function Union(const subjects: TPathsD;
  72. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
  73. function Union(const subjects, clips: TPathsD;
  74. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
  75. function Difference(const subjects, clips: TPathsD;
  76. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
  77. function XOR_(const subjects, clips: TPathsD;
  78. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD; overload;
  79. function InflatePaths(const paths: TPaths64; delta: Double;
  80. jt: TJoinType = jtRound; et: TEndType = etPolygon;
  81. MiterLimit: double = 2.0; ArcTolerance: double = 0.0): TPaths64; overload;
  82. function InflatePaths(const paths: TPathsD; delta: Double;
  83. jt: TJoinType = jtRound; et: TEndType = etPolygon;
  84. miterLimit: double = 2.0; precision: integer = 2;
  85. ArcTolerance: double = 0.0): TPathsD; overload;
  86. // RectClip: for closed paths only (otherwise use RectClipLines)
  87. function RectClip(const rect: TRect64; const path: TPath64): TPath64; overload;
  88. function RectClip(const rect: TRect64; const paths: TPaths64): TPaths64; overload;
  89. function RectClip(const rect: TRectD; const path: TPathD; precision: integer = 2): TPathD; overload;
  90. function RectClip(const rect: TRectD; const paths: TPathsD; precision: integer = 2): TPathsD; overload;
  91. function RectClipLines(const rect: TRect64;
  92. const path: TPath64): TPaths64; overload;
  93. function RectClipLines(const rect: TRect64;
  94. const paths: TPaths64): TPaths64; overload;
  95. function RectClipLines(const rect: TRectD; const path: TPathD;
  96. precision: integer = 2): TPathsD; overload;
  97. function RectClipLines(const rect: TRectD; const paths: TPathsD;
  98. precision: integer = 2): TPathsD; overload;
  99. function TranslatePath(const path: TPath64; dx, dy: Int64): TPath64; overload;
  100. function TranslatePath(const path: TPathD; dx, dy: double): TPathD; overload;
  101. function TranslatePaths(const paths: TPaths64; dx, dy: Int64): TPaths64; overload;
  102. function TranslatePaths(const paths: TPathsD; dx, dy: double): TPathsD; overload;
  103. function MinkowskiSum(const pattern, path: TPath64;
  104. pathIsClosed: Boolean): TPaths64; overload;
  105. function MinkowskiSum(const pattern, path: TPathD;
  106. pathIsClosed: Boolean): TPathsD; overload;
  107. function PolyTreeToPaths64(PolyTree: TPolyTree64): TPaths64;
  108. function PolyTreeToPathsD(PolyTree: TPolyTreeD): TPathsD;
  109. function PathToString(const p: TPath64;
  110. indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
  111. function PathToString(const p: TPathD; decimals: integer;
  112. indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
  113. function PathsToString(const p: TPaths64;
  114. indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
  115. function PathsToString(const p: TPathsD; decimals: integer;
  116. indentSpaces: integer = 0; pointsPerRow: integer = 0): string; overload;
  117. //ShowPolyTreeStructure: only useful when debugging
  118. procedure ShowPolyTreeStructure(polytree: TPolyTree64; strings: TStrings); overload;
  119. procedure ShowPolyTreeStructure(polytree: TPolyTreeD; strings: TStrings); overload;
  120. function MakePath(const ints: array of Int64): TPath64; overload;
  121. function MakePathD(const dbls: array of double): TPathD; overload;
  122. function TrimCollinear(const p: TPath64;
  123. isOpenPath: Boolean = false): TPath64; overload;
  124. function TrimCollinear(const path: TPathD;
  125. precision: integer; isOpenPath: Boolean = false): TPathD; overload;
  126. function PointInPolygon(const pt: TPoint64; const polygon: TPath64):
  127. TPointInPolygonResult;
  128. function SimplifyPath(const path: TPath64;
  129. shapeTolerance: double; isClosedPath: Boolean = true): TPath64; overload;
  130. function SimplifyPaths(const paths: TPaths64;
  131. shapeTolerance: double; isClosedPath: Boolean = true): TPaths64; overload;
  132. function SimplifyPath(const path: TPathD; shapeTolerance: double;
  133. isClosedPath: Boolean = true; decimalPrecision: integer = 2): TPathD; overload;
  134. function SimplifyPaths(const paths: TPathsD; shapeTolerance: double;
  135. isClosedPath: Boolean = true; decimalPrecision: integer = 2): TPathsD; overload;
  136. implementation
  137. uses
  138. Clipper.Minkowski;
  139. //------------------------------------------------------------------------------
  140. //------------------------------------------------------------------------------
  141. {$IFDEF USINGZ}
  142. function MakePath(const ints: array of Int64): TPath64;
  143. var
  144. i, len: integer;
  145. begin
  146. len := length(ints) div 3;
  147. SetLength(Result, len);
  148. for i := 0 to len -1 do
  149. begin
  150. Result[i].X := ints[i*3];
  151. Result[i].Y := ints[i*3 +1];
  152. Result[i].z := ints[i*3 +2];
  153. end;
  154. end;
  155. //------------------------------------------------------------------------------
  156. function MakePathD(const dbls: array of double): TPathD; overload;
  157. var
  158. i, len: integer;
  159. begin
  160. len := length(dbls) div 3;
  161. SetLength(Result, len);
  162. for i := 0 to len -1 do
  163. begin
  164. Result[i].X := dbls[i*3];
  165. Result[i].Y := dbls[i*3 +1];
  166. Result[i].Z := Round(dbls[i*3 +2]);
  167. end;
  168. end;
  169. //------------------------------------------------------------------------------
  170. {$ELSE}
  171. function MakePath(const ints: array of Int64): TPath64;
  172. var
  173. i, len: integer;
  174. begin
  175. len := length(ints) div 2;
  176. SetLength(Result, len);
  177. for i := 0 to len -1 do
  178. begin
  179. Result[i].X := ints[i*2];
  180. Result[i].Y := ints[i*2 +1];
  181. end;
  182. end;
  183. //------------------------------------------------------------------------------
  184. function MakePathD(const dbls: array of double): TPathD; overload;
  185. var
  186. i, len: integer;
  187. begin
  188. len := length(dbls) div 2;
  189. SetLength(Result, len);
  190. for i := 0 to len -1 do
  191. begin
  192. Result[i].X := dbls[i*2];
  193. Result[i].Y := dbls[i*2 +1];
  194. end;
  195. end;
  196. //------------------------------------------------------------------------------
  197. {$ENDIF}
  198. procedure AddPolyNodeToPaths(Poly: TPolyPath64; var Paths: TPaths64);
  199. var
  200. i: Integer;
  201. begin
  202. if (Length(Poly.Polygon) > 0) then
  203. begin
  204. i := Length(Paths);
  205. SetLength(Paths, i +1);
  206. Paths[i] := Poly.Polygon;
  207. end;
  208. for i := 0 to Poly.Count - 1 do
  209. AddPolyNodeToPaths(Poly[i], Paths);
  210. end;
  211. //------------------------------------------------------------------------------
  212. function PolyTreeToPaths64(PolyTree: TPolyTree64): TPaths64;
  213. begin
  214. Result := nil;
  215. AddPolyNodeToPaths(PolyTree, Result);
  216. end;
  217. //------------------------------------------------------------------------------
  218. procedure AddPolyNodeToPathsD(Poly: TPolyPathD; var Paths: TPathsD);
  219. var
  220. i: Integer;
  221. begin
  222. if (Length(Poly.Polygon) > 0) then
  223. begin
  224. i := Length(Paths);
  225. SetLength(Paths, i +1);
  226. Paths[i] := Poly.Polygon;
  227. end;
  228. for i := 0 to Poly.Count - 1 do
  229. AddPolyNodeToPathsD(Poly[i], Paths);
  230. end;
  231. //------------------------------------------------------------------------------
  232. function PolyTreeToPathsD(PolyTree: TPolyTreeD): TPathsD;
  233. begin
  234. Result := nil;
  235. AddPolyNodeToPathsD(PolyTree, Result);
  236. end;
  237. //------------------------------------------------------------------------------
  238. //------------------------------------------------------------------------------
  239. function BooleanOp(clipType: TClipType;
  240. const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
  241. begin
  242. with TClipper64.Create do
  243. try
  244. AddSubject(subjects);
  245. AddClip(clips);
  246. Execute(clipType, fillRule, Result);
  247. finally
  248. Free;
  249. end;
  250. end;
  251. //------------------------------------------------------------------------------
  252. function BooleanOp(clipType: TClipType; const subjects, clips: TPathsD;
  253. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
  254. begin
  255. with TClipperD.Create(decimalPrec) do
  256. try
  257. AddSubject(subjects);
  258. AddClip(clips);
  259. Execute(clipType, fillRule, Result);
  260. finally
  261. Free;
  262. end;
  263. end;
  264. //------------------------------------------------------------------------------
  265. procedure BooleanOp(clipType: TClipType; const subjects, clips: TPaths64;
  266. fillRule: TFillRule; polytree: TPolyTree64);
  267. var
  268. dummy: TPaths64;
  269. begin
  270. with TClipper64.Create do
  271. try
  272. AddSubject(subjects);
  273. AddClip(clips);
  274. Execute(clipType, fillRule, polytree, dummy);
  275. finally
  276. Free;
  277. end;
  278. end;
  279. //------------------------------------------------------------------------------
  280. function Intersect(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
  281. begin
  282. Result := BooleanOp(ctIntersection, subjects, clips, fillRule);
  283. end;
  284. //------------------------------------------------------------------------------
  285. function Union(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
  286. begin
  287. Result := BooleanOp(ctUnion, subjects, clips, fillRule);
  288. end;
  289. //------------------------------------------------------------------------------
  290. function Union(const subjects: TPaths64; fillRule: TFillRule): TPaths64;
  291. begin
  292. Result := BooleanOp(ctUnion, subjects, nil, fillRule);
  293. end;
  294. //------------------------------------------------------------------------------
  295. function Difference(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
  296. begin
  297. Result := BooleanOp(ctDifference, subjects, clips, fillRule);
  298. end;
  299. //------------------------------------------------------------------------------
  300. function XOR_(const subjects, clips: TPaths64; fillRule: TFillRule): TPaths64;
  301. begin
  302. Result := BooleanOp(ctXor, subjects, clips, fillRule);
  303. end;
  304. //------------------------------------------------------------------------------
  305. function Intersect(const subjects, clips: TPathsD;
  306. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
  307. begin
  308. Result := BooleanOp(ctIntersection, subjects, clips, fillRule, decimalPrec);
  309. end;
  310. //------------------------------------------------------------------------------
  311. function Union(const subjects, clips: TPathsD;
  312. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
  313. begin
  314. Result := BooleanOp(ctUnion, subjects, clips, fillRule, decimalPrec);
  315. end;
  316. //------------------------------------------------------------------------------
  317. function Union(const subjects: TPathsD;
  318. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
  319. begin
  320. Result := BooleanOp(ctUnion, subjects, nil, fillRule, decimalPrec);
  321. end;
  322. //------------------------------------------------------------------------------
  323. function Difference(const subjects, clips: TPathsD;
  324. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
  325. begin
  326. Result := BooleanOp(ctDifference, subjects, clips, fillRule, decimalPrec);
  327. end;
  328. //------------------------------------------------------------------------------
  329. function XOR_(const subjects, clips: TPathsD;
  330. fillRule: TFillRule; decimalPrec: integer = 2): TPathsD;
  331. begin
  332. Result := BooleanOp(ctXor, subjects, clips, fillRule, decimalPrec);
  333. end;
  334. //------------------------------------------------------------------------------
  335. //------------------------------------------------------------------------------
  336. function InflatePaths(const paths: TPaths64; delta: Double;
  337. jt: TJoinType; et: TEndType; MiterLimit: double;
  338. ArcTolerance: double): TPaths64;
  339. var
  340. co: TClipperOffset;
  341. begin
  342. co := TClipperOffset.Create(MiterLimit, ArcTolerance);
  343. try
  344. co.AddPaths(paths, jt, et);
  345. co.Execute(delta, Result);
  346. finally
  347. co.free;
  348. end;
  349. end;
  350. //------------------------------------------------------------------------------
  351. function InflatePaths(const paths: TPathsD; delta: Double;
  352. jt: TJoinType; et: TEndType; miterLimit: double;
  353. precision: integer; ArcTolerance: double): TPathsD;
  354. var
  355. pp: TPaths64;
  356. scale, invScale: double;
  357. begin
  358. CheckPrecisionRange(precision);
  359. scale := Power(10, precision);
  360. invScale := 1/scale;
  361. pp := ScalePaths(paths, scale, scale);
  362. with TClipperOffset.Create(miterLimit, ArcTolerance) do
  363. try
  364. AddPaths(pp, jt, et);
  365. Execute(delta * scale, pp); // reuse pp to receive the solution.
  366. finally
  367. free;
  368. end;
  369. Result := ScalePathsD(pp, invScale, invScale);
  370. end;
  371. //------------------------------------------------------------------------------
  372. function RectClip(const rect: TRect64;
  373. const path: TPath64): TPath64;
  374. var
  375. paths: TPaths64;
  376. begin
  377. SetLength(paths, 1);
  378. paths[0] := path;
  379. paths := RectClip(rect, paths);
  380. if Assigned(paths) then
  381. Result := paths[0] else
  382. Result := nil;
  383. end;
  384. //------------------------------------------------------------------------------
  385. function RectClip(const rect: TRect64; const paths: TPaths64): TPaths64;
  386. begin
  387. Result := nil;
  388. if rect.IsEmpty then Exit;
  389. with TRectClip64.Create(rect) do
  390. try
  391. Result := Execute(paths);
  392. finally
  393. Free;
  394. end;
  395. end;
  396. //------------------------------------------------------------------------------
  397. function RectClip(const rect: TRectD; const path: TPathD; precision: integer): TPathD;
  398. var
  399. scale: double;
  400. tmpPath: TPath64;
  401. rec: TRect64;
  402. begin
  403. Result := nil;
  404. if not rect.Intersects(GetBounds(path)) then Exit;
  405. CheckPrecisionRange(precision);
  406. scale := Math.Power(10, precision);
  407. rec := Rect64(ScaleRect(rect, scale));
  408. tmpPath := ScalePath(path, scale);
  409. tmpPath := RectClip(rec, tmpPath);
  410. Result := ScalePathD(tmpPath, 1/scale);
  411. end;
  412. //------------------------------------------------------------------------------
  413. function RectClip(const rect: TRectD; const paths: TPathsD; precision: integer): TPathsD;
  414. var
  415. scale: double;
  416. tmpPaths: TPaths64;
  417. rec: TRect64;
  418. begin
  419. CheckPrecisionRange(precision);
  420. scale := Math.Power(10, precision);
  421. rec := Rect64(ScaleRect(rect, scale));
  422. tmpPaths := ScalePaths(paths, scale);
  423. with TRectClip64.Create(rec) do
  424. try
  425. tmpPaths := Execute(tmpPaths);
  426. finally
  427. Free;
  428. end;
  429. Result := ScalePathsD(tmpPaths, 1/scale);
  430. end;
  431. //------------------------------------------------------------------------------
  432. function RectClipLines(const rect: TRect64; const path: TPath64): TPaths64;
  433. var
  434. tmp: TPaths64;
  435. begin
  436. Result := nil;
  437. SetLength(tmp, 1);
  438. tmp[0] := path;
  439. with TRectClipLines64.Create(rect) do
  440. try
  441. Result := Execute(tmp);
  442. finally
  443. Free;
  444. end;
  445. end;
  446. //------------------------------------------------------------------------------
  447. function RectClipLines(const rect: TRect64; const paths: TPaths64): TPaths64;
  448. begin
  449. Result := nil;
  450. if rect.IsEmpty then Exit;
  451. with TRectClipLines64.Create(rect) do
  452. try
  453. Result := Execute(paths);
  454. finally
  455. Free;
  456. end;
  457. end;
  458. //------------------------------------------------------------------------------
  459. function RectClipLines(const rect: TRectD;
  460. const path: TPathD; precision: integer): TPathsD;
  461. var
  462. scale: double;
  463. tmpPath: TPath64;
  464. tmpPaths: TPaths64;
  465. rec: TRect64;
  466. begin
  467. Result := nil;
  468. if not rect.Intersects(GetBounds(path)) then Exit;
  469. CheckPrecisionRange(precision);
  470. scale := Math.Power(10, precision);
  471. rec := Rect64(ScaleRect(rect, scale));
  472. tmpPath := ScalePath(path, scale);
  473. tmpPaths := RectClipLines(rec, tmpPath);
  474. Result := ScalePathsD(tmpPaths, 1/scale);
  475. end;
  476. //------------------------------------------------------------------------------
  477. function RectClipLines(const rect: TRectD; const paths: TPathsD;
  478. precision: integer = 2): TPathsD;
  479. var
  480. scale: double;
  481. tmpPaths: TPaths64;
  482. rec: TRect64;
  483. begin
  484. Result := nil;
  485. if rect.IsEmpty then Exit;
  486. CheckPrecisionRange(precision);
  487. scale := Math.Power(10, precision);
  488. rec := Rect64(ScaleRect(rect, scale));
  489. tmpPaths := ScalePaths(paths, scale);
  490. with TRectClipLines64.Create(rec) do
  491. try
  492. tmpPaths := Execute(tmpPaths);
  493. finally
  494. Free;
  495. end;
  496. Result := ScalePathsD(tmpPaths, 1/scale);
  497. end;
  498. //------------------------------------------------------------------------------
  499. function TranslatePath(const path: TPath64; dx, dy: Int64): TPath64;
  500. var
  501. i, len: integer;
  502. begin
  503. len := length(path);
  504. setLength(result, len);
  505. for i := 0 to len -1 do
  506. begin
  507. result[i].x := path[i].x + dx;
  508. result[i].y := path[i].y + dy;
  509. end;
  510. end;
  511. //------------------------------------------------------------------------------
  512. function TranslatePath(const path: TPathD; dx, dy: double): TPathD;
  513. var
  514. i, len: integer;
  515. begin
  516. len := length(path);
  517. setLength(result, len);
  518. for i := 0 to len -1 do
  519. begin
  520. result[i].x := path[i].x + dx;
  521. result[i].y := path[i].y + dy;
  522. end;
  523. end;
  524. //------------------------------------------------------------------------------
  525. function TranslatePaths(const paths: TPaths64; dx, dy: Int64): TPaths64;
  526. var
  527. i, len: integer;
  528. begin
  529. len := length(paths);
  530. setLength(result, len);
  531. for i := 0 to len -1 do
  532. begin
  533. result[i] := TranslatePath(paths[i], dx, dy);
  534. end;
  535. end;
  536. //------------------------------------------------------------------------------
  537. function TranslatePaths(const paths: TPathsD; dx, dy: double): TPathsD;
  538. var
  539. i, len: integer;
  540. begin
  541. len := length(paths);
  542. setLength(result, len);
  543. for i := 0 to len -1 do
  544. begin
  545. result[i] := TranslatePath(paths[i], dx, dy);
  546. end;
  547. end;
  548. //------------------------------------------------------------------------------
  549. function MinkowskiSum(const pattern, path: TPath64;
  550. pathIsClosed: Boolean): TPaths64;
  551. begin
  552. Result := Clipper.Minkowski.MinkowskiSum(pattern, path, pathIsClosed);
  553. end;
  554. //------------------------------------------------------------------------------
  555. function MinkowskiSum(const pattern, path: TPathD;
  556. pathIsClosed: Boolean): TPathsD;
  557. begin
  558. Result := Clipper.Minkowski.MinkowskiSum(pattern, path, pathIsClosed);
  559. end;
  560. //------------------------------------------------------------------------------
  561. function PathToString(const p: TPath64;
  562. indentSpaces: integer; pointsPerRow: integer): string;
  563. var
  564. i, highI: Integer;
  565. spaces: string;
  566. begin
  567. spaces := StringOfChar(' ', indentSpaces);
  568. Result := spaces;
  569. highI := high(p);
  570. if highI < 0 then Exit;
  571. for i := 0 to highI -1 do
  572. begin
  573. Result := Result + format('%d,%d, ',[p[i].X,p[i].Y]);
  574. if (pointsPerRow > 0) and ((i + 1) mod pointsPerRow = 0) then
  575. Result := Result + #10 + spaces;
  576. end;
  577. Result := Result + format('%d,%d',[p[highI].X,p[highI].Y]);
  578. end;
  579. //------------------------------------------------------------------------------
  580. function PathToString(const p: TPathD; decimals: integer;
  581. indentSpaces: integer; pointsPerRow: integer): string;
  582. var
  583. i, highI: Integer;
  584. spaces: string;
  585. begin
  586. spaces := StringOfChar(' ', indentSpaces);
  587. Result := '';
  588. highI := high(p);
  589. if highI < 0 then Exit;
  590. for i := 0 to highI -1 do
  591. Result := Result + format('%1.*n,%1.*n, ',
  592. [decimals, p[i].X, decimals, p[i].Y]);
  593. Result := Result + format('%1.*n,%1.*n',[
  594. decimals, p[highI].X, decimals, p[highI].Y]);
  595. end;
  596. //------------------------------------------------------------------------------
  597. function PathsToString(const p: TPaths64;
  598. indentSpaces: integer = 0; pointsPerRow: integer = 0): string;
  599. var
  600. i: integer;
  601. begin
  602. Result := '';
  603. for i := 0 to High(p) do
  604. Result := Result + PathToString(p[i], indentSpaces, pointsPerRow) + #10#10;
  605. end;
  606. //------------------------------------------------------------------------------
  607. function PathsToString(const p: TPathsD; decimals: integer;
  608. indentSpaces: integer = 0; pointsPerRow: integer = 0): string;
  609. var
  610. i: integer;
  611. begin
  612. Result := '';
  613. for i := 0 to High(p) do
  614. Result := Result + PathToString(p[i], indentSpaces, pointsPerRow) + #10#10;
  615. end;
  616. //------------------------------------------------------------------------------
  617. procedure ShowPolyPathStructure64(pp: TPolyPath64; level: integer;
  618. strings: TStrings);
  619. var
  620. i: integer;
  621. spaces, plural: string;
  622. begin
  623. spaces := StringOfChar(' ', level * 2);
  624. if pp.Count = 1 then plural := '' else plural := 's';
  625. if pp.IsHole then
  626. strings.Add(Format('%sA hole containing %d polygon%s', [spaces, pp.Count, plural]))
  627. else
  628. strings.Add(Format('%sA polygon containing %d hole%s', [spaces, pp.Count, plural]));
  629. for i := 0 to pp.Count -1 do
  630. if pp.child[i].Count> 0 then
  631. ShowPolyPathStructure64(pp.child[i], level + 1, strings);
  632. end;
  633. //------------------------------------------------------------------------------
  634. procedure ShowPolyTreeStructure(polytree: TPolyTree64; strings: TStrings);
  635. var
  636. i: integer;
  637. begin
  638. if polytree.Count = 1 then
  639. strings.Add('Polytree with just 1 polygon.') else
  640. strings.Add(Format('Polytree with just %d polygons.', [polytree.Count]));
  641. for i := 0 to polytree.Count -1 do
  642. if polytree[i].Count > 0 then
  643. ShowPolyPathStructure64(polytree[i], 1, strings);
  644. end;
  645. //------------------------------------------------------------------------------
  646. procedure ShowPolyPathStructureD(pp: TPolyPathD; level: integer; strings: TStrings);
  647. var
  648. i: integer;
  649. spaces, plural: string;
  650. begin
  651. spaces := StringOfChar(' ', level * 2);
  652. if pp.Count = 1 then plural := '' else plural := 's';
  653. if pp.IsHole then
  654. strings.Add(Format('%sA hole containing %d polygon%s', [spaces, pp.Count, plural]))
  655. else
  656. strings.Add(Format('%sA polygon containing %d hole%s', [spaces, pp.Count, plural]));
  657. for i := 0 to pp.Count -1 do
  658. if pp.child[i].Count> 0 then
  659. ShowPolyPathStructureD(pp.child[i], level + 1, strings);
  660. end;
  661. //------------------------------------------------------------------------------
  662. procedure ShowPolyTreeStructure(polytree: TPolyTreeD; strings: TStrings);
  663. var
  664. i: integer;
  665. begin
  666. if polytree.Count = 1 then
  667. strings.Add('Polytree with just 1 polygon.') else
  668. strings.Add(Format('Polytree with just %d polygons.', [polytree.Count]));
  669. for i := 0 to polytree.Count -1 do
  670. if polytree[i].Count > 0 then
  671. ShowPolyPathStructureD(polytree[i], 1, strings);
  672. end;
  673. //------------------------------------------------------------------------------
  674. function TrimCollinear(const p: TPath64; isOpenPath: Boolean = false): TPath64;
  675. var
  676. i,j, len: integer;
  677. begin
  678. len := Length(p);
  679. i := 0;
  680. if not isOpenPath then
  681. begin
  682. while (i < len -1) and
  683. IsCollinear(p[len -1], p[i], p[i+1]) do inc(i);
  684. while (i < len -1) and
  685. IsCollinear(p[len -2], p[len -1], p[i]) do dec(len);
  686. end;
  687. if (len - i < 3) then
  688. begin
  689. if not isOpenPath or (len < 2) or PointsEqual(p[0], p[1]) then
  690. Result := nil else
  691. Result := p;
  692. Exit;
  693. end;
  694. SetLength(Result, len -i);
  695. Result[0] := p[i];
  696. j := 0;
  697. for i := i+1 to len -2 do
  698. if not IsCollinear(result[j], p[i], p[i+1]) then
  699. begin
  700. inc(j);
  701. result[j] := p[i];
  702. end;
  703. if isOpenPath then
  704. begin
  705. inc(j);
  706. result[j] := p[len-1];
  707. end
  708. else if not IsCollinear(result[j], p[len-1], result[0]) then
  709. begin
  710. inc(j);
  711. result[j] := p[len-1];
  712. end else
  713. begin
  714. while (j > 1) and
  715. IsCollinear(result[j-1], result[j], result[0]) do dec(j);
  716. if j < 2 then j := -1;
  717. end;
  718. SetLength(Result, j +1);
  719. end;
  720. //------------------------------------------------------------------------------
  721. function TrimCollinear(const path: TPathD;
  722. precision: integer; isOpenPath: Boolean = false): TPathD;
  723. var
  724. p: TPath64;
  725. scale: double;
  726. begin
  727. scale := power(10, precision);
  728. p := ScalePath(path, scale);
  729. p := TrimCollinear(p, isOpenPath);
  730. Result := ScalePathD(p, 1/scale);
  731. end;
  732. //------------------------------------------------------------------------------
  733. function PointInPolygon(const pt: TPoint64;
  734. const polygon: TPath64): TPointInPolygonResult;
  735. begin
  736. Result := Clipper.Core.PointInPolygon(pt, polygon);
  737. end;
  738. //------------------------------------------------------------------------------
  739. function DistanceSqrd(const pt1, pt2: TPoint64): double;
  740. {$IFDEF INLINE} inline; {$ENDIF}
  741. var
  742. x1,y1,x2,y2: double;
  743. begin
  744. // nb: older versions of Delphi don't allow explicit typcasting
  745. x1 := pt1.X; y1 := pt1.Y;
  746. x2 := pt2.X; y2 := pt2.Y;
  747. result := Sqr(x1 - x2) + Sqr(y1 - y2);
  748. end;
  749. //------------------------------------------------------------------------------
  750. function PerpendicDistSqrd(const pt, line1, line2: TPoint64): double;
  751. {$IFDEF INLINE} inline; {$ENDIF}
  752. var
  753. a,b,c,d: double;
  754. begin
  755. a := pt.X - line1.X;
  756. b := pt.Y - line1.Y;
  757. c := line2.X - line1.X;
  758. d := line2.Y - line1.Y;
  759. result := Iif((c = 0) and (d = 0),
  760. 0, Sqr(a * d - c * b) / (c * c + d * d));
  761. end;
  762. //------------------------------------------------------------------------------
  763. //------------------------------------------------------------------------------
  764. type
  765. PSimplifyRec = ^TSimplifyRec;
  766. TSimplifyRec = record
  767. pt : TPoint64;
  768. pdSqrd : double;
  769. prev : PSimplifyRec;
  770. next : PSimplifyRec;
  771. //isEnd : Boolean;
  772. end;
  773. function SimplifyPath(const path: TPath64;
  774. shapeTolerance: double; isClosedPath: Boolean): TPath64;
  775. var
  776. i, highI, minHigh: integer;
  777. tolSqrd: double;
  778. srArray: array of TSimplifyRec;
  779. first, last: PSimplifyRec;
  780. begin
  781. Result := nil;
  782. highI := High(path);
  783. minHigh := Iif(isClosedPath, 2, 1);
  784. if highI < minHigh then Exit;
  785. SetLength(srArray, highI +1);
  786. with srArray[0] do
  787. begin
  788. pt := path[0];
  789. prev := @srArray[highI];
  790. next := @srArray[1];
  791. pdSqrd := Iif(isClosedPath,
  792. PerpendicDistSqrd(path[0], path[highI], path[1]), invalidD);
  793. end;
  794. with srArray[highI] do
  795. begin
  796. pt := path[highI];
  797. prev := @srArray[highI-1];
  798. next := @srArray[0];
  799. pdSqrd := Iif(isClosedPath,
  800. PerpendicDistSqrd(path[highI], path[highI-1], path[0]), invalidD);
  801. end;
  802. for i := 1 to highI -1 do
  803. with srArray[i] do
  804. begin
  805. pt := path[i];
  806. prev := @srArray[i-1];
  807. next := @srArray[i+1];
  808. pdSqrd := PerpendicDistSqrd(path[i], path[i-1], path[i+1]);
  809. end;
  810. first := @srArray[0];
  811. last := first.prev;
  812. tolSqrd := Sqr(shapeTolerance);
  813. while first <> last do
  814. begin
  815. if (first.pdSqrd > tolSqrd) or
  816. (first.next.pdSqrd < first.pdSqrd) then
  817. begin
  818. first := first.next;
  819. Continue;
  820. end;
  821. dec(highI);
  822. first.prev.next := first.next;
  823. first.next.prev := first.prev;
  824. last := first.prev;
  825. first := last.next;
  826. if first.next = first.prev then break;
  827. last.pdSqrd := PerpendicDistSqrd(last.pt, last.prev.pt, first.pt);
  828. first.pdSqrd := PerpendicDistSqrd(first.pt, last.pt, first.next.pt);
  829. end;
  830. if highI < minHigh then Exit;
  831. if not isClosedPath then first := @srArray[0];
  832. SetLength(Result, highI +1);
  833. for i := 0 to HighI do
  834. begin
  835. Result[i] := first.pt;
  836. first := first.next;
  837. end;
  838. end;
  839. //------------------------------------------------------------------------------
  840. function SimplifyPaths(const paths: TPaths64;
  841. shapeTolerance: double; isClosedPath: Boolean): TPaths64;
  842. var
  843. i, len: integer;
  844. begin
  845. len := Length(paths);
  846. SetLength(Result, len);
  847. for i := 0 to len -1 do
  848. result[i] := SimplifyPath(paths[i], shapeTolerance, isClosedPath);
  849. end;
  850. //------------------------------------------------------------------------------
  851. function SimplifyPath(const path: TPathD; shapeTolerance: double;
  852. isClosedPath: Boolean; decimalPrecision: integer): TPathD;
  853. var
  854. p: TPath64;
  855. scale: double;
  856. begin
  857. scale := power(10, decimalPrecision);
  858. p := ScalePath(path, scale);
  859. p := SimplifyPath(p, shapeTolerance, isClosedPath);
  860. Result := ScalePathD(p, 1/scale);
  861. end;
  862. //------------------------------------------------------------------------------
  863. function SimplifyPaths(const paths: TPathsD; shapeTolerance: double;
  864. isClosedPath: Boolean; decimalPrecision: integer): TPathsD;
  865. var
  866. pp: TPaths64;
  867. scale: double;
  868. begin
  869. scale := power(10, decimalPrecision);
  870. pp := ScalePaths(paths, scale);
  871. pp := SimplifyPaths(pp, shapeTolerance, isClosedPath);
  872. Result := ScalePathsD(pp, 1/scale);
  873. end;
  874. //------------------------------------------------------------------------------
  875. end.