void Bernsteins::find_bernstein_roots(Bezier bz, unsigned depth, double left_t, double right_t) { debug(std::cout << left_t << ", " << right_t << std::endl); size_t n_crossings = 0; int old_sign = SGN(bz[0]); //std::cout << "w[0] = " << bz[0] << std::endl; int sign; for (size_t i = 1; i < bz.size(); i++) { //std::cout << "w[" << i << "] = " << w[i] << std::endl; sign = SGN(bz[i]); if (sign != 0) { if (sign != old_sign && old_sign != 0) { ++n_crossings; } old_sign = sign; } } //std::cout << "n_crossings = " << n_crossings << std::endl; if (n_crossings == 0) return; // no solutions here if (n_crossings == 1) /* Unique solution */ { //std::cout << "depth = " << depth << std::endl; /* Stop recursion when the tree is deep enough */ /* if deep enough, return 1 solution at midpoint */ if (depth > MAX_DEPTH) { //printf("bottom out %d\n", depth); const double Ax = right_t - left_t; const double Ay = bz.at1() - bz.at0(); solutions.push_back(left_t - Ax*bz.at0() / Ay); return; } double r = secant(bz); solutions.push_back(r*right_t + (1-r)*left_t); return; } /* Otherwise, solve recursively after subdividing control polygon */ Bezier::Order o(bz); Bezier Left(o), Right = bz; double split_t = (left_t + right_t) * 0.5; // If subdivision is working poorly, split around the leftmost root of the derivative if (depth > 2) { debug(std::cout << "derivative mode\n"); Bezier dbz = derivative(bz); debug(std::cout << "initial = " << dbz << std::endl); std::vector<double> dsolutions = dbz.roots(Interval(left_t, right_t)); debug(std::cout << "dsolutions = " << dsolutions << std::endl); double dsplit_t = 0.5; if(!dsolutions.empty()) { dsplit_t = dsolutions[0]; split_t = left_t + (right_t - left_t)*dsplit_t; debug(std::cout << "split_value = " << bz(split_t) << std::endl); debug(std::cout << "spliting around " << dsplit_t << " = " << split_t << "\n"); } std::pair<Bezier, Bezier> LR = bz.subdivide(dsplit_t); Left = LR.first; Right = LR.second; } else { // split at midpoint, because it is cheap Left[0] = Right[0]; for (size_t i = 1; i < bz.size(); ++i) { for (size_t j = 0; j < bz.size()-i; ++j) { Right[j] = (Right[j] + Right[j+1]) * 0.5; } Left[i] = Right[0]; } } debug(std::cout << "Solution is exactly on the subdivision point.\n"); debug(std::cout << Left << " , " << Right << std::endl); Left = reverse(Left); while(Right.order() > 0 and fabs(Right[0]) <= 1e-10) { debug(std::cout << "deflate\n"); Right = Right.deflate(); Left = Left.deflate(); solutions.push_back(split_t); } Left = reverse(Left); if (Right.order() > 0) { debug(std::cout << Left << " , " << Right << std::endl); find_bernstein_roots(Left, depth+1, left_t, split_t); find_bernstein_roots(Right, depth+1, split_t, right_t); } }
/* * find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all * of the roots in the open interval (0, 1). Return the number of roots found. */ void find_bernstein_roots(double const *w, /* The control points */ unsigned degree, /* The degree of the polynomial */ std::vector<double> &solutions, /* RETURN candidate t-values */ unsigned depth, /* The depth of the recursion */ double left_t, double right_t) { unsigned n_crossings = 0; /* Number of zero-crossings */ int old_sign = SGN(w[0]); for (unsigned i = 1; i <= degree; i++) { int sign = SGN(w[i]); if (sign) { if (sign != old_sign && old_sign) { n_crossings++; } old_sign = sign; } } switch (n_crossings) { case 0: /* No solutions here */ return; case 1: /* Unique solution */ /* Stop recursion when the tree is deep enough */ /* if deep enough, return 1 solution at midpoint */ if (depth >= MAXDEPTH) { solutions.push_back((left_t + right_t) / 2.0); return; } // I thought secant method would be faster here, but it'aint. -- njh if (control_poly_flat_enough(w, degree, left_t, right_t)) { const double Ax = right_t - left_t; const double Ay = w[degree] - w[0]; solutions.push_back(left_t - Ax*w[0] / Ay); return; } break; } /* Otherwise, solve recursively after subdividing control polygon */ std::vector<double> Left(degree+1); /* New left and right */ std::vector<double> Right(degree+1);/* control polygons */ const double split = 0.5; Bernstein(w, degree, split, &Left[0], &Right[0]); double mid_t = left_t*(1-split) + right_t*split; find_bernstein_roots(&Left[0], degree, solutions, depth+1, left_t, mid_t); /* Solution is exactly on the subdivision point. */ if (Right[0] == 0) solutions.push_back(mid_t); find_bernstein_roots(&Right[0], degree, solutions, depth+1, mid_t, right_t); }