| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <2geom/solver.h> | ||
| 2 | #include <2geom/choose.h> | ||
| 3 | #include <2geom/bezier.h> | ||
| 4 | #include <2geom/point.h> | ||
| 5 | |||
| 6 | #include <cmath> | ||
| 7 | #include <algorithm> | ||
| 8 | |||
| 9 | /*** Find the zeros of the bernstein function. The code subdivides until it is happy with the | ||
| 10 | * linearity of the function. This requires an O(degree^2) subdivision for each step, even when | ||
| 11 | * there is only one solution. | ||
| 12 | */ | ||
| 13 | |||
| 14 | namespace Geom { | ||
| 15 | namespace { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * This function is called _a lot_. We have included various manual memory management stuff to reduce the amount of mallocing that goes on. In the future it is possible that this will hurt performance. | ||
| 19 | **/ | ||
| 20 | struct Bernsteins | ||
| 21 | { | ||
| 22 | static constexpr size_t MAX_DEPTH = 53; | ||
| 23 | size_t degree, N; | ||
| 24 | std::vector<double> &solutions; | ||
| 25 | |||
| 26 | 22188 | Bernsteins(size_t _degree, std::vector<double> &sol) | |
| 27 | 22188 | : degree{_degree} | |
| 28 | 22188 | , N{degree + 1} | |
| 29 | 22188 | , solutions{sol} | |
| 30 | 22188 | {} | |
| 31 | |||
| 32 | void | ||
| 33 | find_bernstein_roots(double const *w, /* The control points */ | ||
| 34 | unsigned depth, /* The depth of the recursion */ | ||
| 35 | double left_t, double right_t); | ||
| 36 | }; | ||
| 37 | |||
| 38 | } // namespace | ||
| 39 | |||
| 40 | /* | ||
| 41 | * find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all | ||
| 42 | * of the roots in the open interval (0, 1). Return the number of roots found. | ||
| 43 | */ | ||
| 44 | void | ||
| 45 | 22188 | find_bernstein_roots(double const *w, /* The control points */ | |
| 46 | unsigned degree, /* The degree of the polynomial */ | ||
| 47 | std::vector<double> &solutions, /* RETURN candidate t-values */ | ||
| 48 | unsigned depth, /* The depth of the recursion */ | ||
| 49 | double left_t, double right_t, bool /*use_secant*/) | ||
| 50 | { | ||
| 51 | 22188 | Bernsteins B(degree, solutions); | |
| 52 |
1/2✓ Branch 1 taken 22188 times.
✗ Branch 2 not taken.
|
22188 | B.find_bernstein_roots(w, depth, left_t, right_t); |
| 53 | 22188 | } | |
| 54 | |||
| 55 | void | ||
| 56 | ✗ | find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-values */ | |
| 57 | Geom::Bezier const &bz, /* The control points */ | ||
| 58 | double left_t, double right_t) | ||
| 59 | { | ||
| 60 | ✗ | Bernsteins B(bz.degree(), solutions); | |
| 61 | ✗ | Geom::Bezier& bzl = const_cast<Geom::Bezier&>(bz); | |
| 62 | ✗ | double* w = &(bzl[0]); | |
| 63 | ✗ | B.find_bernstein_roots(w, 0, left_t, right_t); | |
| 64 | ✗ | } | |
| 65 | |||
| 66 | 405298 | void Bernsteins::find_bernstein_roots(double const *w, /* The control points */ | |
| 67 | unsigned depth, /* The depth of the recursion */ | ||
| 68 | double left_t, | ||
| 69 | double right_t) | ||
| 70 | { | ||
| 71 | 405298 | size_t n_crossings = 0; | |
| 72 | |||
| 73 | 405298 | int old_sign = Geom::sgn(w[0]); | |
| 74 | //std::cout << "w[0] = " << w[0] << std::endl; | ||
| 75 |
2/2✓ Branch 0 taken 2819424 times.
✓ Branch 1 taken 405298 times.
|
3224722 | for (size_t i = 1; i < N; i++) |
| 76 | { | ||
| 77 | //std::cout << "w[" << i << "] = " << w[i] << std::endl; | ||
| 78 | 2819424 | int sign = Geom::sgn(w[i]); | |
| 79 |
1/2✓ Branch 0 taken 2819424 times.
✗ Branch 1 not taken.
|
2819424 | if (sign != 0) |
| 80 | { | ||
| 81 |
3/4✓ Branch 0 taken 579462 times.
✓ Branch 1 taken 2239962 times.
✓ Branch 2 taken 579462 times.
✗ Branch 3 not taken.
|
2819424 | if (sign != old_sign && old_sign != 0) |
| 82 | { | ||
| 83 | 579462 | ++n_crossings; | |
| 84 | } | ||
| 85 | 2819424 | old_sign = sign; | |
| 86 | } | ||
| 87 | } | ||
| 88 | //std::cout << "n_crossings = " << n_crossings << std::endl; | ||
| 89 |
2/2✓ Branch 0 taken 193703 times.
✓ Branch 1 taken 211595 times.
|
405298 | if (n_crossings == 0) return; // no solutions here |
| 90 | |||
| 91 |
2/2✓ Branch 0 taken 20040 times.
✓ Branch 1 taken 191555 times.
|
211595 | if (n_crossings == 1) /* Unique solution */ |
| 92 | { | ||
| 93 | //std::cout << "depth = " << depth << std::endl; | ||
| 94 | /* Stop recursion when the tree is deep enough */ | ||
| 95 | /* if deep enough, return 1 solution at midpoint */ | ||
| 96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 20040 times.
|
20040 | if (depth > MAX_DEPTH) |
| 97 | { | ||
| 98 | //printf("bottom out %d\n", depth); | ||
| 99 | ✗ | const double Ax = right_t - left_t; | |
| 100 | ✗ | const double Ay = w[degree] - w[0]; | |
| 101 | |||
| 102 | ✗ | solutions.push_back(left_t - Ax*w[0] / Ay); | |
| 103 | ✗ | return; | |
| 104 | } | ||
| 105 | |||
| 106 | |||
| 107 | 20040 | double s = 0, t = 1; | |
| 108 | 20040 | double e = 1e-10; | |
| 109 | 20040 | int side = 0; | |
| 110 | 20040 | double r, fs = w[0], ft = w[degree]; | |
| 111 | |||
| 112 |
1/2✓ Branch 0 taken 248344 times.
✗ Branch 1 not taken.
|
248344 | for (size_t n = 0; n < 100; ++n) |
| 113 | { | ||
| 114 | 248344 | r = (fs*t - ft*s) / (fs - ft); | |
| 115 |
2/2✓ Branch 0 taken 19734 times.
✓ Branch 1 taken 228610 times.
|
248344 | if (fabs(t-s) < e * fabs(t+s)) break; |
| 116 | |||
| 117 | 228610 | double fr = bernstein_value_at(r, w, degree); | |
| 118 | |||
| 119 |
2/2✓ Branch 0 taken 109154 times.
✓ Branch 1 taken 119456 times.
|
228610 | if (fr * ft > 0) |
| 120 | { | ||
| 121 | 109154 | t = r; ft = fr; | |
| 122 |
2/2✓ Branch 0 taken 46063 times.
✓ Branch 1 taken 63091 times.
|
109154 | if (side == -1) fs /= 2; |
| 123 | 109154 | side = -1; | |
| 124 | } | ||
| 125 |
2/2✓ Branch 0 taken 119150 times.
✓ Branch 1 taken 306 times.
|
119456 | else if (fs * fr > 0) |
| 126 | { | ||
| 127 | 119150 | s = r; fs = fr; | |
| 128 |
2/2✓ Branch 0 taken 56877 times.
✓ Branch 1 taken 62273 times.
|
119150 | if (side == +1) ft /= 2; |
| 129 | 119150 | side = +1; | |
| 130 | } | ||
| 131 | 306 | else break; | |
| 132 | } | ||
| 133 |
1/2✓ Branch 2 taken 20040 times.
✗ Branch 3 not taken.
|
20040 | solutions.push_back(r*right_t + (1-r)*left_t); |
| 134 | 20040 | return; | |
| 135 | |||
| 136 | } | ||
| 137 | |||
| 138 | /* Otherwise, solve recursively after subdividing control polygon */ | ||
| 139 |
2/4✓ Branch 0 taken 191555 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 191555 times.
✗ Branch 5 not taken.
|
191555 | double* LR = new double[2*N]; |
| 140 | 191555 | double* Left = LR; | |
| 141 | 191555 | double* Right = LR + N; | |
| 142 | |||
| 143 |
1/2✓ Branch 1 taken 191555 times.
✗ Branch 2 not taken.
|
191555 | std::copy(w, w + N, Right); |
| 144 | |||
| 145 | 191555 | Left[0] = Right[0]; | |
| 146 |
2/2✓ Branch 0 taken 1333376 times.
✓ Branch 1 taken 191555 times.
|
1524931 | for (size_t i = 1; i < N; ++i) |
| 147 | { | ||
| 148 |
2/2✓ Branch 0 taken 5402960 times.
✓ Branch 1 taken 1333376 times.
|
6736336 | for (size_t j = 0; j < N-i; ++j) |
| 149 | { | ||
| 150 | 5402960 | Right[j] = (Right[j] + Right[j+1]) * 0.5; | |
| 151 | } | ||
| 152 | 1333376 | Left[i] = Right[0]; | |
| 153 | } | ||
| 154 | |||
| 155 | 191555 | double mid_t = (left_t + right_t) * 0.5; | |
| 156 | |||
| 157 | |||
| 158 |
1/2✓ Branch 1 taken 191555 times.
✗ Branch 2 not taken.
|
191555 | find_bernstein_roots(Left, depth+1, left_t, mid_t); |
| 159 | |||
| 160 | |||
| 161 | /* Solution is exactly on the subdivision point. */ | ||
| 162 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 191555 times.
|
191555 | if (Right[0] == 0) |
| 163 | { | ||
| 164 | ✗ | solutions.push_back(mid_t); | |
| 165 | } | ||
| 166 | |||
| 167 |
1/2✓ Branch 1 taken 191555 times.
✗ Branch 2 not taken.
|
191555 | find_bernstein_roots(Right, depth+1, mid_t, right_t); |
| 168 |
1/2✓ Branch 0 taken 191555 times.
✗ Branch 1 not taken.
|
191555 | delete[] LR; |
| 169 | } | ||
| 170 | |||
| 171 | } // namespace Geom | ||
| 172 | |||
| 173 | /* | ||
| 174 | Local Variables: | ||
| 175 | mode:c++ | ||
| 176 | c-file-style:"stroustrup" | ||
| 177 | c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) | ||
| 178 | indent-tabs-mode:nil | ||
| 179 | fill-column:99 | ||
| 180 | End: | ||
| 181 | */ | ||
| 182 | // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : | ||
| 183 |