Exemplo n.º 1
0
void
LMESH::fit(ARRAY<Lvert*>& verts, bool do_gauss_seidel)
{
   static bool debug = Config::get_var_bool("DEBUG_LMESH_FIT",false);
   static bool move_along_normal =
      Config::get_var_bool("FITTING_MOVE_ALONG_NORMAL",false);

   if (verts.empty())
      return;

   // calculate the bounding box of the vertices
   BBOX box;
   int i;
   for (i=0; i<verts.num(); i++)
      box.update(verts[i]->loc());

   double max_err = box.dim().length() * 1e-5;

   int n = verts.num();

   // get original control point locations
   ARRAY<Wpt> C(n);   // original control points
   ARRAY<Wpt> L(n);   // current limit points
   for (i=0; i<n; i++) {
      C += verts[i]->loc();
      L += Wpt::Origin();
   }

   if(debug) {
      cerr << "LMESH::fit- fitting " << n << " vertices"<<endl;
      cerr << "Max_err = " << max_err <<endl;
   }

   // do 50 iterations...
   double prev_err = 0;
   ARRAY<double> errors;
   for (int k=0; k<50; k++) {

      errors.clear();

      double err = 0;

      if (do_gauss_seidel) {

         // Gauss-Seidel iteration: use updated values from the
         // current iteration as they are computed...
         for (int j=0; j<n; j++) {
            // don't need that L[] array...
            Wpt limit;
            verts[j]->limit_loc(limit);
            Wvec delt = C[j] - limit;
            errors+=delt.length();
            err += delt.length();
            if(move_along_normal)
               delt = delt*verts[j]->norm()*verts[j]->norm();
            verts[j]->offset_loc(delt);
         }

      } else {
         // compute the new offsets from the offsets computed in the
         // previous iteration
         int j;
         for (j=0; j<n; j++)
            verts[j]->limit_loc(L[j]);

         for (j=0; j<n; j++) {
            Wvec delt = C[j] - L[j];

            err += delt.length();
            errors+=delt.length();
            if(move_along_normal)
               delt = delt*verts[j]->norm()*verts[j]->norm();
            verts[j]->offset_loc(delt);
         }
      }
      // compute the average error:
      err /= n;

      double avg,std_d,max,min;
      if (debug) {
         if (prev_err != 0) {
            err_msg("Iter %d: avg error: %f, reduction: %f",
                    k, err, err/prev_err);
            statistics(errors,true,&avg,&std_d,&max,&min);
         } else {
            err_msg("Iter %d: avg error: %f", k, err);
            statistics(errors,true,&avg,&std_d,&max,&min);
         }
      } else
         statistics(errors,false,&avg,&std_d,&max,&min);

      prev_err = err;

      if (max < max_err) {
         if(debug) cerr << "Terminating at " << k <<" th iterations"<<endl;
         return;
      }
   }
}
Exemplo n.º 2
0
/////////////////////////////////////
// add()
/////////////////////////////////////
bool
HatchingGroupFixed::add(
   CNDCpt_list &pl,
   const ARRAY<double>&prl,
   int curve_type
   )
{
   int k;
   double a,b;
   Bface *f;

   // It happens:
   if (pl.empty()) 
   {
      err_mesg(ERR_LEV_ERROR, "HatchingGroupFixed:add() - Error: point list is empty!");
      return false;
   }
   if (prl.empty()) 
   {
      err_mesg(ERR_LEV_ERROR, "HatchingGroupFixed:add() - Error: pressure list is empty!");
      return false;
   }

   if (pl.num() != prl.num())
   {
      err_mesg(ERR_LEV_ERROR, "HatchingGroupFixed:add() - gesture pixel list and pressure list are not same length.");
      return false;
   }

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - smoothing gesture.");

   //Smooth the input gesture
   NDCpt_list              smoothpts;
   ARRAY<double>           smoothprl;
   if (!smooth_gesture(pl, smoothpts, prl, smoothprl, _params.anim_style()))
      return false;

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - clipping gesture to model.");

   NDCpt_list              ndcpts;
   ARRAY<double>           finalprl;
   clip_to_patch(smoothpts,ndcpts,smoothprl,finalprl);
   ndcpts.update_length();

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed::add() - Checking gesture silliness.");

   if (HatchingGroupBase::is_gesture_silly(ndcpts,_params.anim_style()))
   {
      err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Punting silly gesture...");
      return false;
   }

   //Even if the user wants to project to create the
   //hatch, we continue with plane cutting to
   //generate a curve we can use to estimate
   //the mesh spacing so that the final projected
   //hatch is sampled evenly on the level of the mesh
   //spacing

   //Get the cutting line
   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - fitting line.");
   if (!fit_line(ndcpts,a,b)) return false;

   //Slide to midpoint if desired
   if (Config::get_var_bool("HATCHING_GROUP_SLIDE_FIT",false,true)) 
      b = ndcpts.interpolate(0.5)[1] - (a*ndcpts.interpolate(0.5)[0]);

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - computing plane.");

   //Find the cutting plane
   Wplane wpPlane;
   f = compute_cutting_plane(_patch, a, b, ndcpts, wpPlane);
   if (!f) return false;
   else
   {
      if (!f->front_facing())
      {
         err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Nearest pt. on fit line hit backfacing surface.");
         return false;
      }
   }

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - slicing mesh.");
   
   //Intersect the mesh to get a 3D curve
   Wpt_list wlList;
   slice_mesh_with_plane(f,wpPlane,wlList);

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - cliping curve to gesture.");

   //Clip end of 3D curve to match gesture
   Wpt_list wlClipList;
   clip_curve_to_stroke(_patch, ndcpts, wlList, wlClipList);
   wlClipList.update_length();

   Wpt_list wlScaledList;

   if (curve_type == HatchingGroup::CURVE_MODE_PROJECT)
   {
      err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed::add() - Projecting to surface.");
     
      //Okay, the user wants to get literal, projected
      //points, so lets do it.  We're careful to
      //toss points that hit the no/wrong mesh
      Wpt_list wlProjList;
      Wpt wloc;

      for (k=0; k<ndcpts.num(); k++)
      {
         f = HatchingGroupBase::find_face_vis(NDCpt(ndcpts[k]),wloc);
         if ((f) && (f->patch() == _patch) && (f->front_facing()))
         {
            wlProjList += wloc;
         }
         else 
         {
            if (!f) 
               err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed while projecting: No hit on a mesh!");
            else if (!(f->patch() == _patch)) 
               err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed while projecting: Hit wrong patch.");
            else if (!f->front_facing())
               err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed while projecting: Hit backfacing tri.");
            else 
               err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed while projecting: WHAT?!?!?!?!");
         }
      }
      if (wlProjList.num()<2)
      {
         err_mesg(ERR_LEV_WARN, "HatchingGroupFixed:add() - Nothing left after projection failures. Punting...");
         return false;
      }
      wlProjList.update_length();

      err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed::add() - Resampling curve.");

      //Resample to even spacing in world space. Sample
      //at a world distance similar to wlClipList, which
      //will be on the order of the mesh resolution
      //unless the gesture fits into one triangle,
      //in which case we ensure a minimum sampling
      int guess = (int)ceil(((double)wlClipList.num()*
                             (double)wlProjList.length())/(double)wlClipList.length());
      int num = max(guess,5);
      double step = 1.0/((double)(num-1));
      for (k=0 ; k<num ; k++) wlScaledList += wlProjList.interpolate((double)k*step);
   }
   else //CURVE_MODE_PLANE
   {
      assert(curve_type == HatchingGroup::CURVE_MODE_PLANE);

      err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed::add() - Resampling curve.");

      //Resample to even spacing in world space. This curve will
      //be sampled on the order of the mesh spacing but we'll
      //not allow the num of samples to drop too low in case
      //the gesture's on the scale of one triangle
      int num = max(wlClipList.num(),5);
      double step = 1.0/((double)(num-1));
      for (k=0 ; k<num ; k++) wlScaledList += wlClipList.interpolate((double)k*step);
   }

   // Convert back to 2D
   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed:add() - converting to 2D.");
   NDCZpt_list ndczlScaledList;
   for (k=0;k<wlScaledList.num();k++) ndczlScaledList += NDCZpt(_patch->xform()*wlScaledList[k]);
   ndczlScaledList.update_length();

   // Calculate pixel length of hatch
   double pix_len = ndczlScaledList.length() * VIEW::peek()->ndc2pix_scale();

   if (pix_len < 8.0)
   {
      err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Stroke only %f pixels. Probably an accident. Punting...", pix_len);
      return false;
   }

   ARRAY<HatchingFixedVertex>    verts;
   Wpt_list                      pts;
   ARRAY<Wvec>                   norms;

   err_mesg_cond(debug, ERR_LEV_SPAM, "HatchingGroupFixed::add() - Final sampling.");

   for (k=0; k<ndczlScaledList.num(); k++) {

      Wpt wloc;
      f = HatchingGroupBase::find_face_vis(NDCpt(ndczlScaledList[k]),wloc);

      if ((f) && (f->patch() == _patch) && (f->front_facing()))
      {
         Wvec bc;
         Wvec norm;

         //f->project_barycentric(wloc,bc);
         f->project_barycentric_ndc(NDCpt(ndczlScaledList[k]),bc);

         Wvec bc_old = bc;
         Bsimplex::clamp_barycentric(bc);
         double dL = fabs(bc.length() - bc_old.length());

         if (bc != bc_old)
         {
            err_mesg(ERR_LEV_INFO, 
               "HatchingGroupFixed::add() - Baycentric clamp modified result: (%f,%f,%f) --> (%f,%f,%f) Length Change: %f", 
                  bc_old[0], bc_old[1], bc_old[2], bc[0], bc[1], bc[2], dL);
         }
         if (dL < 1e-3)
         {
            verts += HatchingFixedVertex(f->index(),bc);
            f->bc2norm_blend(bc,norm);
            pts += wloc;
            norms += norm;
         }
         else
         {
            err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Change too large due to error in projection. Dumping point...");
         }
      }
      else 
      {
         if (!f) 
            err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed in final lookup: No hit on a mesh!");
         else if (!(f->patch() == _patch)) 
            err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed in final lookup: Hit wrong patch.");
         else if (!(f->front_facing())) 
            err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed in final lookup: Hit backfracing tri.");
         else
            err_mesg(ERR_LEV_WARN, "HatchingGroupFixed::add() - Missed in final lookup: WHAT?!?!?!?!");
      }
   }

   if (pts.num()>1)
   {
      //XXX  - Okay, using the gesture pressure, but no offsets.
      //Need to go back and add offset generation...

      BaseStrokeOffsetLISTptr ol = new BaseStrokeOffsetLIST;

      ol->set_replicate(0);
      ol->set_hangover(1);
      ol->set_pix_len(pix_len);

      ol->add(BaseStrokeOffset( 0.0, 0.0, 
            finalprl[0],                  BaseStrokeOffset::OFFSET_TYPE_BEGIN));
      for (k=1; k< finalprl.num(); k++)
            ol->add(BaseStrokeOffset( (double)k/(double)(finalprl.num()-1), 0.0, finalprl[k], BaseStrokeOffset::OFFSET_TYPE_MIDDLE));

      ol->add(BaseStrokeOffset( 1.0, 0.0, finalprl[finalprl.num()-1],   BaseStrokeOffset::OFFSET_TYPE_END));

      if (base_level(num_base_levels()-1)->pix_size() > 0)
      {
         assert(_params.anim_style() != HatchingGroup::STYLE_MODE_NEAT);

         add_base_level();

         // Make sure we can see it whil we're editing!
         base_level(num_base_levels()-1)->set_desired_frac(1.0);

      }

      base_level(num_base_levels()-1)->add_hatch(
         new HatchingHatchFixed(
            base_level(num_base_levels()-1),_patch->mesh()->pix_size(),verts,pts,norms,ol) );

      return true;
   }
   else
   {
      err_mesg(ERR_LEV_WARN, "HatchingGroupFixed:add() - All lookups are bad. Punting...");
      return false;
   }



   return true;

}