Clipper.cs 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360
  1. 
  2. #nullable enable
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Runtime.CompilerServices;
  7. namespace Clipper2Lib
  8. {
  9. public static class Clipper
  10. {
  11. private static Rect64 maxInvalidRect64 = new Rect64(
  12. long.MaxValue, long.MaxValue, long.MinValue, long.MinValue);
  13. private static RectD maxInvalidRectD = new RectD(
  14. double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue);
  15. public static Rect64 MaxInvalidRect64 => maxInvalidRect64;
  16. public static RectD MaxInvalidRectD => maxInvalidRectD;
  17. public static Paths64 Intersect(Paths64 subject, Paths64 clip, FillRule fillRule)
  18. {
  19. return BooleanOp(ClipType.Intersection, subject, clip, fillRule);
  20. }
  21. public static PathsD Intersect(PathsD subject, PathsD clip,
  22. FillRule fillRule, int precision = 2)
  23. {
  24. return BooleanOp(ClipType.Intersection,
  25. subject, clip, fillRule, precision);
  26. }
  27. public static Paths64 Union(List<PointF> subject, FillRule fillRule)
  28. {
  29. return BooleanOp(ClipType.Union, subject, fillRule);
  30. }
  31. public static Paths64 Union(Paths64 subject, FillRule fillRule)
  32. {
  33. return BooleanOp(ClipType.Union, subject, null, fillRule);
  34. }
  35. public static Paths64 Union(Paths64 subject, Paths64 clip, FillRule fillRule)
  36. {
  37. return BooleanOp(ClipType.Union, subject, clip, fillRule);
  38. }
  39. public static PathsD Union(PathsD subject, FillRule fillRule)
  40. {
  41. return BooleanOp(ClipType.Union, subject, null, fillRule);
  42. }
  43. public static PathsD Union(PathsD subject, PathsD clip,
  44. FillRule fillRule, int precision = 2)
  45. {
  46. return BooleanOp(ClipType.Union,
  47. subject, clip, fillRule, precision);
  48. }
  49. public static Paths64 Difference(Paths64 subject, Paths64 clip, FillRule fillRule)
  50. {
  51. return BooleanOp(ClipType.Difference, subject, clip, fillRule);
  52. }
  53. public static PathsD Difference(PathsD subject, PathsD clip,
  54. FillRule fillRule, int precision = 2)
  55. {
  56. return BooleanOp(ClipType.Difference,
  57. subject, clip, fillRule, precision);
  58. }
  59. public static Paths64 Xor(Paths64 subject, Paths64 clip, FillRule fillRule)
  60. {
  61. return BooleanOp(ClipType.Xor, subject, clip, fillRule);
  62. }
  63. public static PathsD Xor(PathsD subject, PathsD clip,
  64. FillRule fillRule, int precision = 2)
  65. {
  66. return BooleanOp(ClipType.Xor,
  67. subject, clip, fillRule, precision);
  68. }
  69. public static Paths64 BooleanOp(ClipType clipType, List<PointF> subject, FillRule fillRule)
  70. {
  71. var solution = new Paths64();
  72. var temp = new List<Int32>();
  73. subject.ForEach(p => { temp.Add((Int32)p.X); temp.Add((Int32)p.Y); });
  74. var sub = new Paths64();
  75. sub.Add(MakePath(temp.ToArray()));
  76. var c = new Clipper64();
  77. c.AddPaths(sub, PathType.Subject);
  78. c.Execute(clipType, fillRule, solution);
  79. return solution;
  80. }
  81. public static Paths64 BooleanOp(ClipType clipType, Paths64? subject, Paths64? clip, FillRule fillRule)
  82. {
  83. Paths64 solution = new Paths64();
  84. if (subject == null)
  85. {
  86. return solution;
  87. }
  88. Clipper64 c = new Clipper64();
  89. c.AddPaths(subject, PathType.Subject);
  90. if (clip != null)
  91. {
  92. c.AddPaths(clip, PathType.Clip);
  93. }
  94. c.Execute(clipType, fillRule, solution);
  95. return solution;
  96. }
  97. public static PathsD BooleanOp(ClipType clipType, PathsD subject, PathsD? clip,
  98. FillRule fillRule, int precision = 2)
  99. {
  100. PathsD solution = new PathsD();
  101. ClipperD c = new ClipperD(precision);
  102. c.AddSubject(subject);
  103. if (clip != null)
  104. {
  105. c.AddClip(clip);
  106. }
  107. c.Execute(clipType, fillRule, solution);
  108. return solution;
  109. }
  110. public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinType,
  111. EndType endType, double miterLimit = 2.0)
  112. {
  113. ClipperOffset co = new ClipperOffset(miterLimit);
  114. co.AddPaths(paths, joinType, endType);
  115. return co.Execute(delta);
  116. }
  117. public static PathsD InflatePaths(PathsD paths, double delta, JoinType joinType,
  118. EndType endType, double miterLimit = 2.0, int precision = 2)
  119. {
  120. InternalClipper.CheckPrecision(precision);
  121. double scale = Math.Pow(10, precision);
  122. Paths64 tmp = ScalePaths64(paths, scale);
  123. ClipperOffset co = new ClipperOffset(miterLimit);
  124. co.AddPaths(tmp, joinType, endType);
  125. tmp = co.Execute(delta * scale);
  126. return ScalePathsD(tmp, 1 / scale);
  127. }
  128. public static Path64 RectClip(Rect64 rect, Path64 path)
  129. {
  130. if (rect.IsEmpty() || path.Count == 0)
  131. {
  132. return new Path64();
  133. }
  134. RectClip rc = new RectClip(rect);
  135. return rc.ExecuteInternal(path);
  136. }
  137. public static Paths64 RectClip(Rect64 rect, Paths64 paths)
  138. {
  139. if (rect.IsEmpty() || paths.Count == 0)
  140. {
  141. return new Paths64();
  142. }
  143. Paths64 result = new Paths64(paths.Count);
  144. RectClip rc = new RectClip(rect);
  145. foreach (Path64 path in paths)
  146. {
  147. Rect64 pathRec = Clipper.GetBounds(path);
  148. if (!rect.Intersects(pathRec))
  149. {
  150. continue;
  151. }
  152. else if (rect.Contains(pathRec))
  153. {
  154. result.Add(path);
  155. }
  156. else
  157. {
  158. Path64 p = rc.ExecuteInternal(path);
  159. if (p.Count > 0)
  160. {
  161. result.Add(p);
  162. }
  163. }
  164. }
  165. return result;
  166. }
  167. public static PathD RectClip(RectD rect, PathD path, int precision = 2)
  168. {
  169. InternalClipper.CheckPrecision(precision);
  170. if (rect.IsEmpty() || path.Count == 0)
  171. {
  172. return new PathD();
  173. }
  174. double scale = Math.Pow(10, precision);
  175. Rect64 r = ScaleRect(rect, scale);
  176. Path64 tmpPath = ScalePath64(path, scale);
  177. RectClip rc = new RectClip(r);
  178. tmpPath = rc.ExecuteInternal(tmpPath);
  179. return ScalePathD(tmpPath, 1 / scale);
  180. }
  181. public static PathsD RectClip(RectD rect, PathsD paths, int precision = 2)
  182. {
  183. InternalClipper.CheckPrecision(precision);
  184. if (rect.IsEmpty() || paths.Count == 0)
  185. {
  186. return new PathsD();
  187. }
  188. double scale = Math.Pow(10, precision);
  189. Rect64 r = ScaleRect(rect, scale);
  190. RectClip rc = new RectClip(r);
  191. PathsD result = new PathsD(paths.Count);
  192. foreach (PathD p in paths)
  193. {
  194. RectD pathRec = Clipper.GetBounds(p);
  195. if (!rect.Intersects(pathRec))
  196. {
  197. continue;
  198. }
  199. else if (rect.Contains(pathRec))
  200. {
  201. result.Add(p);
  202. }
  203. else
  204. {
  205. Path64 p64 = ScalePath64(p, scale);
  206. p64 = rc.ExecuteInternal(p64);
  207. if (p64.Count > 0)
  208. {
  209. result.Add(ScalePathD(p64, 1 / scale));
  210. }
  211. }
  212. }
  213. return result;
  214. }
  215. public static Paths64 RectClipLines(Rect64 rect, Path64 path)
  216. {
  217. if (rect.IsEmpty() || path.Count == 0)
  218. {
  219. return new Paths64();
  220. }
  221. RectClipLines rco = new RectClipLines(rect);
  222. return rco.ExecuteInternal(path);
  223. }
  224. public static Paths64 RectClipLines(Rect64 rect, Paths64 paths)
  225. {
  226. Paths64 result = new Paths64(paths.Count);
  227. if (rect.IsEmpty() || paths.Count == 0)
  228. {
  229. return result;
  230. }
  231. RectClipLines rco = new RectClipLines(rect);
  232. foreach (Path64 path in paths)
  233. {
  234. Rect64 pathRec = Clipper.GetBounds(path);
  235. if (!rect.Intersects(pathRec))
  236. {
  237. continue;
  238. }
  239. else if (rect.Contains(pathRec))
  240. {
  241. result.Add(path);
  242. }
  243. else
  244. {
  245. Paths64 pp = rco.ExecuteInternal(path);
  246. if (pp.Count > 0)
  247. {
  248. result.AddRange(pp);
  249. }
  250. }
  251. }
  252. return result;
  253. }
  254. public static PathsD RectClipLines(RectD rect, PathD path, int precision = 2)
  255. {
  256. InternalClipper.CheckPrecision(precision);
  257. if (rect.IsEmpty() || path.Count == 0)
  258. {
  259. return new PathsD();
  260. }
  261. double scale = Math.Pow(10, precision);
  262. Rect64 r = ScaleRect(rect, scale);
  263. Path64 tmpPath = ScalePath64(path, scale);
  264. RectClipLines rco = new RectClipLines(r);
  265. Paths64 tmpPaths = rco.ExecuteInternal(tmpPath);
  266. return ScalePathsD(tmpPaths, 1 / scale);
  267. }
  268. public static PathsD RectClipLines(RectD rect, PathsD paths, int precision = 2)
  269. {
  270. InternalClipper.CheckPrecision(precision);
  271. PathsD result = new PathsD(paths.Count);
  272. if (rect.IsEmpty() || paths.Count == 0)
  273. {
  274. return result;
  275. }
  276. double scale = Math.Pow(10, precision);
  277. Rect64 r = ScaleRect(rect, scale);
  278. RectClipLines rco = new RectClipLines(r);
  279. foreach (PathD p in paths)
  280. {
  281. RectD pathRec = Clipper.GetBounds(p);
  282. if (!rect.Intersects(pathRec))
  283. {
  284. continue;
  285. }
  286. else if (rect.Contains(pathRec))
  287. {
  288. result.Add(p);
  289. }
  290. else
  291. {
  292. Path64 p64 = ScalePath64(p, scale);
  293. Paths64 pp64 = rco.ExecuteInternal(p64);
  294. if (pp64.Count == 0)
  295. {
  296. continue;
  297. }
  298. PathsD ppd = ScalePathsD(pp64, 1 / scale);
  299. result.AddRange(ppd);
  300. }
  301. }
  302. return result;
  303. }
  304. public static Paths64 MinkowskiSum(Path64 pattern, Path64 path, bool isClosed)
  305. {
  306. return Minkowski.Sum(pattern, path, isClosed);
  307. }
  308. public static Paths64 MinkowskiDiff(Path64 pattern, Path64 path, bool isClosed)
  309. {
  310. return Minkowski.Diff(pattern, path, isClosed);
  311. }
  312. public static double Area(Path64 path)
  313. {
  314. // https://en.wikipedia.org/wiki/Shoelace_formula
  315. double a = 0.0;
  316. int cnt = path.Count;
  317. if (cnt < 3)
  318. {
  319. return 0.0;
  320. }
  321. Point64 prevPt = path[cnt - 1];
  322. foreach (Point64 pt in path)
  323. {
  324. a += (double)(prevPt.Y + pt.Y) * (prevPt.X - pt.X);
  325. prevPt = pt;
  326. }
  327. return a * 0.5;
  328. }
  329. public static double Area(Paths64 paths)
  330. {
  331. double a = 0.0;
  332. foreach (Path64 path in paths)
  333. {
  334. a += Area(path);
  335. }
  336. return a;
  337. }
  338. public static double Area(PathD path)
  339. {
  340. double a = 0.0;
  341. int cnt = path.Count;
  342. if (cnt < 3)
  343. {
  344. return 0.0;
  345. }
  346. PointD prevPt = path[cnt - 1];
  347. foreach (PointD pt in path)
  348. {
  349. a += (prevPt.y + pt.y) * (prevPt.x - pt.x);
  350. prevPt = pt;
  351. }
  352. return a * 0.5;
  353. }
  354. public static double Area(PathsD paths)
  355. {
  356. double a = 0.0;
  357. foreach (PathD path in paths)
  358. {
  359. a += Area(path);
  360. }
  361. return a;
  362. }
  363. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  364. public static bool IsPositive(Path64 poly)
  365. {
  366. return Area(poly) >= 0;
  367. }
  368. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  369. public static bool IsPositive(PathD poly)
  370. {
  371. return Area(poly) >= 0;
  372. }
  373. public static string Path64ToString(Path64 path)
  374. {
  375. string result = "";
  376. foreach (Point64 pt in path)
  377. {
  378. result += pt.ToString();
  379. }
  380. return result + '\n';
  381. }
  382. public static string Paths64ToString(Paths64 paths)
  383. {
  384. string result = "";
  385. foreach (Path64 path in paths)
  386. {
  387. result += Path64ToString(path);
  388. }
  389. return result;
  390. }
  391. public static string PathDToString(PathD path)
  392. {
  393. string result = "";
  394. foreach (PointD pt in path)
  395. {
  396. result += pt.ToString();
  397. }
  398. return result + '\n';
  399. }
  400. public static string PathsDToString(PathsD paths)
  401. {
  402. string result = "";
  403. foreach (PathD path in paths)
  404. {
  405. result += PathDToString(path);
  406. }
  407. return result;
  408. }
  409. public static Path64 OffsetPath(Path64 path, long dx, long dy)
  410. {
  411. Path64 result = new Path64(path.Count);
  412. foreach (Point64 pt in path)
  413. {
  414. result.Add(new Point64(pt.X + dx, pt.Y + dy));
  415. }
  416. return result;
  417. }
  418. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  419. public static Point64 ScalePoint64(Point64 pt, double scale)
  420. {
  421. Point64 result = new Point64()
  422. {
  423. X = (long)(pt.X * scale),
  424. Y = (long)(pt.Y * scale),
  425. #if USINGZ
  426. Z = (long) (pt.Z),
  427. #endif
  428. };
  429. return result;
  430. }
  431. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  432. public static PointD ScalePointD(Point64 pt, double scale)
  433. {
  434. PointD result = new PointD()
  435. {
  436. x = pt.X * scale,
  437. y = pt.Y * scale,
  438. #if USINGZ
  439. z = pt.Z,
  440. #endif
  441. };
  442. return result;
  443. }
  444. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  445. public static Rect64 ScaleRect(RectD rec, double scale)
  446. {
  447. Rect64 result = new Rect64()
  448. {
  449. left = (long)(rec.left * scale),
  450. top = (long)(rec.top * scale),
  451. right = (long)(rec.right * scale),
  452. bottom = (long)(rec.bottom * scale)
  453. };
  454. return result;
  455. }
  456. public static Path64 ScalePath(Path64 path, double scale)
  457. {
  458. if (InternalClipper.IsAlmostZero(scale - 1))
  459. {
  460. return path;
  461. }
  462. Path64 result = new Path64(path.Count);
  463. #if USINGZ
  464. foreach (Point64 pt in path)
  465. result.Add(new Point64(pt.X * scale, pt.Y * scale, pt.Z));
  466. #else
  467. foreach (Point64 pt in path)
  468. {
  469. result.Add(new Point64(pt.X * scale, pt.Y * scale));
  470. }
  471. #endif
  472. return result;
  473. }
  474. public static Paths64 ScalePaths(Paths64 paths, double scale)
  475. {
  476. if (InternalClipper.IsAlmostZero(scale - 1))
  477. {
  478. return paths;
  479. }
  480. Paths64 result = new Paths64(paths.Count);
  481. foreach (Path64 path in paths)
  482. {
  483. result.Add(ScalePath(path, scale));
  484. }
  485. return result;
  486. }
  487. public static PathD ScalePath(PathD path, double scale)
  488. {
  489. if (InternalClipper.IsAlmostZero(scale - 1))
  490. {
  491. return path;
  492. }
  493. PathD result = new PathD(path.Count);
  494. foreach (PointD pt in path)
  495. {
  496. result.Add(new PointD(pt, scale));
  497. }
  498. return result;
  499. }
  500. public static PathsD ScalePaths(PathsD paths, double scale)
  501. {
  502. if (InternalClipper.IsAlmostZero(scale - 1))
  503. {
  504. return paths;
  505. }
  506. PathsD result = new PathsD(paths.Count);
  507. foreach (PathD path in paths)
  508. {
  509. result.Add(ScalePath(path, scale));
  510. }
  511. return result;
  512. }
  513. // Unlike ScalePath, both ScalePath64 & ScalePathD also involve type conversion
  514. public static Path64 ScalePath64(PathD path, double scale)
  515. {
  516. int cnt = path.Count;
  517. Path64 res = new Path64(cnt);
  518. foreach (PointD pt in path)
  519. {
  520. res.Add(new Point64(pt, scale));
  521. }
  522. return res;
  523. }
  524. public static Paths64 ScalePaths64(PathsD paths, double scale)
  525. {
  526. int cnt = paths.Count;
  527. Paths64 res = new Paths64(cnt);
  528. foreach (PathD path in paths)
  529. {
  530. res.Add(ScalePath64(path, scale));
  531. }
  532. return res;
  533. }
  534. public static PathD ScalePathD(Path64 path, double scale)
  535. {
  536. int cnt = path.Count;
  537. PathD res = new PathD(cnt);
  538. foreach (Point64 pt in path)
  539. {
  540. res.Add(new PointD(pt, scale));
  541. }
  542. return res;
  543. }
  544. public static PathsD ScalePathsD(Paths64 paths, double scale)
  545. {
  546. int cnt = paths.Count;
  547. PathsD res = new PathsD(cnt);
  548. foreach (Path64 path in paths)
  549. {
  550. res.Add(ScalePathD(path, scale));
  551. }
  552. return res;
  553. }
  554. // The static functions Path64 and PathD convert path types without scaling
  555. public static Path64 Path64(PathD path)
  556. {
  557. Path64 result = new Path64(path.Count);
  558. foreach (PointD pt in path)
  559. {
  560. result.Add(new Point64(pt));
  561. }
  562. return result;
  563. }
  564. public static Paths64 Paths64(PathsD paths)
  565. {
  566. Paths64 result = new Paths64(paths.Count);
  567. foreach (PathD path in paths)
  568. {
  569. result.Add(Path64(path));
  570. }
  571. return result;
  572. }
  573. public static PathsD PathsD(Paths64 paths)
  574. {
  575. PathsD result = new PathsD(paths.Count);
  576. foreach (Path64 path in paths)
  577. {
  578. result.Add(PathD(path));
  579. }
  580. return result;
  581. }
  582. public static PathD PathD(Path64 path)
  583. {
  584. PathD result = new PathD(path.Count);
  585. foreach (Point64 pt in path)
  586. {
  587. result.Add(new PointD(pt));
  588. }
  589. return result;
  590. }
  591. public static Path64 TranslatePath(Path64 path, long dx, long dy)
  592. {
  593. Path64 result = new Path64(path.Count);
  594. foreach (Point64 pt in path)
  595. {
  596. result.Add(new Point64(pt.X + dx, pt.Y + dy));
  597. }
  598. return result;
  599. }
  600. public static Paths64 TranslatePaths(Paths64 paths, long dx, long dy)
  601. {
  602. Paths64 result = new Paths64(paths.Count);
  603. foreach (Path64 path in paths)
  604. {
  605. result.Add(OffsetPath(path, dx, dy));
  606. }
  607. return result;
  608. }
  609. public static PathD TranslatePath(PathD path, double dx, double dy)
  610. {
  611. PathD result = new PathD(path.Count);
  612. foreach (PointD pt in path)
  613. {
  614. result.Add(new PointD(pt.x + dx, pt.y + dy));
  615. }
  616. return result;
  617. }
  618. public static PathsD TranslatePaths(PathsD paths, double dx, double dy)
  619. {
  620. PathsD result = new PathsD(paths.Count);
  621. foreach (PathD path in paths)
  622. {
  623. result.Add(TranslatePath(path, dx, dy));
  624. }
  625. return result;
  626. }
  627. public static Path64 ReversePath(Path64 path)
  628. {
  629. Path64 result = new Path64(path);
  630. result.Reverse();
  631. return result;
  632. }
  633. public static PathD ReversePath(PathD path)
  634. {
  635. PathD result = new PathD(path);
  636. result.Reverse();
  637. return result;
  638. }
  639. public static Paths64 ReversePaths(Paths64 paths)
  640. {
  641. Paths64 result = new Paths64(paths.Count);
  642. foreach (Path64 t in paths)
  643. {
  644. result.Add(ReversePath(t));
  645. }
  646. return result;
  647. }
  648. public static PathsD ReversePaths(PathsD paths)
  649. {
  650. PathsD result = new PathsD(paths.Count);
  651. foreach (PathD path in paths)
  652. {
  653. result.Add(ReversePath(path));
  654. }
  655. return result;
  656. }
  657. public static Rect64 GetBounds(Path64 path)
  658. {
  659. Rect64 result = MaxInvalidRect64;
  660. foreach (Point64 pt in path)
  661. {
  662. if (pt.X < result.left)
  663. {
  664. result.left = pt.X;
  665. }
  666. if (pt.X > result.right)
  667. {
  668. result.right = pt.X;
  669. }
  670. if (pt.Y < result.top)
  671. {
  672. result.top = pt.Y;
  673. }
  674. if (pt.Y > result.bottom)
  675. {
  676. result.bottom = pt.Y;
  677. }
  678. }
  679. return result.IsEmpty() ? new Rect64() : result;
  680. }
  681. public static Rect64 GetBounds(Paths64 paths)
  682. {
  683. Rect64 result = MaxInvalidRect64;
  684. foreach (Path64 path in paths)
  685. {
  686. foreach (Point64 pt in path)
  687. {
  688. if (pt.X < result.left)
  689. {
  690. result.left = pt.X;
  691. }
  692. if (pt.X > result.right)
  693. {
  694. result.right = pt.X;
  695. }
  696. if (pt.Y < result.top)
  697. {
  698. result.top = pt.Y;
  699. }
  700. if (pt.Y > result.bottom)
  701. {
  702. result.bottom = pt.Y;
  703. }
  704. }
  705. }
  706. return result.IsEmpty() ? new Rect64() : result;
  707. }
  708. public static RectD GetBounds(PathD path)
  709. {
  710. RectD result = MaxInvalidRectD;
  711. foreach (PointD pt in path)
  712. {
  713. if (pt.x < result.left)
  714. {
  715. result.left = pt.x;
  716. }
  717. if (pt.x > result.right)
  718. {
  719. result.right = pt.x;
  720. }
  721. if (pt.y < result.top)
  722. {
  723. result.top = pt.y;
  724. }
  725. if (pt.y > result.bottom)
  726. {
  727. result.bottom = pt.y;
  728. }
  729. }
  730. return result.IsEmpty() ? new RectD() : result;
  731. }
  732. public static RectD GetBounds(PathsD paths)
  733. {
  734. RectD result = MaxInvalidRectD;
  735. foreach (PathD path in paths)
  736. {
  737. foreach (PointD pt in path)
  738. {
  739. if (pt.x < result.left)
  740. {
  741. result.left = pt.x;
  742. }
  743. if (pt.x > result.right)
  744. {
  745. result.right = pt.x;
  746. }
  747. if (pt.y < result.top)
  748. {
  749. result.top = pt.y;
  750. }
  751. if (pt.y > result.bottom)
  752. {
  753. result.bottom = pt.y;
  754. }
  755. }
  756. }
  757. return result.IsEmpty() ? new RectD() : result;
  758. }
  759. public static Path64 MakePath(int[] arr)
  760. {
  761. int len = arr.Length / 2;
  762. Path64 p = new Path64(len);
  763. for (int i = 0; i < len; i++)
  764. {
  765. p.Add(new Point64(arr[i * 2], arr[i * 2 + 1]));
  766. }
  767. return p;
  768. }
  769. public static Path64 MakePath(long[] arr)
  770. {
  771. int len = arr.Length / 2;
  772. Path64 p = new Path64(len);
  773. for (int i = 0; i < len; i++)
  774. {
  775. p.Add(new Point64(arr[i * 2], arr[i * 2 + 1]));
  776. }
  777. return p;
  778. }
  779. public static PathD MakePath(double[] arr)
  780. {
  781. int len = arr.Length / 2;
  782. PathD p = new PathD(len);
  783. for (int i = 0; i < len; i++)
  784. {
  785. p.Add(new PointD(arr[i * 2], arr[i * 2 + 1]));
  786. }
  787. return p;
  788. }
  789. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  790. public static double Sqr(double value)
  791. {
  792. return value * value;
  793. }
  794. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  795. public static bool PointsNearEqual(PointD pt1, PointD pt2, double distanceSqrd)
  796. {
  797. return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y) < distanceSqrd;
  798. }
  799. public static PathD StripNearDuplicates(PathD path,
  800. double minEdgeLenSqrd, bool isClosedPath)
  801. {
  802. int cnt = path.Count;
  803. PathD result = new PathD(cnt);
  804. if (cnt == 0)
  805. {
  806. return result;
  807. }
  808. PointD lastPt = path[0];
  809. result.Add(lastPt);
  810. for (int i = 1; i < cnt; i++)
  811. {
  812. if (!PointsNearEqual(lastPt, path[i], minEdgeLenSqrd))
  813. {
  814. lastPt = path[i];
  815. result.Add(lastPt);
  816. }
  817. }
  818. if (isClosedPath && PointsNearEqual(lastPt, result[0], minEdgeLenSqrd))
  819. {
  820. result.RemoveAt(result.Count - 1);
  821. }
  822. return result;
  823. }
  824. public static Path64 StripDuplicates(Path64 path, bool isClosedPath)
  825. {
  826. int cnt = path.Count;
  827. Path64 result = new Path64(cnt);
  828. if (cnt == 0)
  829. {
  830. return result;
  831. }
  832. Point64 lastPt = path[0];
  833. result.Add(lastPt);
  834. for (int i = 1; i < cnt; i++)
  835. {
  836. if (lastPt != path[i])
  837. {
  838. lastPt = path[i];
  839. result.Add(lastPt);
  840. }
  841. }
  842. if (isClosedPath && lastPt == result[0])
  843. {
  844. result.RemoveAt(result.Count - 1);
  845. }
  846. return result;
  847. }
  848. private static void AddPolyNodeToPaths(PolyPath64 polyPath, Paths64 paths)
  849. {
  850. if (polyPath.Polygon!.Count > 0)
  851. {
  852. paths.Add(polyPath.Polygon);
  853. }
  854. for (int i = 0; i < polyPath.Count; i++)
  855. {
  856. AddPolyNodeToPaths((PolyPath64)polyPath._childs[i], paths);
  857. }
  858. }
  859. public static Paths64 PolyTreeToPaths64(PolyTree64 polyTree)
  860. {
  861. Paths64 result = new Paths64();
  862. for (int i = 0; i < polyTree.Count; i++)
  863. {
  864. AddPolyNodeToPaths((PolyPath64)polyTree._childs[i], result);
  865. }
  866. return result;
  867. }
  868. public static void AddPolyNodeToPathsD(PolyPathD polyPath, PathsD paths)
  869. {
  870. if (polyPath.Polygon!.Count > 0)
  871. {
  872. paths.Add(polyPath.Polygon);
  873. }
  874. for (int i = 0; i < polyPath.Count; i++)
  875. {
  876. AddPolyNodeToPathsD((PolyPathD)polyPath._childs[i], paths);
  877. }
  878. }
  879. public static PathsD PolyTreeToPathsD(PolyTreeD polyTree)
  880. {
  881. PathsD result = new PathsD();
  882. foreach (PolyPathD polyPathBase in polyTree)
  883. {
  884. PolyPathD p = polyPathBase;
  885. AddPolyNodeToPathsD(p, result);
  886. }
  887. return result;
  888. }
  889. public static double PerpendicDistFromLineSqrd(PointD pt, PointD line1, PointD line2)
  890. {
  891. double a = pt.x - line1.x;
  892. double b = pt.y - line1.y;
  893. double c = line2.x - line1.x;
  894. double d = line2.y - line1.y;
  895. if (c == 0 && d == 0)
  896. {
  897. return 0;
  898. }
  899. return Sqr(a * d - c * b) / (c * c + d * d);
  900. }
  901. public static double PerpendicDistFromLineSqrd(Point64 pt, Point64 line1, Point64 line2)
  902. {
  903. double a = (double)pt.X - line1.X;
  904. double b = (double)pt.Y - line1.Y;
  905. double c = (double)line2.X - line1.X;
  906. double d = (double)line2.Y - line1.Y;
  907. if (c == 0 && d == 0)
  908. {
  909. return 0;
  910. }
  911. return Sqr(a * d - c * b) / (c * c + d * d);
  912. }
  913. internal static void RDP(Path64 path, int begin, int end, double epsSqrd, List<bool> flags)
  914. {
  915. int idx = 0;
  916. double max_d = 0;
  917. while (end > begin && path[begin] == path[end])
  918. {
  919. flags[end--] = false;
  920. }
  921. for (int i = begin + 1; i < end; ++i)
  922. {
  923. // PerpendicDistFromLineSqrd - avoids expensive Sqrt()
  924. double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]);
  925. if (d <= max_d)
  926. {
  927. continue;
  928. }
  929. max_d = d;
  930. idx = i;
  931. }
  932. if (max_d <= epsSqrd)
  933. {
  934. return;
  935. }
  936. flags[idx] = true;
  937. if (idx > begin + 1)
  938. {
  939. RDP(path, begin, idx, epsSqrd, flags);
  940. }
  941. if (idx < end - 1)
  942. {
  943. RDP(path, idx, end, epsSqrd, flags);
  944. }
  945. }
  946. public static Path64 RamerDouglasPeucker(Path64 path, double epsilon)
  947. {
  948. int len = path.Count;
  949. if (len < 5)
  950. {
  951. return path;
  952. }
  953. List<bool> flags = new List<bool>(new bool[len]) { [0] = true, [len - 1] = true };
  954. RDP(path, 0, len - 1, Sqr(epsilon), flags);
  955. Path64 result = new Path64(len);
  956. for (int i = 0; i < len; ++i)
  957. {
  958. if (flags[i])
  959. {
  960. result.Add(path[i]);
  961. }
  962. }
  963. return result;
  964. }
  965. public static Paths64 RamerDouglasPeucker(Paths64 paths, double epsilon)
  966. {
  967. Paths64 result = new Paths64(paths.Count);
  968. foreach (Path64 path in paths)
  969. {
  970. result.Add(RamerDouglasPeucker(path, epsilon));
  971. }
  972. return result;
  973. }
  974. internal static void RDP(PathD path, int begin, int end, double epsSqrd, List<bool> flags)
  975. {
  976. int idx = 0;
  977. double max_d = 0;
  978. while (end > begin && path[begin] == path[end])
  979. {
  980. flags[end--] = false;
  981. }
  982. for (int i = begin + 1; i < end; ++i)
  983. {
  984. // PerpendicDistFromLineSqrd - avoids expensive Sqrt()
  985. double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]);
  986. if (d <= max_d)
  987. {
  988. continue;
  989. }
  990. max_d = d;
  991. idx = i;
  992. }
  993. if (max_d <= epsSqrd)
  994. {
  995. return;
  996. }
  997. flags[idx] = true;
  998. if (idx > begin + 1)
  999. {
  1000. RDP(path, begin, idx, epsSqrd, flags);
  1001. }
  1002. if (idx < end - 1)
  1003. {
  1004. RDP(path, idx, end, epsSqrd, flags);
  1005. }
  1006. }
  1007. public static PathD RamerDouglasPeucker(PathD path, double epsilon)
  1008. {
  1009. int len = path.Count;
  1010. if (len < 5)
  1011. {
  1012. return path;
  1013. }
  1014. List<bool> flags = new List<bool>(new bool[len]) { [0] = true, [len - 1] = true };
  1015. RDP(path, 0, len - 1, Sqr(epsilon), flags);
  1016. PathD result = new PathD(len);
  1017. for (int i = 0; i < len; ++i)
  1018. {
  1019. if (flags[i])
  1020. {
  1021. result.Add(path[i]);
  1022. }
  1023. }
  1024. return result;
  1025. }
  1026. public static PathsD RamerDouglasPeucker(PathsD paths, double epsilon)
  1027. {
  1028. PathsD result = new PathsD(paths.Count);
  1029. foreach (PathD path in paths)
  1030. {
  1031. result.Add(RamerDouglasPeucker(path, epsilon));
  1032. }
  1033. return result;
  1034. }
  1035. public static Path64 TrimCollinear(Path64 path, bool isOpen = false)
  1036. {
  1037. int len = path.Count;
  1038. int i = 0;
  1039. if (!isOpen)
  1040. {
  1041. while (i < len - 1 && InternalClipper.CrossProduct(
  1042. path[len - 1], path[i], path[i + 1]) == 0)
  1043. {
  1044. i++;
  1045. }
  1046. while (i < len - 1 && InternalClipper.CrossProduct(
  1047. path[len - 2], path[len - 1], path[i]) == 0)
  1048. {
  1049. len--;
  1050. }
  1051. }
  1052. if (len - i < 3)
  1053. {
  1054. if (!isOpen || len < 2 || path[0] == path[1])
  1055. {
  1056. return new Path64();
  1057. }
  1058. return path;
  1059. }
  1060. Path64 result = new Path64(len - i);
  1061. Point64 last = path[i];
  1062. result.Add(last);
  1063. for (i++; i < len - 1; i++)
  1064. {
  1065. if (InternalClipper.CrossProduct(
  1066. last, path[i], path[i + 1]) == 0)
  1067. {
  1068. continue;
  1069. }
  1070. last = path[i];
  1071. result.Add(last);
  1072. }
  1073. if (isOpen)
  1074. {
  1075. result.Add(path[len - 1]);
  1076. }
  1077. else if (InternalClipper.CrossProduct(
  1078. last, path[len - 1], result[0]) != 0)
  1079. {
  1080. result.Add(path[len - 1]);
  1081. }
  1082. else
  1083. {
  1084. while (result.Count > 2 && InternalClipper.CrossProduct(
  1085. result[result.Count - 1], result[result.Count - 2], result[0]) == 0)
  1086. {
  1087. result.RemoveAt(result.Count - 1);
  1088. }
  1089. if (result.Count < 3)
  1090. {
  1091. result.Clear();
  1092. }
  1093. }
  1094. return result;
  1095. }
  1096. public static PathD TrimCollinear(PathD path, int precision, bool isOpen = false)
  1097. {
  1098. InternalClipper.CheckPrecision(precision);
  1099. double scale = Math.Pow(10, precision);
  1100. Path64 p = ScalePath64(path, scale);
  1101. p = TrimCollinear(p, isOpen);
  1102. return ScalePathD(p, 1 / scale);
  1103. }
  1104. public static PointInPolygonResult PointInPolygon(Point64 pt, Path64 polygon)
  1105. {
  1106. return InternalClipper.PointInPolygon(pt, polygon);
  1107. }
  1108. public static PointInPolygonResult PointInPolygon(PointD pt,
  1109. PathD polygon, int precision = 2)
  1110. {
  1111. InternalClipper.CheckPrecision(precision);
  1112. double scale = Math.Pow(10, precision);
  1113. Point64 p = new Point64(pt, scale);
  1114. Path64 path = ScalePath64(polygon, scale);
  1115. return InternalClipper.PointInPolygon(p, path);
  1116. }
  1117. public static Path64 Ellipse(Point64 center,
  1118. double radiusX, double radiusY = 0, int steps = 0)
  1119. {
  1120. if (radiusX <= 0)
  1121. {
  1122. return new Path64();
  1123. }
  1124. if (radiusY <= 0)
  1125. {
  1126. radiusY = radiusX;
  1127. }
  1128. if (steps <= 2)
  1129. {
  1130. steps = (int)Math.Ceiling(Math.PI * Math.Sqrt((radiusX + radiusY) / 2));
  1131. }
  1132. double si = Math.Sin(2 * Math.PI / steps);
  1133. double co = Math.Cos(2 * Math.PI / steps);
  1134. double dx = co, dy = si;
  1135. Path64 result = new Path64(steps) { new Point64(center.X + radiusX, center.Y) };
  1136. for (int i = 1; i < steps; ++i)
  1137. {
  1138. result.Add(new Point64(center.X + radiusX * dx, center.Y + radiusY * dy));
  1139. double x = dx * co - dy * si;
  1140. dy = dy * co + dx * si;
  1141. dx = x;
  1142. }
  1143. return result;
  1144. }
  1145. public static PathD Ellipse(PointD center,
  1146. double radiusX, double radiusY = 0, int steps = 0)
  1147. {
  1148. if (radiusX <= 0)
  1149. {
  1150. return new PathD();
  1151. }
  1152. if (radiusY <= 0)
  1153. {
  1154. radiusY = radiusX;
  1155. }
  1156. if (steps <= 2)
  1157. {
  1158. steps = (int)Math.Ceiling(Math.PI * Math.Sqrt((radiusX + radiusY) / 2));
  1159. }
  1160. double si = Math.Sin(2 * Math.PI / steps);
  1161. double co = Math.Cos(2 * Math.PI / steps);
  1162. double dx = co, dy = si;
  1163. PathD result = new PathD(steps) { new PointD(center.x + radiusX, center.y) };
  1164. for (int i = 1; i < steps; ++i)
  1165. {
  1166. result.Add(new PointD(center.x + radiusX * dx, center.y + radiusY * dy));
  1167. double x = dx * co - dy * si;
  1168. dy = dy * co + dx * si;
  1169. dx = x;
  1170. }
  1171. return result;
  1172. }
  1173. } // Clipper
  1174. } // namespace