| 9 | double constexpr kEps = 1e-5; |
| 10 | |
| 11 | void SmoothGeneric(m2::PointD const & pt0, m2::PointD const & pt1, m2::PointD const & pt2, m2::PointD const & pt3, |
| 12 | double alpha, size_t pointsCount, std::vector<m2::PointD> & path) |
| 13 | { |
| 14 | if (pt0.EqualDxDy(pt1, kEps) || pt1.EqualDxDy(pt2, kEps) || pt2.EqualDxDy(pt3, kEps)) |
| 15 | return; |
| 16 | |
| 17 | auto const calcNextT = [alpha](double prevT, m2::PointD const & prevP, m2::PointD const & nextP) |
| 18 | { |
| 19 | auto const dx = nextP.x - prevP.x; |
| 20 | auto const dy = nextP.y - prevP.y; |
| 21 | return pow(dx * dx + dy * dy, alpha * 0.5) + prevT; |
| 22 | }; |
| 23 | |
| 24 | double const t0 = 0.0; |
| 25 | double const t1 = calcNextT(t0, pt0, pt1); |
| 26 | double const t2 = calcNextT(t1, pt1, pt2); |
| 27 | double const t3 = calcNextT(t2, pt2, pt3); |
| 28 | |
| 29 | size_t partsCount = pointsCount + 1; |
| 30 | for (size_t i = 1; i < partsCount; ++i) |
| 31 | { |
| 32 | double const t = t1 + (t2 - t1) * i / partsCount; |
| 33 | |
| 34 | auto const a1 = pt0 * (t1 - t) / (t1 - t0) + pt1 * (t - t0) / (t1 - t0); |
| 35 | auto const a2 = pt1 * (t2 - t) / (t2 - t1) + pt2 * (t - t1) / (t2 - t1); |
| 36 | auto const a3 = pt2 * (t3 - t) / (t3 - t2) + pt3 * (t - t2) / (t3 - t2); |
| 37 | |
| 38 | auto const b1 = a1 * (t2 - t) / (t2 - t0) + a2 * (t - t0) / (t2 - t0); |
| 39 | auto const b2 = a2 * (t3 - t) / (t3 - t1) + a3 * (t - t1) / (t3 - t1); |
| 40 | |
| 41 | auto const pt = b1 * (t2 - t) / (t2 - t1) + b2 * (t - t1) / (t2 - t1); |
| 42 | if (!path.back().EqualDxDy(pt, kEps) && !pt2.EqualDxDy(pt, kEps)) |
| 43 | path.push_back(pt); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // The same as SmoothGeneric but optimized for alpha == 0.0. |
| 48 | void SmoothUniform(m2::PointD const & pt0, m2::PointD const & pt1, m2::PointD const & pt2, m2::PointD const & pt3, |
no test coverage detected