| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * @file | ||
| 3 | * @brief Bernstein-Bezier polynomial | ||
| 4 | *//* | ||
| 5 | * Authors: | ||
| 6 | * MenTaLguY <mental@rydia.net> | ||
| 7 | * Michael Sloan <mgsloan@gmail.com> | ||
| 8 | * Nathan Hurst <njh@njhurst.com> | ||
| 9 | * Krzysztof Kosiński <tweenk.pl@gmail.com> | ||
| 10 | * | ||
| 11 | * Copyright 2007-2015 Authors | ||
| 12 | * | ||
| 13 | * This library is free software; you can redistribute it and/or | ||
| 14 | * modify it either under the terms of the GNU Lesser General Public | ||
| 15 | * License version 2.1 as published by the Free Software Foundation | ||
| 16 | * (the "LGPL") or, at your option, under the terms of the Mozilla | ||
| 17 | * Public License Version 1.1 (the "MPL"). If you do not alter this | ||
| 18 | * notice, a recipient may use your version of this file under either | ||
| 19 | * the MPL or the LGPL. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the LGPL along with this library | ||
| 22 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 24 | * You should have received a copy of the MPL along with this library | ||
| 25 | * in the file COPYING-MPL-1.1 | ||
| 26 | * | ||
| 27 | * The contents of this file are subject to the Mozilla Public License | ||
| 28 | * Version 1.1 (the "License"); you may not use this file except in | ||
| 29 | * compliance with the License. You may obtain a copy of the License at | ||
| 30 | * http://www.mozilla.org/MPL/ | ||
| 31 | * | ||
| 32 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY | ||
| 33 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for | ||
| 34 | * the specific language governing rights and limitations. | ||
| 35 | * | ||
| 36 | */ | ||
| 37 | |||
| 38 | #include <2geom/bezier.h> | ||
| 39 | #include <2geom/solver.h> | ||
| 40 | #include <2geom/concepts.h> | ||
| 41 | #include <2geom/choose.h> | ||
| 42 | |||
| 43 | // Workaround for GCC null-analyser false positives. | ||
| 44 | // See https://gitlab.com/inkscape/lib2geom/-/issues/64 | ||
| 45 | #if defined(__GNUC__) && !defined(__clang__) | ||
| 46 | #pragma GCC diagnostic ignored "-Wnonnull" | ||
| 47 | #endif | ||
| 48 | |||
| 49 | namespace Geom { | ||
| 50 | |||
| 51 | 279 | std::vector<Coord> Bezier::valueAndDerivatives(Coord t, unsigned n_derivs) const { | |
| 52 | /* This is inelegant, as it uses several extra stores. I think there might be a way to | ||
| 53 | * evaluate roughly in situ. */ | ||
| 54 | |||
| 55 | // initialize return vector with zeroes, such that we only need to replace the non-zero derivs | ||
| 56 |
1/2✓ Branch 3 taken 279 times.
✗ Branch 4 not taken.
|
837 | std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0)); |
| 57 | |||
| 58 | // initialize temp storage variables | ||
| 59 |
2/4✓ Branch 2 taken 279 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 279 times.
✗ Branch 6 not taken.
|
279 | std::valarray<Coord> d_(order()+1); |
| 60 |
3/4✓ Branch 1 taken 901 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 622 times.
✓ Branch 4 taken 279 times.
|
901 | for(unsigned i = 0; i < size(); i++) { |
| 61 | 622 | d_[i] = c_[i]; | |
| 62 | } | ||
| 63 | |||
| 64 | 279 | unsigned nn = n_derivs + 1; | |
| 65 |
3/4✓ Branch 1 taken 279 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 263 times.
✓ Branch 4 taken 16 times.
|
279 | if(n_derivs > order()) { |
| 66 |
1/2✓ Branch 1 taken 263 times.
✗ Branch 2 not taken.
|
263 | nn = order()+1; // only calculate the non zero derivs |
| 67 | } | ||
| 68 |
2/2✓ Branch 0 taken 622 times.
✓ Branch 1 taken 279 times.
|
901 | for(unsigned di = 0; di < nn; di++) { |
| 69 | //val_n_der[di] = (casteljau_subdivision(t, &d_[0], NULL, NULL, order() - di)); | ||
| 70 |
1/2✓ Branch 1 taken 622 times.
✗ Branch 2 not taken.
|
622 | val_n_der[di] = bernstein_value_at(t, &d_[0], order() - di); |
| 71 |
3/4✓ Branch 1 taken 1046 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 424 times.
✓ Branch 4 taken 622 times.
|
1046 | for(unsigned i = 0; i < order() - di; i++) { |
| 72 |
1/2✓ Branch 1 taken 424 times.
✗ Branch 2 not taken.
|
424 | d_[i] = (order()-di)*(d_[i+1] - d_[i]); |
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | 558 | return val_n_der; | |
| 77 | 279 | } | |
| 78 | |||
| 79 | 62190 | void Bezier::subdivide(Coord t, Bezier *left, Bezier *right) const | |
| 80 | { | ||
| 81 |
1/2✓ Branch 0 taken 62190 times.
✗ Branch 1 not taken.
|
62190 | if (left) { |
| 82 | 62190 | left->c_.resize(size()); | |
| 83 |
1/2✓ Branch 0 taken 62190 times.
✗ Branch 1 not taken.
|
62190 | if (right) { |
| 84 | 62190 | right->c_.resize(size()); | |
| 85 | 124380 | casteljau_subdivision<double>(t, &c_[0], | |
| 86 | 62190 | &left->c_[0], &right->c_[0], order()); | |
| 87 | } else { | ||
| 88 | ✗ | casteljau_subdivision<double>(t, &c_[0], | |
| 89 | ✗ | &left->c_[0], nullptr, order()); | |
| 90 | } | ||
| 91 | ✗ | } else if (right) { | |
| 92 | ✗ | right->c_.resize(size()); | |
| 93 | ✗ | casteljau_subdivision<double>(t, &c_[0], | |
| 94 | ✗ | nullptr, &right->c_[0], order()); | |
| 95 | } | ||
| 96 | 62190 | } | |
| 97 | |||
| 98 | 62190 | std::pair<Bezier, Bezier> Bezier::subdivide(Coord t) const | |
| 99 | { | ||
| 100 | 62190 | std::pair<Bezier, Bezier> ret; | |
| 101 |
1/2✓ Branch 1 taken 62190 times.
✗ Branch 2 not taken.
|
62190 | subdivide(t, &ret.first, &ret.second); |
| 102 | 62190 | return ret; | |
| 103 | ✗ | } | |
| 104 | |||
| 105 | 67906 | std::vector<Coord> Bezier::roots() const | |
| 106 | { | ||
| 107 | 67906 | std::vector<Coord> solutions; | |
| 108 |
1/2✓ Branch 1 taken 67906 times.
✗ Branch 2 not taken.
|
67906 | find_bezier_roots(solutions, 0, 1); |
| 109 |
1/2✓ Branch 3 taken 67906 times.
✗ Branch 4 not taken.
|
67906 | std::sort(solutions.begin(), solutions.end()); |
| 110 | 67906 | return solutions; | |
| 111 | ✗ | } | |
| 112 | |||
| 113 | 22188 | std::vector<Coord> Bezier::roots(Interval const &ivl) const | |
| 114 | { | ||
| 115 | 22188 | std::vector<Coord> solutions; | |
| 116 |
2/4✓ Branch 3 taken 22188 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 22188 times.
✗ Branch 8 not taken.
|
22188 | find_bernstein_roots(&c_[0], order(), solutions, 0, ivl.min(), ivl.max()); |
| 117 |
1/2✓ Branch 3 taken 22188 times.
✗ Branch 4 not taken.
|
22188 | std::sort(solutions.begin(), solutions.end()); |
| 118 | 22188 | return solutions; | |
| 119 | ✗ | } | |
| 120 | |||
| 121 | 2 | Bezier Bezier::forward_difference(unsigned k) const | |
| 122 | { | ||
| 123 |
2/4✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
2 | Bezier fd(Order(order() - k)); |
| 124 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | int n = fd.size(); |
| 125 | |||
| 126 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
|
9 | for (int i = 0; i < n; i++) { |
| 127 | 7 | fd[i] = 0; | |
| 128 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
|
7 | int b = (i & 1) ? -1 : 1; // b = (-1)^j binomial(n, j - i) |
| 129 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 7 times.
|
23 | for (int j = i; j < n; j++) { |
| 130 | 16 | fd[i] += c_[j] * b; | |
| 131 | 16 | binomial_increment_k(b, n, j - i); | |
| 132 | 16 | b = -b; | |
| 133 | } | ||
| 134 | } | ||
| 135 | 2 | return fd; | |
| 136 | ✗ | } | |
| 137 | |||
| 138 | 88 | Bezier Bezier::elevate_degree() const | |
| 139 | { | ||
| 140 |
2/4✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 88 times.
✗ Branch 7 not taken.
|
88 | Bezier ed(Order(order()+1)); |
| 141 |
1/2✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
|
88 | unsigned n = size(); |
| 142 | 88 | ed[0] = c_[0]; | |
| 143 | 88 | ed[n] = c_[n-1]; | |
| 144 |
2/2✓ Branch 0 taken 536 times.
✓ Branch 1 taken 88 times.
|
624 | for(unsigned i = 1; i < n; i++) { |
| 145 | 536 | ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n); | |
| 146 | } | ||
| 147 | 88 | return ed; | |
| 148 | ✗ | } | |
| 149 | |||
| 150 | ✗ | Bezier Bezier::reduce_degree() const | |
| 151 | { | ||
| 152 | ✗ | if(order() == 0) return *this; | |
| 153 | ✗ | Bezier ed(Order(order()-1)); | |
| 154 | ✗ | unsigned n = size(); | |
| 155 | ✗ | ed[0] = c_[0]; | |
| 156 | ✗ | ed[n-1] = c_[n]; // ensure exact endpoints | |
| 157 | ✗ | unsigned middle = n/2; | |
| 158 | ✗ | for(unsigned i = 1; i < middle; i++) { | |
| 159 | ✗ | ed[i] = (n*c_[i] - i*ed[i-1])/(n-i); | |
| 160 | } | ||
| 161 | ✗ | for(unsigned i = n-1; i >= middle; i--) { | |
| 162 | ✗ | ed[i] = (n*c_[i] - i*ed[n-i])/(i); | |
| 163 | } | ||
| 164 | ✗ | return ed; | |
| 165 | ✗ | } | |
| 166 | |||
| 167 | 22 | Bezier Bezier::elevate_to_degree(unsigned newDegree) const | |
| 168 | { | ||
| 169 | 22 | Bezier ed = *this; | |
| 170 |
3/4✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 87 times.
✓ Branch 4 taken 22 times.
|
109 | for(unsigned i = degree(); i < newDegree; i++) { |
| 171 |
2/4✓ Branch 2 taken 87 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 87 times.
✗ Branch 6 not taken.
|
87 | ed = ed.elevate_degree(); |
| 172 | } | ||
| 173 | 22 | return ed; | |
| 174 | ✗ | } | |
| 175 | |||
| 176 | 40334 | Bezier Bezier::deflate() const | |
| 177 | { | ||
| 178 |
2/6✓ Branch 1 taken 40334 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 40334 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
40334 | if(order() == 0) return *this; |
| 179 |
1/2✓ Branch 1 taken 40334 times.
✗ Branch 2 not taken.
|
40334 | unsigned n = order(); |
| 180 |
1/2✓ Branch 4 taken 40334 times.
✗ Branch 5 not taken.
|
40334 | Bezier b(Order(n-1)); |
| 181 |
2/2✓ Branch 0 taken 327734 times.
✓ Branch 1 taken 40334 times.
|
368068 | for(unsigned i = 0; i < n; i++) { |
| 182 | 327734 | b[i] = (n*c_[i+1])/(i+1); | |
| 183 | } | ||
| 184 |
1/2✓ Branch 1 taken 40334 times.
✗ Branch 2 not taken.
|
40334 | return b; |
| 185 | 40334 | } | |
| 186 | |||
| 187 | 558 | SBasis Bezier::toSBasis() const | |
| 188 | { | ||
| 189 | 558 | SBasis sb; | |
| 190 |
1/2✓ Branch 1 taken 558 times.
✗ Branch 2 not taken.
|
558 | bezier_to_sbasis(sb, *this); |
| 191 | 558 | return sb; | |
| 192 | //return bezier_to_sbasis(&c_[0], order()); | ||
| 193 | ✗ | } | |
| 194 | |||
| 195 | 40096 | Bezier &Bezier::operator+=(Bezier const &other) | |
| 196 | { | ||
| 197 |
2/2✓ Branch 2 taken 8 times.
✓ Branch 3 taken 40088 times.
|
40096 | if (c_.size() > other.size()) { |
| 198 |
3/6✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
|
8 | c_ += other.elevate_to_degree(degree()).c_; |
| 199 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 40088 times.
|
40088 | } else if (c_.size() < other.size()) { |
| 200 | ✗ | *this = elevate_to_degree(other.degree()); | |
| 201 | ✗ | c_ += other.c_; | |
| 202 | } else { | ||
| 203 | 40088 | c_ += other.c_; | |
| 204 | } | ||
| 205 | 40096 | return *this; | |
| 206 | } | ||
| 207 | |||
| 208 | 18 | Bezier &Bezier::operator-=(Bezier const &other) | |
| 209 | { | ||
| 210 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
|
18 | if (c_.size() > other.size()) { |
| 211 | ✗ | c_ -= other.elevate_to_degree(degree()).c_; | |
| 212 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
|
18 | } else if (c_.size() < other.size()) { |
| 213 |
3/6✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 9 times.
✗ Branch 9 not taken.
|
9 | *this = elevate_to_degree(other.degree()); |
| 214 | 9 | c_ -= other.c_; | |
| 215 | } else { | ||
| 216 | 9 | c_ -= other.c_; | |
| 217 | } | ||
| 218 | 18 | return *this; | |
| 219 | } | ||
| 220 | |||
| 221 | 160 | Bezier operator*(Bezier const &f, Bezier const &g) | |
| 222 | { | ||
| 223 |
1/2✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
|
160 | int m = f.order(); |
| 224 |
1/2✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
|
160 | int n = g.order(); |
| 225 |
1/2✓ Branch 3 taken 160 times.
✗ Branch 4 not taken.
|
160 | Bezier h(Bezier::Order(m+n)); |
| 226 | // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k) | ||
| 227 | |||
| 228 | 160 | int mci = 1; | |
| 229 |
2/2✓ Branch 0 taken 563 times.
✓ Branch 1 taken 160 times.
|
723 | for (int i = 0; i <= m; i++) { |
| 230 | 563 | double const fi = mci * f[i]; | |
| 231 | |||
| 232 | 563 | int ncj = 1; | |
| 233 |
2/2✓ Branch 0 taken 1827 times.
✓ Branch 1 taken 563 times.
|
2390 | for (int j = 0; j <= n; j++) { |
| 234 | 1827 | h[i + j] += fi * ncj * g[j]; | |
| 235 | 1827 | binomial_increment_k(ncj, n, j); | |
| 236 | } | ||
| 237 | |||
| 238 | 563 | binomial_increment_k(mci, m, i); | |
| 239 | } | ||
| 240 | |||
| 241 | 160 | int mnck = 1; | |
| 242 |
2/2✓ Branch 0 taken 873 times.
✓ Branch 1 taken 160 times.
|
1033 | for (int k = 0; k <= m + n; k++) { |
| 243 | 873 | h[k] /= mnck; | |
| 244 | 873 | binomial_increment_k(mnck, m + n, k); | |
| 245 | } | ||
| 246 | |||
| 247 | 320 | return h; | |
| 248 | } | ||
| 249 | |||
| 250 | 40173 | Bezier portion(Bezier const &a, double from, double to) | |
| 251 | { | ||
| 252 | 40173 | Bezier ret(a); | |
| 253 | |||
| 254 | 40173 | bool reverse_result = false; | |
| 255 |
2/2✓ Branch 0 taken 20312 times.
✓ Branch 1 taken 19861 times.
|
40173 | if (from > to) { |
| 256 | 20312 | std::swap(from, to); | |
| 257 | 20312 | reverse_result = true; | |
| 258 | } | ||
| 259 | |||
| 260 | do { | ||
| 261 |
2/2✓ Branch 0 taken 106 times.
✓ Branch 1 taken 40067 times.
|
40173 | if (from == 0) { |
| 262 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 52 times.
|
106 | if (to == 1) { |
| 263 | 54 | break; | |
| 264 | } | ||
| 265 |
2/4✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 52 times.
✗ Branch 7 not taken.
|
52 | casteljau_subdivision<double>(to, &ret.c_[0], &ret.c_[0], nullptr, ret.order()); |
| 266 | 52 | break; | |
| 267 | } | ||
| 268 |
2/4✓ Branch 1 taken 40067 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 40067 times.
✗ Branch 7 not taken.
|
40067 | casteljau_subdivision<double>(from, &ret.c_[0], NULL, &ret.c_[0], ret.order()); |
| 269 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 40023 times.
|
40067 | if (to == 1) break; |
| 270 |
2/4✓ Branch 1 taken 40023 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 40023 times.
✗ Branch 7 not taken.
|
40023 | casteljau_subdivision<double>((to - from) / (1 - from), &ret.c_[0], &ret.c_[0], NULL, ret.order()); |
| 271 | // to protect against numerical inaccuracy in the above expression, we manually set | ||
| 272 | // the last coefficient to a value evaluated directly from the original polynomial | ||
| 273 |
2/4✓ Branch 1 taken 40023 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 40023 times.
✗ Branch 5 not taken.
|
40023 | ret.c_[ret.order()] = a.valueAt(to); |
| 274 | } while(0); | ||
| 275 | |||
| 276 |
2/2✓ Branch 0 taken 20312 times.
✓ Branch 1 taken 19861 times.
|
40173 | if (reverse_result) { |
| 277 |
1/2✓ Branch 4 taken 20312 times.
✗ Branch 5 not taken.
|
20312 | std::reverse(&ret.c_[0], &ret.c_[0] + ret.c_.size()); |
| 278 | } | ||
| 279 | 40173 | return ret; | |
| 280 | ✗ | } | |
| 281 | |||
| 282 | 22640 | Bezier derivative(Bezier const &a) | |
| 283 | { | ||
| 284 | //if(a.order() == 1) return Bezier(0.0); | ||
| 285 |
4/6✓ Branch 1 taken 22640 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 115 times.
✓ Branch 4 taken 22525 times.
✓ Branch 8 taken 115 times.
✗ Branch 9 not taken.
|
22640 | if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]); |
| 286 |
2/4✓ Branch 3 taken 22525 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 22525 times.
✗ Branch 8 not taken.
|
22525 | Bezier der(Bezier::Order(a.order()-1)); |
| 287 | |||
| 288 |
3/4✓ Branch 1 taken 198266 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 175741 times.
✓ Branch 4 taken 22525 times.
|
198266 | for(unsigned i = 0; i < a.order(); i++) { |
| 289 |
1/2✓ Branch 1 taken 175741 times.
✗ Branch 2 not taken.
|
175741 | der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]); |
| 290 | } | ||
| 291 |
1/2✓ Branch 1 taken 22525 times.
✗ Branch 2 not taken.
|
22525 | return der; |
| 292 | 22525 | } | |
| 293 | |||
| 294 | 1 | Bezier integral(Bezier const &a) | |
| 295 | { | ||
| 296 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | Bezier inte(Bezier::Order(a.order()+1)); |
| 297 | |||
| 298 | 1 | inte[0] = 0; | |
| 299 |
3/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1 times.
|
5 | for(unsigned i = 0; i < inte.order(); i++) { |
| 300 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | inte[i+1] = inte[i] + a[i]/(inte.order()); |
| 301 | } | ||
| 302 | 1 | return inte; | |
| 303 | ✗ | } | |
| 304 | |||
| 305 | 1797516 | OptInterval bounds_fast(Bezier const &b) | |
| 306 | { | ||
| 307 |
3/6✓ Branch 2 taken 1797516 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1797516 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1797516 times.
✗ Branch 10 not taken.
|
3595032 | return Interval::from_array(&b.c_[0], b.size()); |
| 308 | } | ||
| 309 | |||
| 310 | 318 | OptInterval bounds_exact(Bezier const &b) | |
| 311 | { | ||
| 312 |
2/4✓ Branch 1 taken 318 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 318 times.
✗ Branch 6 not taken.
|
318 | OptInterval ret(b.at0(), b.at1()); |
| 313 |
2/4✓ Branch 3 taken 318 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 318 times.
✗ Branch 7 not taken.
|
318 | std::vector<Coord> r = derivative(b).roots(); |
| 314 |
2/2✓ Branch 7 taken 122 times.
✓ Branch 8 taken 318 times.
|
440 | for (double i : r) { |
| 315 |
1/2✓ Branch 2 taken 122 times.
✗ Branch 3 not taken.
|
122 | ret->expandTo(b.valueAt(i)); |
| 316 | } | ||
| 317 | 318 | return ret; | |
| 318 | 318 | } | |
| 319 | |||
| 320 | 1 | OptInterval bounds_local(Bezier const &b, OptInterval const &i) | |
| 321 | { | ||
| 322 | //return bounds_local(b.toSBasis(), i); | ||
| 323 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (i) { |
| 324 |
2/4✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
2 | return bounds_fast(portion(b, i->min(), i->max())); |
| 325 | } else { | ||
| 326 | ✗ | return OptInterval(); | |
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | /* | ||
| 331 | * The general bézier of degree n is | ||
| 332 | * | ||
| 333 | * p(t) = sum_{i = 0...n} binomial(n, i) t^i (1 - t)^(n - i) x[i] | ||
| 334 | * | ||
| 335 | * It can be written explicitly as a polynomial in t as | ||
| 336 | * | ||
| 337 | * p(t) = sum_{i = 0...n} binomial(n, i) t^i [ sum_{j = 0...i} binomial(i, j) (-1)^(i - j) x[j] ] | ||
| 338 | * | ||
| 339 | * Its derivative is | ||
| 340 | * | ||
| 341 | * p'(t) = n sum_{i = 1...n} binomial(n - 1, i - 1) t^(i - 1) [ sum_{j = 0...i} binomial(i, j) (-1)^(i - j) x[j] ] | ||
| 342 | * | ||
| 343 | * This is used by the various specialisations below as an optimisation for low degree n <= 3. | ||
| 344 | * In the remaining cases, the generic implementation is used which resorts to iteration. | ||
| 345 | */ | ||
| 346 | |||
| 347 | 100 | void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2) | |
| 348 | { | ||
| 349 | 100 | range.expandTo(x2); | |
| 350 | |||
| 351 |
2/2✓ Branch 1 taken 70 times.
✓ Branch 2 taken 30 times.
|
100 | if (range.contains(x1)) { |
| 352 | // The interval contains all control points, and therefore the entire curve. | ||
| 353 | 70 | return; | |
| 354 | } | ||
| 355 | |||
| 356 | // p'(t) / 2 = at + b | ||
| 357 | 30 | auto a = (x2 - x1) - (x1 - x0); | |
| 358 | 30 | auto b = x1 - x0; | |
| 359 | |||
| 360 | // t = -b / a | ||
| 361 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | if (std::abs(a) > EPSILON) { |
| 362 | 30 | auto t = -b / a; | |
| 363 |
2/4✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
30 | if (t > 0.0 && t < 1.0) { |
| 364 | 30 | auto s = 1.0 - t; | |
| 365 | 30 | auto x = s * s * x0 + 2 * s * t * x1 + t * t * x2; | |
| 366 | 30 | range.expandTo(x); | |
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | 100 | void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2, Coord x3) | |
| 372 | { | ||
| 373 | 100 | range.expandTo(x3); | |
| 374 | |||
| 375 |
6/6✓ Branch 1 taken 70 times.
✓ Branch 2 taken 30 times.
✓ Branch 4 taken 46 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 46 times.
✓ Branch 7 taken 54 times.
|
100 | if (range.contains(x1) && range.contains(x2)) { |
| 376 | // The interval contains all control points, and therefore the entire curve. | ||
| 377 | 46 | return; | |
| 378 | } | ||
| 379 | |||
| 380 | // p'(t) / 3 = at^2 + 2bt + c | ||
| 381 | 54 | auto a = (x3 - x0) - 3 * (x2 - x1); | |
| 382 | 54 | auto b = (x2 - x1) - (x1 - x0); | |
| 383 | 54 | auto c = x1 - x0; | |
| 384 | |||
| 385 | 54 | auto expand = [&] (Coord t) { | |
| 386 |
3/4✓ Branch 0 taken 84 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
|
104 | if (t > 0.0 && t < 1.0) { |
| 387 | 84 | auto s = 1.0 - t; | |
| 388 | 84 | auto x = s * s * s * x0 + 3 * s * s * t * x1 + 3 * t * t * s * x2 + t * t * t * x3; | |
| 389 | 84 | range.expandTo(x); | |
| 390 | } | ||
| 391 | 158 | }; | |
| 392 | |||
| 393 | // t = (-b ± sqrt(b^2 - ac)) / a | ||
| 394 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
|
54 | if (std::abs(a) < EPSILON) { |
| 395 | ✗ | if (std::abs(b) > EPSILON) { | |
| 396 | ✗ | expand(-c / (2 * b)); | |
| 397 | } | ||
| 398 | } else { | ||
| 399 | 54 | auto d2 = b * b - a * c; | |
| 400 |
2/2✓ Branch 0 taken 52 times.
✓ Branch 1 taken 2 times.
|
54 | if (d2 >= 0.0) { |
| 401 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 26 times.
|
52 | auto bsign = b >= 0.0 ? 1 : -1; |
| 402 | 52 | auto tmp = -(b + bsign * std::sqrt(d2)); | |
| 403 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | expand(tmp / a); |
| 404 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | expand(c / tmp); // Using Vieta's formula: product of roots == c/a |
| 405 | } | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | } // namespace Geom | ||
| 410 | |||
| 411 | /* | ||
| 412 | Local Variables: | ||
| 413 | mode:c++ | ||
| 414 | c-file-style:"stroustrup" | ||
| 415 | c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) | ||
| 416 | indent-tabs-mode:nil | ||
| 417 | fill-column:99 | ||
| 418 | End: | ||
| 419 | */ | ||
| 420 | // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : | ||
| 421 |