File indexing completed on 2025-04-20 03:36:29
0001 /******************************************************************************* 0002 * Author : Angus Johnson * 0003 * Date : 16 July 2023 * 0004 * Website : http://www.angusj.com * 0005 * Copyright : Angus Johnson 2010-2023 * 0006 * Purpose : This module provides a simple interface to the Clipper Library * 0007 * License : http://www.boost.org/LICENSE_1_0.txt * 0008 *******************************************************************************/ 0009 0010 #ifndef CLIPPER_H 0011 #define CLIPPER_H 0012 0013 #include <cstdlib> 0014 #include <type_traits> 0015 #include <vector> 0016 0017 #include "clipper.core.h" 0018 #include "clipper.engine.h" 0019 #include "clipper.offset.h" 0020 #include "clipper.minkowski.h" 0021 #include "clipper.rectclip.h" 0022 0023 namespace Clipper2Lib { 0024 0025 inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule, 0026 const Paths64& subjects, const Paths64& clips) 0027 { 0028 Paths64 result; 0029 Clipper64 clipper; 0030 clipper.AddSubject(subjects); 0031 clipper.AddClip(clips); 0032 clipper.Execute(cliptype, fillrule, result); 0033 return result; 0034 } 0035 0036 inline void BooleanOp(ClipType cliptype, FillRule fillrule, 0037 const Paths64& subjects, const Paths64& clips, PolyTree64& solution) 0038 { 0039 Paths64 sol_open; 0040 Clipper64 clipper; 0041 clipper.AddSubject(subjects); 0042 clipper.AddClip(clips); 0043 clipper.Execute(cliptype, fillrule, solution, sol_open); 0044 } 0045 0046 inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule, 0047 const PathsD& subjects, const PathsD& clips, int precision = 2) 0048 { 0049 int error_code = 0; 0050 CheckPrecision(precision, error_code); 0051 PathsD result; 0052 if (error_code) return result; 0053 ClipperD clipper(precision); 0054 clipper.AddSubject(subjects); 0055 clipper.AddClip(clips); 0056 clipper.Execute(cliptype, fillrule, result); 0057 return result; 0058 } 0059 0060 inline void BooleanOp(ClipType cliptype, FillRule fillrule, 0061 const PathsD& subjects, const PathsD& clips, 0062 PolyTreeD& polytree, int precision = 2) 0063 { 0064 polytree.Clear(); 0065 int error_code = 0; 0066 CheckPrecision(precision, error_code); 0067 if (error_code) return; 0068 ClipperD clipper(precision); 0069 clipper.AddSubject(subjects); 0070 clipper.AddClip(clips); 0071 clipper.Execute(cliptype, fillrule, polytree); 0072 } 0073 0074 inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule) 0075 { 0076 return BooleanOp(ClipType::Intersection, fillrule, subjects, clips); 0077 } 0078 0079 inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) 0080 { 0081 return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec); 0082 } 0083 0084 inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule) 0085 { 0086 return BooleanOp(ClipType::Union, fillrule, subjects, clips); 0087 } 0088 0089 inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) 0090 { 0091 return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec); 0092 } 0093 0094 inline Paths64 Union(const Paths64& subjects, FillRule fillrule) 0095 { 0096 Paths64 result; 0097 Clipper64 clipper; 0098 clipper.AddSubject(subjects); 0099 clipper.Execute(ClipType::Union, fillrule, result); 0100 return result; 0101 } 0102 0103 inline PathsD Union(const PathsD& subjects, FillRule fillrule, int precision = 2) 0104 { 0105 PathsD result; 0106 int error_code = 0; 0107 CheckPrecision(precision, error_code); 0108 if (error_code) return result; 0109 ClipperD clipper(precision); 0110 clipper.AddSubject(subjects); 0111 clipper.Execute(ClipType::Union, fillrule, result); 0112 return result; 0113 } 0114 0115 inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule) 0116 { 0117 return BooleanOp(ClipType::Difference, fillrule, subjects, clips); 0118 } 0119 0120 inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) 0121 { 0122 return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec); 0123 } 0124 0125 inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule) 0126 { 0127 return BooleanOp(ClipType::Xor, fillrule, subjects, clips); 0128 } 0129 0130 inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) 0131 { 0132 return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec); 0133 } 0134 0135 inline Paths64 InflatePaths(const Paths64& paths, double delta, 0136 JoinType jt, EndType et, double miter_limit = 2.0, 0137 double arc_tolerance = 0.0) 0138 { 0139 if (!delta) return paths; 0140 ClipperOffset clip_offset(miter_limit, arc_tolerance); 0141 clip_offset.AddPaths(paths, jt, et); 0142 Paths64 solution; 0143 clip_offset.Execute(delta, solution); 0144 return solution; 0145 } 0146 0147 inline PathsD InflatePaths(const PathsD& paths, double delta, 0148 JoinType jt, EndType et, double miter_limit = 2.0, 0149 int precision = 2, double arc_tolerance = 0.0) 0150 { 0151 int error_code = 0; 0152 CheckPrecision(precision, error_code); 0153 if (!delta) return paths; 0154 if (error_code) return PathsD(); 0155 const double scale = std::pow(10, precision); 0156 ClipperOffset clip_offset(miter_limit, arc_tolerance); 0157 clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et); 0158 if (error_code) return PathsD(); 0159 Paths64 solution; 0160 clip_offset.Execute(delta * scale, solution); 0161 return ScalePaths<double, int64_t>(solution, 1 / scale, error_code); 0162 } 0163 0164 template <typename T> 0165 inline Path<T> TranslatePath(const Path<T>& path, T dx, T dy) 0166 { 0167 Path<T> result; 0168 result.reserve(path.size()); 0169 std::transform(path.begin(), path.end(), back_inserter(result), 0170 [dx, dy](const auto& pt) { return Point<T>(pt.x + dx, pt.y +dy); }); 0171 return result; 0172 } 0173 0174 inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy) 0175 { 0176 return TranslatePath<int64_t>(path, dx, dy); 0177 } 0178 0179 inline PathD TranslatePath(const PathD& path, double dx, double dy) 0180 { 0181 return TranslatePath<double>(path, dx, dy); 0182 } 0183 0184 template <typename T> 0185 inline Paths<T> TranslatePaths(const Paths<T>& paths, T dx, T dy) 0186 { 0187 Paths<T> result; 0188 result.reserve(paths.size()); 0189 std::transform(paths.begin(), paths.end(), back_inserter(result), 0190 [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); }); 0191 return result; 0192 } 0193 0194 inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy) 0195 { 0196 return TranslatePaths<int64_t>(paths, dx, dy); 0197 } 0198 0199 inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy) 0200 { 0201 return TranslatePaths<double>(paths, dx, dy); 0202 } 0203 0204 inline Paths64 RectClip(const Rect64& rect, const Paths64& paths) 0205 { 0206 if (rect.IsEmpty() || paths.empty()) return Paths64(); 0207 RectClip64 rc(rect); 0208 return rc.Execute(paths); 0209 } 0210 0211 inline Paths64 RectClip(const Rect64& rect, const Path64& path) 0212 { 0213 if (rect.IsEmpty() || path.empty()) return Paths64(); 0214 RectClip64 rc(rect); 0215 return rc.Execute(Paths64{ path }); 0216 } 0217 0218 inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2) 0219 { 0220 if (rect.IsEmpty() || paths.empty()) return PathsD(); 0221 int error_code = 0; 0222 CheckPrecision(precision, error_code); 0223 if (error_code) return PathsD(); 0224 const double scale = std::pow(10, precision); 0225 Rect64 r = ScaleRect<int64_t, double>(rect, scale); 0226 RectClip64 rc(r); 0227 Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code); 0228 if (error_code) return PathsD(); // ie: error_code result is lost 0229 return ScalePaths<double, int64_t>( 0230 rc.Execute(pp), 1 / scale, error_code); 0231 } 0232 0233 inline PathsD RectClip(const RectD& rect, const PathD& path, int precision = 2) 0234 { 0235 return RectClip(rect, PathsD{ path }, precision); 0236 } 0237 0238 inline Paths64 RectClipLines(const Rect64& rect, const Paths64& lines) 0239 { 0240 if (rect.IsEmpty() || lines.empty()) return Paths64(); 0241 RectClipLines64 rcl(rect); 0242 return rcl.Execute(lines); 0243 } 0244 0245 inline Paths64 RectClipLines(const Rect64& rect, const Path64& line) 0246 { 0247 return RectClipLines(rect, Paths64{ line }); 0248 } 0249 0250 inline PathsD RectClipLines(const RectD& rect, const PathsD& lines, int precision = 2) 0251 { 0252 if (rect.IsEmpty() || lines.empty()) return PathsD(); 0253 int error_code = 0; 0254 CheckPrecision(precision, error_code); 0255 if (error_code) return PathsD(); 0256 const double scale = std::pow(10, precision); 0257 Rect64 r = ScaleRect<int64_t, double>(rect, scale); 0258 RectClipLines64 rcl(r); 0259 Paths64 p = ScalePaths<int64_t, double>(lines, scale, error_code); 0260 if (error_code) return PathsD(); 0261 p = rcl.Execute(p); 0262 return ScalePaths<double, int64_t>(p, 1 / scale, error_code); 0263 } 0264 0265 inline PathsD RectClipLines(const RectD& rect, const PathD& line, int precision = 2) 0266 { 0267 return RectClipLines(rect, PathsD{ line }, precision); 0268 } 0269 0270 namespace details 0271 { 0272 0273 inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths) 0274 { 0275 paths.push_back(polypath.Polygon()); 0276 for (const auto& child : polypath) 0277 PolyPathToPaths64(*child, paths); 0278 } 0279 0280 inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths) 0281 { 0282 paths.push_back(polypath.Polygon()); 0283 for (const auto& child : polypath) 0284 PolyPathToPathsD(*child, paths); 0285 } 0286 0287 inline bool PolyPath64ContainsChildren(const PolyPath64& pp) 0288 { 0289 for (const auto& child : pp) 0290 { 0291 // return false if this child isn't fully contained by its parent 0292 0293 // checking for a single vertex outside is a bit too crude since 0294 // it doesn't account for rounding errors. It's better to check 0295 // for consecutive vertices found outside the parent's polygon. 0296 0297 int outsideCnt = 0; 0298 for (const Point64& pt : child->Polygon()) 0299 { 0300 PointInPolygonResult result = PointInPolygon(pt, pp.Polygon()); 0301 if (result == PointInPolygonResult::IsInside) --outsideCnt; 0302 else if (result == PointInPolygonResult::IsOutside) ++outsideCnt; 0303 if (outsideCnt > 1) return false; 0304 else if (outsideCnt < -1) break; 0305 } 0306 0307 // now check any nested children too 0308 if (child->Count() > 0 && !PolyPath64ContainsChildren(*child)) 0309 return false; 0310 } 0311 return true; 0312 } 0313 0314 static void OutlinePolyPath(std::ostream& os, 0315 size_t idx, bool isHole, size_t count, const std::string& preamble) 0316 { 0317 std::string plural = (count == 1) ? "." : "s."; 0318 if (isHole) 0319 os << preamble << "+- Hole (" << idx << ") contains " << count << 0320 " nested polygon" << plural << std::endl; 0321 else 0322 os << preamble << "+- Polygon (" << idx << ") contains " << count << 0323 " hole" << plural << std::endl; 0324 } 0325 0326 static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp, 0327 size_t idx, std::string preamble) 0328 { 0329 OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble); 0330 for (size_t i = 0; i < pp.Count(); ++i) 0331 if (pp.Child(i)->Count()) 0332 details::OutlinePolyPath64(os, *pp.Child(i), i, preamble + " "); 0333 } 0334 0335 static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp, 0336 size_t idx, std::string preamble) 0337 { 0338 OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble); 0339 for (size_t i = 0; i < pp.Count(); ++i) 0340 if (pp.Child(i)->Count()) 0341 details::OutlinePolyPathD(os, *pp.Child(i), i, preamble + " "); 0342 } 0343 0344 } // end details namespace 0345 0346 inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp) 0347 { 0348 std::string plural = (pp.Count() == 1) ? " polygon." : " polygons."; 0349 os << std::endl << "Polytree with " << pp.Count() << plural << std::endl; 0350 for (size_t i = 0; i < pp.Count(); ++i) 0351 if (pp.Child(i)->Count()) 0352 details::OutlinePolyPath64(os, *pp.Child(i), i, " "); 0353 os << std::endl << std::endl; 0354 return os; 0355 } 0356 0357 inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp) 0358 { 0359 std::string plural = (pp.Count() == 1) ? " polygon." : " polygons."; 0360 os << std::endl << "Polytree with " << pp.Count() << plural << std::endl; 0361 for (size_t i = 0; i < pp.Count(); ++i) 0362 if (pp.Child(i)->Count()) 0363 details::OutlinePolyPathD(os, *pp.Child(i), i, " "); 0364 os << std::endl << std::endl; 0365 if (!pp.Level()) os << std::endl; 0366 return os; 0367 } 0368 0369 inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree) 0370 { 0371 Paths64 result; 0372 for (const auto& child : polytree) 0373 details::PolyPathToPaths64(*child, result); 0374 return result; 0375 } 0376 0377 inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree) 0378 { 0379 PathsD result; 0380 for (const auto& child : polytree) 0381 details::PolyPathToPathsD(*child, result); 0382 return result; 0383 } 0384 0385 inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree) 0386 { 0387 for (const auto& child : polytree) 0388 if (child->Count() > 0 && 0389 !details::PolyPath64ContainsChildren(*child)) 0390 return false; 0391 return true; 0392 } 0393 0394 namespace details { 0395 0396 template<typename T, typename U> 0397 inline constexpr void MakePathGeneric(const T list, size_t size, 0398 std::vector<U>& result) 0399 { 0400 for (size_t i = 0; i < size; ++i) 0401 #ifdef USINGZ 0402 result[i / 2] = U{list[i], list[++i], 0}; 0403 #else 0404 result[i / 2] = U{list[i], list[++i]}; 0405 #endif 0406 } 0407 0408 } // end details namespace 0409 0410 template<typename T, 0411 typename std::enable_if< 0412 std::is_integral<T>::value && 0413 !std::is_same<char, T>::value, bool 0414 >::type = true> 0415 inline Path64 MakePath(const std::vector<T>& list) 0416 { 0417 const auto size = list.size() - list.size() % 2; 0418 if (list.size() != size) 0419 DoError(non_pair_error_i); // non-fatal without exception handling 0420 Path64 result(size / 2); // else ignores unpaired value 0421 details::MakePathGeneric(list, size, result); 0422 return result; 0423 } 0424 0425 template<typename T, std::size_t N, 0426 typename std::enable_if< 0427 std::is_integral<T>::value && 0428 !std::is_same<char, T>::value, bool 0429 >::type = true> 0430 inline Path64 MakePath(const T(&list)[N]) 0431 { 0432 // Make the compiler error on unpaired value (i.e. no runtime effects). 0433 static_assert(N % 2 == 0, "MakePath requires an even number of arguments"); 0434 Path64 result(N / 2); 0435 details::MakePathGeneric(list, N, result); 0436 return result; 0437 } 0438 0439 template<typename T, 0440 typename std::enable_if< 0441 std::is_arithmetic<T>::value && 0442 !std::is_same<char, T>::value, bool 0443 >::type = true> 0444 inline PathD MakePathD(const std::vector<T>& list) 0445 { 0446 const auto size = list.size() - list.size() % 2; 0447 if (list.size() != size) 0448 DoError(non_pair_error_i); // non-fatal without exception handling 0449 PathD result(size / 2); // else ignores unpaired value 0450 details::MakePathGeneric(list, size, result); 0451 return result; 0452 } 0453 0454 template<typename T, std::size_t N, 0455 typename std::enable_if< 0456 std::is_arithmetic<T>::value && 0457 !std::is_same<char, T>::value, bool 0458 >::type = true> 0459 inline PathD MakePathD(const T(&list)[N]) 0460 { 0461 // Make the compiler error on unpaired value (i.e. no runtime effects). 0462 static_assert(N % 2 == 0, "MakePath requires an even number of arguments"); 0463 PathD result(N / 2); 0464 details::MakePathGeneric(list, N, result); 0465 return result; 0466 } 0467 0468 #ifdef USINGZ 0469 template<typename T2, std::size_t N> 0470 inline Path64 MakePathZ(const T2(&list)[N]) 0471 { 0472 static_assert(N % 3 == 0 && std::numeric_limits<T2>::is_integer, 0473 "MakePathZ requires integer values in multiples of 3"); 0474 std::size_t size = N / 3; 0475 Path64 result(size); 0476 for (size_t i = 0; i < size; ++i) 0477 result[i] = Point64(list[i * 3], 0478 list[i * 3 + 1], list[i * 3 + 2]); 0479 return result; 0480 } 0481 0482 template<typename T2, std::size_t N> 0483 inline PathD MakePathZD(const T2(&list)[N]) 0484 { 0485 static_assert(N % 3 == 0, 0486 "MakePathZD requires values in multiples of 3"); 0487 std::size_t size = N / 3; 0488 PathD result(size); 0489 if constexpr (std::numeric_limits<T2>::is_integer) 0490 for (size_t i = 0; i < size; ++i) 0491 result[i] = PointD(list[i * 3], 0492 list[i * 3 + 1], list[i * 3 + 2]); 0493 else 0494 for (size_t i = 0; i < size; ++i) 0495 result[i] = PointD(list[i * 3], list[i * 3 + 1], 0496 static_cast<int64_t>(list[i * 3 + 2])); 0497 return result; 0498 } 0499 #endif 0500 0501 inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false) 0502 { 0503 size_t len = p.size(); 0504 if (len < 3) 0505 { 0506 if (!is_open_path || len < 2 || p[0] == p[1]) return Path64(); 0507 else return p; 0508 } 0509 0510 Path64 dst; 0511 dst.reserve(len); 0512 Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1; 0513 0514 if (!is_open_path) 0515 { 0516 while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1))) 0517 ++srcIt; 0518 while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt)) 0519 --stop; 0520 if (srcIt == stop) return Path64(); 0521 } 0522 0523 prevIt = srcIt++; 0524 dst.push_back(*prevIt); 0525 for (; srcIt != stop; ++srcIt) 0526 { 0527 if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1))) 0528 { 0529 prevIt = srcIt; 0530 dst.push_back(*prevIt); 0531 } 0532 } 0533 0534 if (is_open_path) 0535 dst.push_back(*srcIt); 0536 else if (CrossProduct(*prevIt, *stop, dst[0])) 0537 dst.push_back(*stop); 0538 else 0539 { 0540 while (dst.size() > 2 && 0541 !CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0])) 0542 dst.pop_back(); 0543 if (dst.size() < 3) return Path64(); 0544 } 0545 return dst; 0546 } 0547 0548 inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false) 0549 { 0550 int error_code = 0; 0551 CheckPrecision(precision, error_code); 0552 if (error_code) return PathD(); 0553 const double scale = std::pow(10, precision); 0554 Path64 p = ScalePath<int64_t, double>(path, scale, error_code); 0555 if (error_code) return PathD(); 0556 p = TrimCollinear(p, is_open_path); 0557 return ScalePath<double, int64_t>(p, 1/scale, error_code); 0558 } 0559 0560 template <typename T> 0561 inline double Distance(const Point<T> pt1, const Point<T> pt2) 0562 { 0563 return std::sqrt(DistanceSqr(pt1, pt2)); 0564 } 0565 0566 template <typename T> 0567 inline double Length(const Path<T>& path, bool is_closed_path = false) 0568 { 0569 double result = 0.0; 0570 if (path.size() < 2) return result; 0571 auto it = path.cbegin(), stop = path.end() - 1; 0572 for (; it != stop; ++it) 0573 result += Distance(*it, *(it + 1)); 0574 if (is_closed_path) 0575 result += Distance(*stop, *path.cbegin()); 0576 return result; 0577 } 0578 0579 0580 template <typename T> 0581 inline bool NearCollinear(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3, double sin_sqrd_min_angle_rads) 0582 { 0583 double cp = std::abs(CrossProduct(pt1, pt2, pt3)); 0584 return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads; 0585 } 0586 0587 template <typename T> 0588 inline Path<T> Ellipse(const Rect<T>& rect, int steps = 0) 0589 { 0590 return Ellipse(rect.MidPoint(), 0591 static_cast<double>(rect.Width()) *0.5, 0592 static_cast<double>(rect.Height()) * 0.5, steps); 0593 } 0594 0595 template <typename T> 0596 inline Path<T> Ellipse(const Point<T>& center, 0597 double radiusX, double radiusY = 0, int steps = 0) 0598 { 0599 if (radiusX <= 0) return Path<T>(); 0600 if (radiusY <= 0) radiusY = radiusX; 0601 if (steps <= 2) 0602 steps = static_cast<int>(PI * sqrt((radiusX + radiusY) / 2)); 0603 0604 double si = std::sin(2 * PI / steps); 0605 double co = std::cos(2 * PI / steps); 0606 double dx = co, dy = si; 0607 Path<T> result; 0608 result.reserve(steps); 0609 result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y))); 0610 for (int i = 1; i < steps; ++i) 0611 { 0612 result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy)); 0613 double x = dx * co - dy * si; 0614 dy = dy * co + dx * si; 0615 dx = x; 0616 } 0617 return result; 0618 } 0619 0620 template <typename T> 0621 inline double PerpendicDistFromLineSqrd(const Point<T>& pt, 0622 const Point<T>& line1, const Point<T>& line2) 0623 { 0624 double a = static_cast<double>(pt.x - line1.x); 0625 double b = static_cast<double>(pt.y - line1.y); 0626 double c = static_cast<double>(line2.x - line1.x); 0627 double d = static_cast<double>(line2.y - line1.y); 0628 if (c == 0 && d == 0) return 0; 0629 return Sqr(a * d - c * b) / (c * c + d * d); 0630 } 0631 0632 inline size_t GetNext(size_t current, size_t high, 0633 const std::vector<bool>& flags) 0634 { 0635 ++current; 0636 while (current <= high && flags[current]) ++current; 0637 if (current <= high) return current; 0638 current = 0; 0639 while (flags[current]) ++current; 0640 return current; 0641 } 0642 0643 inline size_t GetPrior(size_t current, size_t high, 0644 const std::vector<bool>& flags) 0645 { 0646 if (current == 0) current = high; 0647 else --current; 0648 while (current > 0 && flags[current]) --current; 0649 if (!flags[current]) return current; 0650 current = high; 0651 while (flags[current]) --current; 0652 return current; 0653 } 0654 0655 template <typename T> 0656 inline Path<T> SimplifyPath(const Path<T> path, 0657 double epsilon, bool isClosedPath = true) 0658 { 0659 const size_t len = path.size(), high = len -1; 0660 const double epsSqr = Sqr(epsilon); 0661 if (len < 4) return Path<T>(path); 0662 0663 std::vector<bool> flags(len); 0664 std::vector<double> distSqr(len); 0665 size_t prior = high, curr = 0, start, next, prior2, next2; 0666 if (isClosedPath) 0667 { 0668 distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]); 0669 distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]); 0670 } 0671 else 0672 { 0673 distSqr[0] = MAX_DBL; 0674 distSqr[high] = MAX_DBL; 0675 } 0676 for (size_t i = 1; i < high; ++i) 0677 distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]); 0678 0679 for (;;) 0680 { 0681 if (distSqr[curr] > epsSqr) 0682 { 0683 start = curr; 0684 do 0685 { 0686 curr = GetNext(curr, high, flags); 0687 } while (curr != start && distSqr[curr] > epsSqr); 0688 if (curr == start) break; 0689 } 0690 0691 prior = GetPrior(curr, high, flags); 0692 next = GetNext(curr, high, flags); 0693 if (next == prior) break; 0694 0695 if (distSqr[next] < distSqr[curr]) 0696 { 0697 flags[next] = true; 0698 next = GetNext(next, high, flags); 0699 next2 = GetNext(next, high, flags); 0700 distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]); 0701 if (next != high || isClosedPath) 0702 distSqr[next] = PerpendicDistFromLineSqrd(path[next], path[curr], path[next2]); 0703 curr = next; 0704 } 0705 else 0706 { 0707 flags[curr] = true; 0708 curr = next; 0709 next = GetNext(next, high, flags); 0710 prior2 = GetPrior(prior, high, flags); 0711 distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]); 0712 if (prior != 0 || isClosedPath) 0713 distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]); 0714 } 0715 } 0716 Path<T> result; 0717 result.reserve(len); 0718 for (typename Path<T>::size_type i = 0; i < len; ++i) 0719 if (!flags[i]) result.push_back(path[i]); 0720 return result; 0721 } 0722 0723 template <typename T> 0724 inline Paths<T> SimplifyPaths(const Paths<T> paths, 0725 double epsilon, bool isClosedPath = true) 0726 { 0727 Paths<T> result; 0728 result.reserve(paths.size()); 0729 for (const auto& path : paths) 0730 result.push_back(SimplifyPath(path, epsilon, isClosedPath)); 0731 return result; 0732 } 0733 0734 template <typename T> 0735 inline void RDP(const Path<T> path, std::size_t begin, 0736 std::size_t end, double epsSqrd, std::vector<bool>& flags) 0737 { 0738 typename Path<T>::size_type idx = 0; 0739 double max_d = 0; 0740 while (end > begin && path[begin] == path[end]) flags[end--] = false; 0741 for (typename Path<T>::size_type i = begin + 1; i < end; ++i) 0742 { 0743 // PerpendicDistFromLineSqrd - avoids expensive Sqrt() 0744 double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]); 0745 if (d <= max_d) continue; 0746 max_d = d; 0747 idx = i; 0748 } 0749 if (max_d <= epsSqrd) return; 0750 flags[idx] = true; 0751 if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags); 0752 if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags); 0753 } 0754 0755 template <typename T> 0756 inline Path<T> RamerDouglasPeucker(const Path<T>& path, double epsilon) 0757 { 0758 const typename Path<T>::size_type len = path.size(); 0759 if (len < 5) return Path<T>(path); 0760 std::vector<bool> flags(len); 0761 flags[0] = true; 0762 flags[len - 1] = true; 0763 RDP(path, 0, len - 1, Sqr(epsilon), flags); 0764 Path<T> result; 0765 result.reserve(len); 0766 for (typename Path<T>::size_type i = 0; i < len; ++i) 0767 if (flags[i]) 0768 result.push_back(path[i]); 0769 return result; 0770 } 0771 0772 template <typename T> 0773 inline Paths<T> RamerDouglasPeucker(const Paths<T>& paths, double epsilon) 0774 { 0775 Paths<T> result; 0776 result.reserve(paths.size()); 0777 std::transform(paths.begin(), paths.end(), back_inserter(result), 0778 [epsilon](const auto& path) 0779 { return RamerDouglasPeucker<T>(path, epsilon); }); 0780 return result; 0781 } 0782 0783 } // end Clipper2Lib namespace 0784 0785 #endif // CLIPPER_H