bool Quadric::intersect(const Point3D &eye, const Point3D &_ray, HitReporter &hr) const { Polynomial<2> x, y, z; const Vector3D ray = _ray - eye; x[0] = eye[0]; x[1] = ray[0]; y[0] = eye[1]; y[1] = ray[1]; z[0] = eye[2]; z[1] = ray[2]; const Polynomial<2> eqn = A * x * x + B * x * y + C * x * z + D * y * y + E * y * z + F * z * z + G * x + H * y + J * z + K; double ts[2]; auto nhits = eqn.solve(ts); if(nhits > 0) { const Polynomial<2> ddx = 2 * A * x + B * y + C * z + G, ddy = 2 * D * y + B * x + E * z + H, ddz = 2 * F * z + C * x + E * y + J; for(int i = 0; i < nhits; i++) { // Figure out the normal. const double t = ts[i]; const Point3D pt = eye + t * ray; if(!predicate(pt)) continue; Vector3D normal(ddx.eval(t), ddy.eval(t), ddz.eval(t)); // Figure out the uv. Point2D uv; Vector3D u, v; get_uv(pt, normal, uv, u, v); if(!hr.report(ts[i], normal, uv, u, v)) return false; } } return true; }
int main() { Polynomial a ({2,1,0,1,0}) ; Polynomial b ; Polynomial c (a); cout<<a<<endl; cout<<a.deriv()<<endl; cout<<endl; cout<<endl; cout<<endl; cout<<a<<endl; b.setcoefs({1,1,1,1}); cout<<b.getcoef(0)<<endl; cout<<b<<endl; cout<< b.eval(2)<<endl; cout<<endl; cout<<endl; cout<<endl; cout<<b<<endl; cout<<b*3<<endl; cout<<endl; cout<<endl; cout<<endl; cout<<b<<endl; cout<<b+3<<endl; cout<<endl; return 0; }
void isolate_root_with_cluster(double& low, double& hgh, Polynomial *p) { dprint(low); dprint(hgh); // ensure starting conditions // PointVal pv_low(low,p); // double p_low = eval(low); if(pv_low.v() < 0.) { Polynomial p_minus = -(*p); p_minus.isolate_root(low,hgh); return; } // initialize algorithm // // initialize root bracket. // PointVal pv_hgh(hgh,p); RootBracketClusterState rb(pv_low,pv_hgh); dprint(rb.low().x()); dprint(rb.low().v()); dprint(rb.hgh().x()); dprint(rb.hgh().v()); // Polynomial Dp = p->get_derivative(); // requirements of algorithm: // - second-order convergence in all cases // - high performance in generic cases // - handles repeated polynomial roots // - converges to a root between low and hgh // - residual of answer is less than machine precision // conditions maintained by algorithm: // * low < hgh // * p(low) > EPSILON // * p(hgh) < -EPSILON // using_hgh = fabs(p(hgh)) > fabs(p(low)) // state of algorithm is specified by: // * low // * hgh // * using_hgh // * num_roots_in_cluster = assumed odd number of roots in cluster // iteration of algorithm: // use newton iteration on smaller side to look for root // - check that guess is between low and hgh // and that new value has smaller residual // else switch to secant method // - if newton does not change the sign // increment num_roots_in_cluster until you leave interval, // the sign changes, or the residual increases. // - if the sign does change // decrement num_roots_in_cluster until num_roots_in_cluster=1 // or the sign does not change (if residual does not shrink // sufficiently modify num_roots_in_cluster). // - do one iteration of bisection if residual did not // shrink by factor of e.g. .5 // assumed number of roots in root cluster we are converging towards goto start_newton_iteration; start_newton_iteration: // initialize newton iteration // choose smaller endpoint as starting value. goto newton_iteration; done: dprint(rb.num_evals()); low = rb.low().x(); hgh = rb.hgh().x(); //return rb.x(); return; // newton's method begins with a point which is one end of a root bracket. // it determines a displacement based on the value of the function // and its derivative at the point. To handle root clusters // it assumes that the function is approximated by a shifted // and scaled monomial. newton_iteration: { if(rb.done()) goto done; dprintf("using newton: x0=%24.16e, p(x0)=%24.16e",rb.pv().x(),rb.pv().v()); const PointVal start_pv0(rb.pv()); // const double Dpx0 = Dp.eval(rb.pv().x()); // bail if the derivative is too small if(fabs(Dpx0) < 1e-6) { dprintf("bad derivative: %f",Dpx0); goto newton_bailed; } double displacement = -rb.pv().v()/Dpx0; compute_x0: double x0 = rb.pv().x() + rb.num_roots_in_cluster()*displacement; // if Newton prediction is outside interval if(rb.not_in_interval(x0)) { // reset num_roots_in_cluster and try again. if(rb.num_roots_in_cluster()!=1) { rb.reset_num_roots_in_cluster(); goto compute_x0; } dprintf("Newton %24.16e outside interval [%24.16e,%24.16e]", x0, rb.low().x(), rb.hgh().x()); dprintf("with values [%24.16e,%24.16e]", rb.low().v(), rb.hgh().v()); goto newton_bailed; } // evaluate the polynomial at the new guess if(rb.eval(x0,p)) goto done; // bail if the new residual is not sufficiently smaller if(rb.pv().a() > .5*start_pv0.a()) // exp(-1) < .5 < 1 { dprintf("apx0=%24.16e and start_apx0=%24.16e",rb.pv().a(),start_pv0.a()); dprintf("so Newton didn't help enough, maybe because Dpx0=%24.16e.",Dpx0); goto newton_bailed; } // did the sign change? bool sign_changed = ((start_pv0.v() < 0. && rb.pv().v() > 0.) || (start_pv0.v() > 0. && rb.pv().v() < 0.)) ? true : false; // does changing number of roots in root cluster help? const PointVal old_pv0 = rb.pv(); if(sign_changed) { // keep decrementing presumed number // of roots in cluster as long as you can // until it does not help the residual if(rb.num_roots_in_cluster()>=3) { //double old_x0 = x0; decrement: double x0 = old_pv0.x() - 2*displacement; // displacement stayed in root bracket, so smaller one will too if(rb.eval(x0,p)) goto done; if(rb.pv().a() < old_pv0.a()) { // accept decrement rb.decrement_num_roots_in_cluster(); goto decrement; } else { // reject decrement rb.set_to_using_smaller(); goto newton_iteration; } } // only one root so just iterate goto newton_iteration; } else // !sign_changed { // if the residual did not shrink by trigger_ratio then // we will try incrementing num_roots_in_cluster // // compute trigger_ratio // const double trigger_ratio = get_trigger_ratio_for_newton(rb.num_roots_in_cluster()); // if residual shrunk by more than trigger_ratio we're fine if(rb.pv().a() < trigger_ratio*old_pv0.a()) { // evidently not near a root cluster // with a wrong assumption about the number // of roots, so just iterate goto newton_iteration; } else { // keep incrementing presumed number of roots // in cluster until we are outside the interval // or until doing so does not improve // the residual by better than multiplying // it by a factor of .5 // (can use any number between 1/e and 1). { increment: x0 += 2*displacement; if(rb.not_in_interval(x0)) { // reject increment goto newton_iteration; } const PointVal curr_pv0(rb.pv()); if(rb.eval(x0,p)) goto done; if(rb.pv().a() > .5*curr_pv0.a()) { // reject increment // // the constraint that we always // tighten the root bracket whenever // we evaluate the polynomial means // we cannot completely reject the evaluation // although we can still refrain from // incrementing num_roots_in_cluster. rb.set_to_using_smaller(); goto newton_iteration; } else { // accept increment rb.increment_num_roots_in_cluster(); goto increment; } } } goto newton_iteration; } } newton_bailed: { // ideally we should try the secant method // before reverting to bisection // // the secant method should handle repeated roots better goto secant_bracket_iteration; } // the secant rule needs to maintain a list of two // points that we are "using". // the root bracket contains the most recently evaluated point. // The secant method is initialized with a root bracket. secant_bracket_iteration: { dprintf("using secant: low=%24.16e, hgh=%24.16e, p_low=%24.16e, p_hgh=%24.16e", rb.low().x(),rb.hgh().x(),rb.low().v(),rb.hgh().v()); // rb.set_to_using_smaller(); PointVal pv1(rb.pv_old()); const double start_pva = fmax(pv1.a(),rb.pv().a()); secant_iteration: // double accepted_num_roots_in_cluster = num_roots_in_cluster; compute_x0_secant: // for try_num_roots_in_cluster const double ratio = rb.pv().v()/pv1.v(); compute_secant_rule: // problem: these numbers can be equal. //dprint(rb.pv().v()); //dprint(pv1.v()); double x0 = secant_rule(rb.pv().x(),pv1.x(),ratio, rb.num_roots_in_cluster()); // make sure that x0 is in root bracket if(x0<rb.low().x() || x0>rb.hgh().x()) { // different signs would force x0 in root bracket //assert_gt(ratio, 0.) // increasing num roots would only make it worse if(rb.num_roots_in_cluster()>1) { rb.decrement_num_roots_in_cluster(); goto compute_secant_rule; } //else //{ // goto bisect; //} } // evaluate at the candidate dprint(x0); pv1 = rb.pv(); if(rb.eval(x0,p)) goto done; if(rb.pv().a() >= pv1.a()) { dprintf("bisect"); goto bisect; } // did residual change signs? bool sign_changed = ((pv1.v() < 0. && rb.pv().v() > 0.) || (pv1.v() > 0. && rb.pv().v() < 0.)) ? true : false; // if residual changed signs then try decrementing num_roots_in_cluster // if residual did not change signs then try incrementing. if(!sign_changed) { // if the residual did not shrink by trigger_ratio then // we will try incrementing num_roots_in_cluster double secant_trigger = get_trigger_ratio_for_secant( rb.num_roots_in_cluster()); if(pv1.a() > secant_trigger * rb.pv().a()) { // try incrementing num_roots_in_cluster const double ratio = rb.pv().v()/pv1.v(); const double x0 = secant_rule(rb.pv().x(),pv1.x(),ratio, rb.num_roots_in_cluster()+2); // make sure root is in interval if(x0<rb.low().x() || x0>rb.hgh().x()) { dprintf("bisect"); goto bisect; } pv1 = rb.pv(); if(rb.eval(x0,p)) goto done; if(rb.pv().a() < pv1.a()) { rb.increment_num_roots_in_cluster(); goto secant_iteration; } } } else { // try decrementing num_roots_in_cluster if(rb.num_roots_in_cluster()>=3) { const double ratio = rb.pv().v()/pv1.v(); const double x0 = secant_rule(rb.pv().x(),pv1.x(),ratio, rb.num_roots_in_cluster()-2); // make sure root is in interval if(x0<rb.low().x() || x0>rb.hgh().x()) goto bisect; pv1 = rb.pv(); if(rb.eval(x0,p)) goto done; if(rb.pv().a() < pv1.a()) { rb.decrement_num_roots_in_cluster(); goto secant_iteration; } } } secant_iter_done: { if(rb.pv().a() > .5*start_pva) { dprintf("secant did not help enough: apx=%24.16e, old_apx=%24.16e", rb.pv().a(), start_pva); goto bisect; } goto secant_bracket_iteration; } } bisect: { dprintf("bisecting: x=%24.16e, px=%24.16e",rb.pv().x(),rb.pv().v()); const double x0 = (rb.low().x() + rb.hgh().x())*.5; if(rb.eval(x0,p)) goto done; //goto newton_iteration; goto secant_bracket_iteration; } eprintf("should not get here"); return; }