Exemple #1
0
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;
}
Exemple #2
0
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;
}