Esempio n. 1
0
VGboolean shIsTessCacheValid (VGContext *c, SHPath *p)
{
  SHfloat nX, nY;
  SHVector2 X, Y;
  SHMatrix3x3 mi, mchange;
  VGboolean valid = VG_TRUE;

  if (p->cacheDataValid == VG_FALSE) {
    valid = VG_FALSE;
  }
  else if (p->cacheTransformInit == VG_FALSE) {
    valid = VG_FALSE;
  }
  else if (shInvertMatrix( &p->cacheTransform, &mi ) == VG_FALSE) {
    valid = VG_FALSE;
  }
  else
  {
    /* TODO: Compare change matrix for any scale or shear  */
    MULMATMAT( c->pathTransform, mi, mchange );
    SET2( X, mi.m[0][0], mi.m[1][0] );
    SET2( Y, mi.m[0][1], mi.m[1][1] );
    nX = NORM2( X ); nY = NORM2( Y );
    if (nX > 1.01f || nX < 0.99 ||
        nY > 1.01f || nY < 0.99)
      valid = VG_FALSE;
  }

  if (valid == VG_FALSE)
  {
    /* Update cache */
    p->cacheDataValid = VG_TRUE;
    p->cacheTransformInit = VG_TRUE;
    p->cacheTransform = c->pathTransform;
    p->cacheStrokeTessValid = VG_FALSE;
  }
  
  return valid;
}
Esempio n. 2
0
void incr_facet(
  REAL n1, REAL n2, REAL n3,
  REAL i1, REAL i2, REAL i3,
  REAL j1, REAL j2, REAL j3,
  REAL k1, REAL k2, REAL k3){


  REAL x[3] = {i1-j1,i2-j2,i3-j3};
  REAL y[3] = {i1-k1,i2-k2,i3-k3};
  REAL zz[3]; CROSS(x,y,zz);
  REAL A = sqrt(NORM2(zz))/2;
  REAL incr = 1./3. * (i1*n1+i2*n2+i3*n3)*A;

//  REAL incr = (-1*k1*j2*i3 + j1*k2*i3 + k1*i2*j3 - i1*k2*j3 - j1*i2*k3 + i1*j2*k3)/6.;

  vol+=incr;
}
Esempio n. 3
0
int shDrawRadialGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
                             VGPaintMode mode, GLenum texUnit)
{
  SHint i, j;
  float a, n;
  
  SHfloat cx = p->radialGradient[0];
  SHfloat cy = p->radialGradient[1];
  SHfloat fx = p->radialGradient[2];
  SHfloat fy = p->radialGradient[3];
  float r = p->radialGradient[4];
  float fcx, fcy, rr, C;
  
  SHVector2 ux;
  SHVector2 uy;
  SHVector2 c, f;
  SHVector2 cf;

  SHMatrix3x3 *m;
  SHMatrix3x3 mi;
  SHint invertible;
  SHVector2 corners[4];
  SHVector2 fcorners[4];
  SHfloat minOffset=0.0f;
  SHfloat maxOffset=0.0f;
  
  SHint maxI=0, maxJ=0;
  SHfloat maxA=0.0f;
  SHfloat startA=0.0f;
  
  int numsteps = 100;
  float step = 2*PI/numsteps;
  SHVector2 tmin, tmax;
  SHVector2 min1, max1, min2, max2;
  
  /* Pick paint transform matrix */
  SH_GETCONTEXT(0);
  if (mode == VG_FILL_PATH)
    m = &context->fillTransform;
  else if (mode == VG_STROKE_PATH)
    m = &context->strokeTransform;
  
  /* Move focus into circle if outside */
  SET2(cf, fx,fy);
  SUB2(cf, cx,cy);
  n = NORM2(cf);
  if (n > r) {
    DIV2(cf, n);
    fx = cx + 0.995f * r * cf.x;
    fy = cy + 0.995f * r * cf.y;
  }
  
  /* Precalculations */
  rr = r*r;
  fcx = fx - cx;
  fcy = fy - cy;
  C = fcx*fcx + fcy*fcy - rr;
  
  /* Apply paint-to-user transformation
     to focus and unit vectors */
  SET2(f, fx, fy);
  SET2(c, cx, cy);
  SET2(ux, 1, 0);
  SET2(uy, 0, 1);
  ADD2(ux, cx, cy);
  ADD2(uy, cx, cy);
  TRANSFORM2(f, (*m));
  TRANSFORM2(c, (*m));
  TRANSFORM2(ux, (*m));
  TRANSFORM2(uy, (*m));
  SUB2V(ux, c); SUB2V(uy, c);
  
  /* Boundbox corners */
  SET2(corners[0], min->x, min->y);
  SET2(corners[1], max->x, min->y);
  SET2(corners[2], max->x, max->y);
  SET2(corners[3], min->x, max->y);
  
  /* Find inverse transformation (back to paint space) */
  invertible = shInvertMatrix(m, &mi);
  if (!invertible || r <= 0.0f) {
    
    /* Fill boundbox with color at offset 1 */
    SHColor *c = &p->stops.items[p->stops.size-1].color;
    glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
    for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
    glEnd();
    return 1;
  }
  
  /*--------------------------------------------------------*/
  
  /* Find min/max offset */
  for (i=0; i<4; ++i) {
    
    /* Transform to paint space */
    SHfloat ax,ay, A,B,D,t, off;
    TRANSFORM2TO(corners[i], mi, fcorners[i]);
    SUB2(fcorners[i], fx, fy);
    n = NORM2(fcorners[i]);
    if (n == 0.0f) {
      
      /* Avoid zero-length vectors */
      off = 0.0f;
      
    }else{
      
      /* Distance from focus to circle at corner angle */
      DIV2(fcorners[i], n);
      ax = fcorners[i].x;
      ay = fcorners[i].y;
      A = ax*ax + ay*ay;
      B = 2 * (fcx*ax + fcy*ay);
      D = B*B - 4*A*C;
      t = (-B + SH_SQRT(D)) / (2*A);
      
      /* Relative offset of boundbox corner */
      if (D <= 0.0f) off = 1.0f;
      else off = n / t;
    }
    
    /* Find smallest and largest offset */
    if (off < minOffset || i==0) minOffset = off;
    if (off > maxOffset || i==0) maxOffset = off;
  }
  
  /* Is transformed focus inside original boundbox? */
  if (f.x >= min->x && f.x <= max->x &&
      f.y >= min->y && f.y <= max->y) {
    
    /* Draw whole circle */
    minOffset = 0.0f;
    startA = 0.0f;
    maxA = 2*PI;
    
  }else{
    
    /* Find most distant corner pair */
    for (i=0; i<3; ++i) {
      if (ISZERO2(fcorners[i])) continue;
      for (j=i+1; j<4; ++j) {
        if (ISZERO2(fcorners[j])) continue;
        a = ANGLE2N(fcorners[i], fcorners[j]);
        if (a > maxA || maxA == 0.0f)
          {maxA=a; maxI=i; maxJ=j;}
      }}
    
    /* Pick starting angle */
    if (CROSS2(fcorners[maxI],fcorners[maxJ]) > 0.0f)
      startA = shVectorOrientation(&fcorners[maxI]);
    else startA = shVectorOrientation(&fcorners[maxJ]);
  }
  
  /*---------------------------------------------------------*/
  
  /* TODO: for minOffset we'd actually need to find minimum
     of the gradient function when X and Y are substitued
     with a line equation for each bound-box edge. As a
     workaround we use 0.0f for now. */
  minOffset = 0.0f;
  step = PI/50;
  numsteps = (SHint)SH_CEIL(maxA / step) + 1;
  
  glActiveTexture(texUnit);
  shSetGradientTexGLState(p);
  
  glEnable(GL_TEXTURE_1D);
  glBegin(GL_QUADS);
  
  /* Walk the steps and draw gradient mesh */
  for (i=0, a=startA; i<numsteps; ++i, a+=step) {
    
    /* Distance from focus to circle border
         at current angle (gradient space) */
    float ax = SH_COS(a);
    float ay = SH_SIN(a);
    float A = ax*ax + ay*ay;
    float B = 2 * (fcx*ax + fcy*ay);
    float D = B*B - 4*A*C;
    float t = (-B + SH_SQRT(D)) / (2*A);
    if (D <= 0.0f) t = 0.0f;
    
    /* Vectors pointing towards minimum and maximum
         offset at current angle (gradient space) */
    tmin.x = ax * t * minOffset;
    tmin.y = ay * t * minOffset;
    tmax.x = ax * t * maxOffset;
    tmax.y = ay * t * maxOffset;
    
    /* Transform back to user space */
    min2.x = f.x + tmin.x * ux.x + tmin.y * uy.x;
    min2.y = f.y + tmin.x * ux.y + tmin.y * uy.y;
    max2.x = f.x + tmax.x * ux.x + tmax.y * uy.x;
    max2.y = f.y + tmax.x * ux.y + tmax.y * uy.y;
    
    /* Draw quad */
    if (i!=0) {
      glMultiTexCoord1f(texUnit, minOffset);
      glVertex2fv((GLfloat*)&min1);
      glVertex2fv((GLfloat*)&min2);
      glMultiTexCoord1f(texUnit, maxOffset);
      glVertex2fv((GLfloat*)&max2);
      glVertex2fv((GLfloat*)&max1);
    }
    
    /* Save prev points */
    min1 = min2;
    max1 = max2;
  }
  
  glEnd();
  glDisable(GL_TEXTURE_1D);

  return 1;
}
Esempio n. 4
0
int shDrawLinearGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
                             VGPaintMode mode, GLenum texUnit)
{
  SHint i;
  SHfloat n;
  
  SHfloat x1 = p->linearGradient[0];
  SHfloat y1 = p->linearGradient[1];
  SHfloat x2 = p->linearGradient[2];
  SHfloat y2 = p->linearGradient[3];
  SHVector2 c, ux, uy;
  SHVector2 cc, uux, uuy;
  
  SHMatrix3x3 *m;
  SHMatrix3x3 mi;
  SHint invertible;
  SHVector2 corners[4];
  SHfloat minOffset = 0.0f;
  SHfloat maxOffset = 0.0f;
  SHfloat left = 0.0f;
  SHfloat right = 0.0f;
  SHVector2 l1,r1,l2,r2;

  /* Pick paint transform matrix */
  SH_GETCONTEXT(0);
  if (mode == VG_FILL_PATH)
    m = &context->fillTransform;
  else if (mode == VG_STROKE_PATH)
    m = &context->strokeTransform;
  
  /* Gradient center and unit vectors */
  SET2(c, x1, y1);
  SET2(ux, x2-x1, y2-y1);
  SET2(uy, -ux.y, ux.x);
  n = NORM2(ux);
  DIV2(ux, n);
  NORMALIZE2(uy);

  /* Apply paint-to-user transformation */
  ADD2V(ux, c); ADD2V(uy, c);
  TRANSFORM2TO(c, (*m), cc);
  TRANSFORM2TO(ux, (*m), uux);
  TRANSFORM2TO(uy, (*m), uuy);
  SUB2V(ux,c); SUB2V(uy,c);
  SUB2V(uux,cc); SUB2V(uuy,cc);
  
  /* Boundbox corners */
  SET2(corners[0], min->x, min->y);
  SET2(corners[1], max->x, min->y);
  SET2(corners[2], max->x, max->y);
  SET2(corners[3], min->x, max->y);
  
  /* Find inverse transformation (back to paint space) */
  invertible = shInvertMatrix(m, &mi);
  if (!invertible || n==0.0f) {
    
    /* Fill boundbox with color at offset 1 */
    SHColor *c = &p->stops.items[p->stops.size-1].color;
    glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
    for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
    glEnd();
    return 1;
  }
  
  /*--------------------------------------------------------*/
  
  for (i=0; i<4; ++i) {
    
    /* Find min/max offset and perpendicular span */
    SHfloat o, s;
    TRANSFORM2(corners[i], mi);
    SUB2V(corners[i], c);
    o = DOT2(corners[i], ux) / n;
    s = DOT2(corners[i], uy);
    if (o < minOffset || i==0) minOffset = o;
    if (o > maxOffset || i==0) maxOffset = o;
    if (s < left || i==0) left = s;
    if (s > right || i==0) right = s;
  }
  
  /*---------------------------------------------------------*/
  
  /* Corners of boundbox in gradient system */
  SET2V(l1, cc); SET2V(r1, cc);
  SET2V(l2, cc); SET2V(r2, cc);
  OFFSET2V(l1, uuy, left);  OFFSET2V(l1, uux, minOffset * n);
  OFFSET2V(r1, uuy, right); OFFSET2V(r1, uux, minOffset * n);
  OFFSET2V(l2, uuy, left);  OFFSET2V(l2, uux, maxOffset * n);
  OFFSET2V(r2, uuy, right); OFFSET2V(r2, uux, maxOffset * n);
  
  /* Draw quad using color-ramp texture */
  glActiveTexture(texUnit);
  shSetGradientTexGLState(p);
  
  glEnable(GL_TEXTURE_1D);
  glBegin(GL_QUAD_STRIP);
  
  glMultiTexCoord1f(texUnit, minOffset);
  glVertex2fv((GLfloat*)&r1);
  glVertex2fv((GLfloat*)&l1);
  
  glMultiTexCoord1f(texUnit, maxOffset);
  glVertex2fv((GLfloat*)&r2);
  glVertex2fv((GLfloat*)&l2);
  
  glEnd();
  glDisable(GL_TEXTURE_1D);

  return 1;
}
Esempio n. 5
0
opk_task_t
opk_iterate_vmlmb(opk_vmlmb_t* opt, opk_vector_t* x,
                  double f, opk_vector_t* g)
{
  double dtg, gtest, stpmin, stpmax;
  opk_index_t k;
  opk_status_t status;
  opk_lnsrch_task_t lnsrch_task;
  opk_bool_t bounded;

  if (opt == NULL) {
    return OPK_TASK_ERROR;
  }
  bounded = (opt->box != NULL);

  switch (opt->task) {

  case OPK_TASK_COMPUTE_FG:

    /* Caller has computed the function value and the gradient at the current
       point. */
    ++opt->evaluations;

    if (opt->evaluations > 1) {
      /* A line search is in progress, check whether it has converged. */
      if (opk_lnsrch_use_deriv(opt->lnsrch)) {
        if (bounded) {
          /* Compute the directional derivative as the inner product between
             the effective step and the gradient divided by the step length. */
#if 0
          if (opt->tmp == NULL &&
              (opt->tmp = opk_vcreate(opt->vspace)) == NULL) {
            return failure(opt, OPK_INSUFFICIENT_MEMORY);
          }
          AXPBY(opt->tmp, 1, x, -1, opt->x0);
          dtg = DOT(opt->tmp, g)/opt->stp;
#else
          dtg = (DOT(x, g) - DOT(opt->x0, g))/opt->stp;
#endif
        } else {
          /* Compute the directional derivative. */
          dtg = -opk_vdot(opt->d, g);
        }
      } else {
        /* Line search does not need directional derivative. */
        dtg = 0;
      }
      lnsrch_task = opk_lnsrch_iterate(opt->lnsrch, &opt->stp, f, dtg);
      if (lnsrch_task == OPK_LNSRCH_SEARCH) {
        /* Line search has not yet converged, break to compute a new trial
           point along the search direction. */
        break;
      }
      if (lnsrch_task != OPK_LNSRCH_CONVERGENCE) {
        /* An error may have occurred during the line search.  Figure out
           whether this error can be safely ignored. */
        status = opk_lnsrch_get_status(opt->lnsrch);
        if (lnsrch_task != OPK_LNSRCH_WARNING ||
            status != OPK_ROUNDING_ERRORS_PREVENT_PROGRESS) {
          return failure(opt, status);
        }
      }
      ++opt->iterations;
    }

    /* The current step is acceptable.  Check for global convergence. */
    if (bounded) {
      /* Determine the set of free variables. */
      status = opk_get_free_variables(opt->w, x, opt->box, g, OPK_ASCENT);
      if (status != OPK_SUCCESS) {
        return failure(opt, status);
      }
    }
    if (opt->method == OPK_VMLMB) {
      /* Compute the Euclidean norm of the projected gradient. */
      opt->gnorm = WNORM2(g);
    } else if (opt->method == OPK_BLMVM) {
      /* Compute the projected gradient and its norm. */
      opk_vproduct(opt->gp, opt->w, g);
      opt->gnorm = NORM2(opt->gp);
    } else {
      /* Compute the Euclidean norm of the gradient. */
      opt->gnorm = NORM2(g);
    }
    if (opt->evaluations == 1) {
      opt->ginit = opt->gnorm;
    }
    gtest = max3(0.0, opt->gatol, opt->grtol*opt->ginit);
    return success(opt, (opt->gnorm <= gtest
                         ? OPK_TASK_FINAL_X
                         : OPK_TASK_NEW_X));

  case OPK_TASK_NEW_X:
  case OPK_TASK_FINAL_X:

    /* Compute a new search direction. */
    if (opt->iterations >= 1) {
      /* Update L-BFGS approximation of the Hessian. */
      update(opt, x, (opt->method == OPK_BLMVM ? opt->gp : g));
    }
    status = apply(opt, g);
    if (status == OPK_SUCCESS) {
      /* The L-BFGS approximation produces a search direction D.  To warrant
         convergence, we have to check whether -D is a sufficient descent
         direction (that is to say that D is a sufficient ascent direction).
         As shown by Zoutendijk, this is true if cos(theta) = (D/|D|)'.(G/|G|)
         is larger or equal EPSILON > 0, where G is the gradient at X and D
         the, ascent for us, search direction. */
      if (bounded) {
        /* Project the search direction produced by the L-BFGS recursion. */
        status = opk_project_direction(opt->d, x, opt->box, opt->d, OPK_ASCENT);
        if (status != OPK_SUCCESS) {
          return failure(opt, status);
        }
      }
      dtg = -DOT(opt->d, g);
      if (opt->epsilon > 0 && -dtg < opt->epsilon*NORM2(opt->d)*opt->gnorm) {
        /* -D is not a sufficient descent direction.  Set the directional
           derivative to zero to force using the steepest descent direction. */
        dtg = 0.0;
      }
    } else {
      /* The L-BFGS approximation is not available (first iteration or just
         after a reset) or failed to produce a direction.  Set the directional
         derivative to zero to use the steepest descent direction. */
      dtg = 0.0;
    }

    /* Determine the initial step length. */
    if (dtg < 0) {
      /* A sufficient descent direction has been produced by L-BFGS recursion.
         An initial unit step will be used. */
      opt->stp = 1.0;
    } else {
      /* First iteration or L-BFGS recursion failed to produce a sufficient
         descent direction, use the (projected) gradient as a search
         direction. */
      if (opt->mp > 0) {
        /* L-BFGS recursion did not produce a sufficient descent direction. */
        ++opt->restarts;
        opt->mp = 0;
      }
      if (opt->method == OPK_VMLMB) {
        /* Use the projected gradient. */
        opk_vproduct(opt->d, opt->w, g);
      } else if (opt->method == OPK_BLMVM) {
        /* Use the projected gradient (which has already been computed and
         * stored in the scratch vector). */
        opk_vcopy(opt->d, opt->gp);
      } else {
        /* Use the gradient. */
        opk_vcopy(opt->d, g);
      }
      dtg = -opt->gnorm*opt->gnorm;
      if (f != 0) {
        opt->stp = 2*fabs(f/dtg);
      } else {
        /* Use a small step compared to X. */
        double dnorm = opt->gnorm;
        double xnorm = (bounded ? WNORM2(x) : NORM2(x));
        if (xnorm > 0) {
          opt->stp = opt->delta*xnorm/dnorm;
        } else {
          opt->stp = opt->delta/dnorm;
        }
      }
    }

    stpmin = opt->stp*opt->stpmin;
    stpmax = opt->stp*opt->stpmax;
    if (bounded) {
      /* Shortcut the step length. */
      double bsmin1, bsmin2, bsmax;
      status = opk_get_step_limits(&bsmin1, &bsmin2, &bsmax,
                                   x, opt->box, opt->d, OPK_ASCENT);
      if (bsmin1 < 0) {
        fprintf(stderr, "FIXME: SMIN1 =%g, SMIN2 =%g, SMAX =%g\n",
                bsmin1, bsmin2, bsmax);
      }
      if (status != OPK_SUCCESS) {
        return failure(opt, status);
      }
      if (bsmax <= 0) {
        return failure(opt, OPK_WOULD_BLOCK);
      }
      if (opt->stp > bsmax) {
        opt->stp = bsmax;
      }
      if (stpmax > bsmax) {
        stpmax = bsmax;
      }
      opt->bsmin = bsmin2;
    }

    /* Save current point. */
    if (opt->save_memory) {
      k = SLOT(0);
      opt->x0 = S(k); /* weak reference */
      opt->g0 = Y(k); /* weak reference */
      if (opt->mp == opt->m) {
        --opt->mp;
      }
    }
    COPY(opt->x0, x);
    COPY(opt->g0, (opt->method == OPK_BLMVM ? opt->gp : g));
    opt->f0 = f;

    /* Start the line search and break to take the first step along the line
       search. */
    if (opk_lnsrch_start(opt->lnsrch, f, dtg, opt->stp,
                         stpmin, stpmax) != OPK_LNSRCH_SEARCH) {
      return failure(opt, opk_lnsrch_get_status(opt->lnsrch));
    }
    break;

  default:

    /* There must be something wrong. */
    return opt->task;

  }

  /* Compute a new trial point along the line search. */
  opk_vaxpby(x, 1, opt->x0, -opt->stp, opt->d);
  if (bounded && opt->stp > opt->bsmin) {
    opk_status_t status = opk_project_variables(x, x, opt->box);
    if (status != OPK_SUCCESS) {
      return failure(opt, status);
    }
  }
  return success(opt, OPK_TASK_COMPUTE_FG);
}
Esempio n. 6
0
opk_task_t
opk_iterate_vmlmn(opk_vmlmn_t* opt, opk_vector_t* x,
                  double f, opk_vector_t* g)
{
  double dtg;
  opk_index_t k;
  opk_status_t status;
  opk_lnsrch_task_t lnsrch_task;
  opk_bool_t bounded, final;

  bounded = (opt->bounds != 0);

  switch (opt->task) {

  case OPK_TASK_COMPUTE_FG:

    /* Caller has computed the function value and the gradient at the current
       point. */
    ++opt->evaluations;

    if (opt->evaluations > 1) {
      /* A line search is in progress, check whether it has converged. */
      if (opk_lnsrch_use_deriv(opt->lnsrch)) {
        if (bounded) {
          /* Compute the directional derivative as the inner product between
             the effective step and the gradient. */
#if 0
          if (opt->tmp == NULL &&
              (opt->tmp = opk_vcreate(opt->vspace)) == NULL) {
            return failure(opt, OPK_INSUFFICIENT_MEMORY);
          }
          AXPBY(opt->tmp, 1, x, -1, opt->x0);
          dtg = DOT(opt->tmp, g)/opt->stp;
#else
          dtg = (DOT(x, g) - DOT(opt->x0, g))/opt->stp;
#endif
        } else {
          /* Compute the directional derivative. */
          dtg = -opk_vdot(opt->d, g);
        }
      } else {
        /* Line search does not need directional derivative. */
        dtg = 0;
      }
      lnsrch_task = opk_lnsrch_iterate(opt->lnsrch, &opt->stp, f, dtg);
      if (lnsrch_task == OPK_LNSRCH_SEARCH) {
        /* Line search has not yet converged, break to compute a new trial
           point along the search direction. */
        break;
      }
      if (lnsrch_task != OPK_LNSRCH_CONVERGENCE) {
        status = opk_lnsrch_get_status(opt->lnsrch);
        if (lnsrch_task != OPK_LNSRCH_WARNING ||
            status != OPK_ROUNDING_ERRORS_PREVENT_PROGRESS) {
          return failure(opt, status);
        }
      }
      ++opt->iterations;
    }

    if (bounded) {
      /* Determine the set of free variables. */
      status = opk_box_get_free_variables(opt->w, x, opt->xl, opt->xu,
                                          g, OPK_ASCENT);
      if (status != OPK_SUCCESS) {
        return failure(opt, status);
      }
    }

    /* Check for global convergence. */
    if (opt->method == OPK_VMLMN) {
      /* Compute the Euclidean norm of the projected gradient. */
      opt->gnorm = WNORM2(g);
    } else if (opt->method == OPK_BLMVM) {
      /* Compute the projected gradient and its norm. */
      opk_vproduct(opt->tmp, opt->w, g);
      opt->gnorm = NORM2(opt->tmp);
    } else {
      /* Compute the Euclidean norm of the gradient. */
      opt->gnorm = NORM2(g);
    }
    if (opt->evaluations == 1) {
      opt->ginit = opt->gnorm;
    }
    final = (opt->gnorm <= max3(0.0, opt->gatol, opt->grtol*opt->ginit));
    return success(opt, (final ? OPK_TASK_FINAL_X : OPK_TASK_NEW_X));

  case OPK_TASK_NEW_X:
  case OPK_TASK_FINAL_X:

    /* Compute a new search direction. */
    if (opt->iterations >= 1) {
      /* Update L-BFGS approximation of the Hessian. */
      update(opt, x, (opt->method == OPK_BLMVM ? opt->tmp : g));
    }
    if (apply(opt, g) == OPK_SUCCESS) {
      /* We take care of checking whether -D is a sufficient descent direction
         (that is to say that D is a sufficient ascent direction).  As shown by
         Zoutendijk, this is true if cos(theta) = (D/|D|)'.(G/|G|) is larger or
         equal EPSILON > 0, where G is the gradient at X and D the (ascent for
         us) search direction. */
      dtg = -DOT(opt->d, g);
      if (opt->epsilon > 0 &&
          dtg > -opt->epsilon*NORM2(opt->d)*opt->gnorm) {
        /* We do not have a sufficient descent direction.  Set the directional
           derivative to zero to force using the steepest descent direction. */
        dtg = 0.0;
      }
    } else {
      /* The L-BFGS approximation is unset (first iteration) or failed to
         produce a direction.  Set the directional derivative to zero to use
         the steepest descent direction. */
      dtg = 0.0;
    }

    /* Determine the initial step length. */
    if (dtg < 0) {
      /* A sufficient descent direction has been produced by L-BFGS recursion.
         An initial unit step will be used. */
      opt->stp = 1.0;
    } else {
      /* First iteration or L-BFGS recursion failed to produce a sufficient
         descent direction, use the (projected) gradient as a search
         direction. */
      if (opt->mp > 0) {
        /* L-BFGS recursion did not produce a sufficient descent direction. */
        ++opt->restarts;
        opt->mp = 0;
      }
      if (opt->method == OPK_VMLMN) {
        /* Use the projected gradient. */
        opk_vproduct(opt->d, opt->w, g);
      } else if (opt->method == OPK_BLMVM) {
        /* Use the projected gradient (which has aready been computed and
         * stored in the scratch vector). */
        opk_vcopy(opt->d, opt->tmp);
      } else {
        /* Use the gradient. */
        opk_vcopy(opt->d, g);
      }
      dtg = -opt->gnorm*opt->gnorm;
      if (f != 0) {
        opt->stp = 2*fabs(f/dtg);
      } else {
        /* Use a small step compared to X. */
        double dnorm = opt->gnorm;
        double xnorm = (bounded ? WNORM2(x) : NORM2(x));
        if (xnorm > 0) {
          opt->stp = opt->delta*xnorm/dnorm;
        } else {
          opt->stp = opt->delta/dnorm;
        }
      }
    }

    if (bounded) {
      /* Shortcut the step length. */
      double bsmin, bsmax, wolfe;
      status = opk_box_get_step_limits(&bsmin, &wolfe, &bsmax,
                                       x, opt->xl, opt->xu,
                                       opt->d, OPK_ASCENT);
      if (status != OPK_SUCCESS) {
        return failure(opt, status);
      }
      if (bsmax <= 0) {
        return failure(opt, OPK_WOULD_BLOCK);
      }
      if (opt->stp > bsmax) {
        opt->stp = bsmax;
      }
      opt->bsmin = bsmin;
    }

    /* Save current point. */
#if SAVE_MEMORY
    k = slot(opt, 0);
    opt->x0 = S(k); /* weak reference */
    opt->g0 = Y(k); /* weak reference */
    if (opt->mp == opt->m) {
      --opt->mp;
    }
#endif
    COPY(opt->x0, x);
    COPY(opt->g0, (opt->method == OPK_BLMVM ? opt->tmp : g));
    opt->f0 = f;

    /* Start the line search and break to take the first step along the line
       search. */
    if (opk_lnsrch_start(opt->lnsrch, f, dtg, opt->stp,
                         opt->stp*opt->stpmin,
                         opt->stp*opt->stpmax) != OPK_LNSRCH_SEARCH) {
      return failure(opt, opk_lnsrch_get_status(opt->lnsrch));
    }
    break;

  default:

    /* There must be something wrong. */
    return opt->task;

  }