예제 #1
0
파일: bedge.C 프로젝트: karmakat/jot-lib
void
Bedge::fix_multi()
{
    // If any faces in the _adj listed are labelled "primary",
    // then move them to a primary slot (_f1 or _f2). Used when
    // reading a mesh from file, when primary/secondary face
    // labels are specified only after all faces are created.

    if (!_adj)   // no _adj, so no fixing required
        return;

    // It's not possible if the total number of "primary" faces
    // exceeds 2.
    if (nfaces_satisfy(PrimaryFaceFilter()) > 2) {
        cerr << "Bedge::fix_multi: error: more than 2 primary faces" << endl;
        return;
    }

    // Work backwards to remove items from the list:
    for (int i=_adj->num()-1; i>=0; i--) {
        Bface *face = (*_adj)[i];
        if (face->is_primary()) {
            promote(face);
        }
    }
}
예제 #2
0
/////////////////////////////////////
// clip_to_patch()
/////////////////////////////////////
void    
HatchingGroupFixed::clip_to_patch(
   CNDCpt_list &pts,        NDCpt_list &cpts,
   const ARRAY<double>&prl, ARRAY<double>&cprl ) 
{
   int k, started = 0;
   Bface *f;
   Wpt foo;

   for (k=0; k<pts.num(); k++)
   {
      f = find_face_vis(pts[k], foo);
      if ((f) && (f->patch() == _patch))
      {
         started = 1;
         cpts += pts[k];
         cprl += prl[k];
      }
      else
      {
         if (started)
         {
            k=pts.num();
         }
      }
   }
}
예제 #3
0
파일: bface.cpp 프로젝트: QuLogic/jot-lib
bool
Bface::get_quad_verts(Bvert*& a, Bvert*& b, Bvert*& c, Bvert*& d) const
{
   //  Return CCW verts a, b, c, d as in the picture, orienting
   //  things so that the weak edge runs NE as shown:
   //
   //    d ---------- c = w->v2()              ^      
   //    |          / |                        |       
   //    |        /   |                        |       
   //    |    w /     |       tan1        tan2 |       
   //    |    /       |    -------->           |       
   //    |  /     f   |                        |       
   //    |/           |                                
   //    a ---------- b                                
   //     = w->v1()
   //
   if (!is_quad())
      return 0;

   Bedge* w = weak_edge();
   Bface* f = w->ccw_face(w->v2());
   a = w->v1();
   b = f->next_vert_ccw(a);
   c = w->v2();
   d = f->quad_vert();

   return true;
}
예제 #4
0
파일: bedge.C 프로젝트: karmakat/jot-lib
Patch*
Bedge::patch() const
{
    Bface* f = frontfacing_face();
    f = f ? f : _f1 ? _f1 : _f2;
    return f ? f->patch() : 0;
}
예제 #5
0
/////////////////////////////////////
// notify_change()
/////////////////////////////////////
void
HatchingHatchFixed::notify_change(BMESHptr m, BMESH::change_t chg)
{
   assert(chg == BMESH::VERT_POSITIONS_CHANGED);

   vector<HatchingFixedVertex>::size_type k;

   if (_verts.size() > 0) {
      //Sanity check
      assert(_verts.size() == _pts.size());
      assert(_verts.size() == _norms.size());

      for (k=0; k < _verts.size(); k++) {
         Bface *f = m->bf(_verts[k].ind);
         assert(f);
         f->bc2pos(_verts[k].bar,_pts[k]);
         f->bc2norm_blend(_verts[k].bar,_norms[k]);
      }

      //Clear these cached values so they regenerate
      _real_pts.clear();      
      _real_norms.clear();
      _real_good.clear();

   }
   else
   {
      err_mesg(ERR_LEV_WARN, "HatchingHatchFixed::notify_change() - Verts changed, but we can't update fixed hatches!!!"); 
   }

}
예제 #6
0
/////////////////////////////////////
// store_visibility()
/////////////////////////////////////
bool
HatchingGroupFixed::store_visibility(HatchingLevelBase *hlb)
{
   assert(!_complete);

   HatchingLevelBase::size_type k;
   NDCZpt_list::size_type j;

   NDCZpt_list pts, hull;

   //One side
   for (k=0; k<hlb->size(); k++)
      for (j=0; j<(*hlb)[k]->get_pts().size(); j++)
         pts.push_back(NDCZpt( _patch->xform() * ((*hlb)[k]->get_pts()[j]) ));

   compute_convex_hull(pts,hull);

   if (hull.size() == 0) {
      err_mesg(ERR_LEV_WARN, 
         "HatchingGroupFixed:store_visibility() - Error!! There were %d convex hull verts found from %d hatch verts.",
         hull.size(), pts.size());
      return false;
   } else {
      err_mesg(ERR_LEV_INFO, 
         "HatchingGroupFixed:store_visibility() - There were %d convex hull verts found from %d hatch verts.",
         hull.size(), pts.size());
   }

   Bface *f = nullptr;
   XYpt center = NDCpt(hull.average());
   BMESHray r(center);
   VIEW::peek()->intersect(r);
   f = r.face();

   if (!f) 
   {
      err_mesg(ERR_LEV_WARN, "HatchingGroupFixed:store_visibility() - Seed face intersect failed (hull center)!");
   }
        
   if (!(_patch == f->patch())) 
   {
      err_mesg(ERR_LEV_WARN, "HatchingGroupFixed:store_visibility() - Seed face intersected wrong patch (hull center)!!");
      return false;
   }

   CBface_list & faces = f->mesh()->faces();
   for (k=0; k < faces.size(); k++) faces[k]->clear_bit(1);

   int ctr = 0;
        
   ctr = recurse_visibility(f,hull);

   err_mesg(ERR_LEV_INFO, "HatchingGroupFixed:store_visibility() - There were %d faces in the visible region (from %d).", ctr, faces.size());

   _group->patch()->changed();

   if (ctr>0) return true;
   else return false;
}
예제 #7
0
int
SELECT_WIDGET::tap_cb(CGESTUREptr& g, DrawState*& s)
{
   err_adv(debug, "SELECT_WIDGET::tap_cb()");

   assert(g && g->is_tap());


   if (_mode==SLASH_SEL)
   {
	   //pattern editing
		Bface* f = find_face(g->start(),0.25,MIN_PIX_AREA);
		if (f) {
			if (select_list.contains(f)||select_list.contains(f->quad_partner()))
			{
	
				//get whichever part of the quad is in the selection list
				int temp = select_list.contains(f) ? select_list.get_index(f)+1 : select_list.get_index(f->quad_partner())+1 ;


				if (temp>end_face) //user selected the end face
				{
					end_face=temp;
				}
				else //user is selecting a pattern
				{
					if (pattern<temp)
						pattern=temp;

					//select/deselect face
					if (temp < MAX_PATTERN_SIZE)
					pattern_array[temp]=!pattern_array[temp];

				}
				return 1;
			}
			else
				cerr << "tap found a NULL face !" << endl;
		}

		return cancel_cb(g,s);


   }
   else
   {
   // Tap a selected face near the middle to deselect it:
	if (try_deselect_face(g->center(), 0.25))
		  return 1;

   // Tap edge to deselect
	if (try_deselect_edge(g->center()))
		  return 1;

   }
   // Otherwise, turn off SELECT_WIDGET
   
   return cancel_cb(g,s);
}
예제 #8
0
//! Same as try_select_face(), but deselects.
//! Also requires the found face is currently selected.
bool
SELECT_WIDGET::try_deselect_face(CPIXEL &pix, double margin)
{

   Bface* f = find_face(pix, margin, MIN_PIX_AREA);
   if (!(f && f->is_selected()))
      return false;
   WORLD::add_command(new MESH_DESELECT_CMD(f));
   return true;
}
예제 #9
0
파일: bface.cpp 프로젝트: QuLogic/jot-lib
inline Bface*
check_partner(Bface* f)
{
   // helper function used in quad_complete_faces() below

   if (!(f && f->is_quad()))
      return nullptr;
   Bface* p = f->quad_partner();
   return p->flag() ? nullptr : p;
}
예제 #10
0
int
INFLATE::stroke_cb(CGESTUREptr& gest, DrawState*& s)
{
   err_adv(debug, "INFLATE::stroke_cb");

   reset_timeout();

   // Verify that we have a starting face
   if ( _orig_face ) {
      Bface* face = 0;

      // Check that the stroke is straight enough to represent a line
      if (!(gest->straightness() > 0.8)) {
         err_adv(debug, "INFLATE::stroke_cb: gesture not straight");
         return false;
      }

      // Check that the gesture starts on the mesh
      Bsurface::hit_ctrl_surface(gest->start(), 1, &face);
      if (!(face)) {
         err_adv(debug, "INFLATE::stroke_cb: can't get hit face");
         return false;
      }

      // create VEXELs for the gesture and the face normal
      VEXEL fvec  = VEXEL(face->v1()->loc(), face->norm());
      VEXEL fgest = gest->endpt_vec();

      // If gesture nearly parallel to normal:
      double a = rad2deg(line_angle(fvec,fgest));
      err_adv(debug, "INFLATE::stroke_cb: angle: %f %s",
              a, (a > 15) ? "(bad)" : "(good)");
      if (a > 15) {
         // Fail if angle is too extreme
         WORLD::message("Bad angle");
         return 0;
      }

      // calculate extrude width
      double dist = fgest.length()/fvec.length();

      err_adv(debug, "INFLATE::stroke_cb: strong_edge_len: %f, gest_len: %f, \
fvect_len: %f", avg_strong_edge_len(face), fgest.length(), fvec.length() );

      // Convert to relative to local edge length
      dist /= avg_strong_edge_len(face);
      if (fvec*fgest<0)
         dist=-dist; // Get the sign right

      // Store the new inflate distance
      _preview_dist = dist;
   }

   return 1;    // we used up the gesture...
}
예제 #11
0
int
XformPen::tap_cb(CGESTUREptr& tap, DrawState*& s)
{
   assert(tap);
   if (tap->is_double_tap()) {
      // should never happen given order of arcs in XformPen constructor
      cerr << "XformPen::tap_cb: error: gesture is double tap"
           << endl;
      return 0;
   }

   // tap on cursor?
   BMESHray ray(tap->center());
   _view->intersect(ray);
   Cursor3D* c = Cursor3D::upcast(ray.geom());
   if (c) {
      err_adv(debug, "XformPen::tap_cb: hit axis");
      c->handle_gesture(tap);
      return 0;
   }

   // tap on mesh?
   _mesh = 0;
   Bface* f = cur_face();
   if (!f) {
      err_adv(debug, "XformPen::tap_cb: missed face");
      return cancel_cb(tap, s);
   }
   BMESH* m = f->mesh();
   if (!m) {
      err_adv(debug, "XformPen::tap_cb: hit face, no mesh");
      return cancel_cb(tap, s);
   }
   GEOMptr g = bmesh_to_geom(m);
   if (!g) {
      err_adv(debug, "XformPen::tap_cb: hit mesh, no geom");
      return cancel_cb(tap, s);
   }
   // skip floor:
   if (FLOOR::isa(g)) {
      err_adv(debug, "XformPen::tap_cb: hit floor, skipping...");
      return cancel_cb(tap, s);
   }

   // tap on ordinary mesh (not floor):
   _mesh = m;
   assert(_mesh);
   BMESH::set_focus(_mesh, f->patch());
   FLOOR::show();
   FLOOR::realign(_mesh,0); // 0 = no undo command

   Cursor3D::attach(g);

   return 0;
}
예제 #12
0
/*!
	Find the nearest Bface at the given screen location pix,
	(within the search radius rad), then re-map it to the
	corresponding edit-level face and barycentric
	coordinates bc.
 */
inline Bface*
find_face(CPIXEL& pix, double rad, Wvec& bc)
{

   Bface* f = VisRefImage::get_edit_face(bc, pix, rad);

   // Only select faces belonging to a TEXBODY
   if (!(f && f->mesh() && TEXBODY::isa(f->mesh()->geom())))
      return 0;

   return f;
}
예제 #13
0
void
EdgeStrip::build_ccw_boundaries(
   CBedge_list& edges,
   CSimplexFilter& face_filter
   )
{
   // Similar to previous...
   //
   // XXX - needs comments

   // Clear edge flags to screen for unreached edges:
   // set edge flags to 1 in 1-ring of verts,
   // then clear edge flags of internal edges
   set_adjacent_edges(edges.get_verts(), 1);
   edges.clear_flags();

   // get an edge filter that accepts "boundary" edges WRT the
   // given face filter
   BoundaryEdgeFilter boundary(face_filter);

   // Pull out the edge tips:
   Bedge_list tips = edges.filter(ChainTipEdgeFilter(boundary));

   // Construct the filter that screens out previously reached
   // edges:
   UnreachedSimplexFilter unreached;
   AndFilter       wanted = unreached + boundary;

   int k;

   // Start from all the tips first:
   for (k=0; k<tips.num(); k++) {
      Bedge* e = tips[k];
      Bvert* v = (e->v2()->degree(boundary) != 2) ? e->v2() : e->v1();
      Bface* f = e->screen_face(face_filter);
      assert(f); // e must have 1 face satisfying the filter

      // If this will start out running ccw, take it.
      // otherwise skip:
      if (f->next_vert_ccw(v) == e->other_vertex(v))
         build(v, e, wanted);
   }

   // Now check the rest:
   for (k=0; k<edges.num(); k++) {
      Bedge* e = edges[k];
      Bface* f = e->screen_face(face_filter);
      assert(f); // e must have 1 face satisfying the filter

      // Go CCW around faces
      build(f->leading_vert_ccw(e), e, wanted);
   }
}
예제 #14
0
//   2
// 3|_|1
//   0
Bface*   
ProxySurface::neighbor_face(int dir, Bface* face)
{
    UVpt base_uv = baseUVpt(face);
    
	UVpt uv1, uv2, uv3, uv4;
	switch (dir)
	{
	case 0:
		uv1 = UVpt(base_uv[0], base_uv[1]);
	    uv2 = UVpt(base_uv[0]+1, base_uv[1]);
		uv3 = UVpt(base_uv[0], base_uv[1]-1);
		uv4 = UVpt(base_uv[0]+1, base_uv[1]-1);
		break;
	case 1:
		uv1 = UVpt(base_uv[0]+1, base_uv[1]);
	    uv2 = UVpt(base_uv[0]+2, base_uv[1]);
		uv3 = UVpt(base_uv[0]+1, base_uv[1]+1);
		uv4 = UVpt(base_uv[0]+2, base_uv[1]+1);
		break;
	case 2:
		uv1 = UVpt(base_uv[0], base_uv[1]+1);
	    uv2 = UVpt(base_uv[0]+1, base_uv[1]+1);
		uv3 = UVpt(base_uv[0], base_uv[1]+2);
		uv4 = UVpt(base_uv[0]+1, base_uv[1]+2);
		break;
	case 3:
		uv1 = UVpt(base_uv[0]-1, base_uv[1]);
	    uv2 = UVpt(base_uv[0], base_uv[1]);
		uv3 = UVpt(base_uv[0], base_uv[1]+1);
		uv4 = UVpt(base_uv[0]-1, base_uv[1]+1);
		break;
	default:
		cerr << "ProxySurface::neighbor_face: invalid diraction" << endl;
		assert(0);
	}

    Bvert* v1 = get_vert_grid(uv1);
	Bvert* v2 = get_vert_grid(uv2);
	Bvert* v3 = get_vert_grid(uv3);
	Bvert* v4 = get_vert_grid(uv4);
	
	if(!v1 || !v2 || !v3 || !v4)
		return 0;

	Bface* n = lookup_quad(v1, v2, v3, v4);
	assert(n);
	assert(n->is_quad());
	n = (n->is_quad_rep()) ? n : n->quad_partner();
	return n;
}
예제 #15
0
파일: bface.cpp 프로젝트: QuLogic/jot-lib
inline void
get_other_face(CBedge* e, CBface* f, Bsimplex_list& ret)
{
   assert(e && f && f->contains(e));
   if (e->is_weak())
      return;
   Bface* g = e->other_face(f);
   if (!g)
      return;
   ret.push_back(g);
   g = g->quad_partner();
   if (g)
      ret.push_back(g);
}
예제 #16
0
void 
visit(OctreeNode* node,  
      double regularity, Bface_list& flist, ARRAY<Wvec>& blist)
{
   if (node->get_leaf()) {
      if (node->get_disp()) {
         // subdivision
         ARRAY<QuadtreeNode*> fs;
         Bface_list temp;
         for (int i = 0; i < node->intersects().num(); i++) {
            Bface* f = node->intersects()[i];
            temp += f;
            fs += new QuadtreeNode(f->v1()->loc(), f->v2()->loc(), f->v3()->loc());
            fs.last()->build_quadtree(node, regularity);
            fs.last()->set_terms();
         }

         // assign weights
         assign_weights(fs, regularity, node->center());
         
         // pick a triangle
         int t = pick(fs);

         // moved below; want to ensure flist and blist stay in sync:
//         flist += temp[t];

         //set node face
         Bface_list ftemp;
         ftemp += temp[t];
         node->set_face(ftemp);

         // pick a point
         int p = pick(fs[t]->terms());
         if (p != -1) {
            Wvec bc;
            temp[t]->project_barycentric(fs[t]->terms()[p]->urand_pick(), bc);
            blist += bc;
            flist += temp[t]; // moved from above
            node->set_point(bc);
         }

         for (int i = 0; i < fs.num(); i++)
            delete fs[i];
         fs.clear();
      }
   } else {
      for (int i = 0; i < 8; i++)
         visit(node->get_children()[i], regularity, flist, blist);
   }
}
예제 #17
0
int
INFLATE::line_cb(CGESTUREptr& gest, DrawState*& s)
{
   // Activity occurred to extend the deadline for fading away:
   reset_timeout();

   err_adv(debug, "INFLATE::line_cb");

   // Verify that we have a starting face
   if ( _orig_face ) {
      // Check that the stroke is straight enough to represent a line
      Bface* face = 0;
      if (!(gest->straightness() > 0.8)) {
         err_adv(debug, "INFLATE::line_cb: gesture not straight");
         return false;
      }

      // Check that the gesture starts on the mesh
      Bsurface::hit_ctrl_surface(gest->start(), 1, &face);
      if (!(face)) {
         err_adv(debug, "INFLATE::line_cb: can't get hit face");
         return false;
      }

      // create VEXELs for the gesture and the face normal
      VEXEL fvec  = VEXEL(face->v1()->loc(), face->norm());
      VEXEL fgest = gest->endpt_vec();

      // If gesture nearly parallel to normal:
      double a = rad2deg(line_angle(fvec,fgest));
      err_adv(debug, "INFLATE::line_cb: angle: %f %s",
              a, (a > 15) ? "(bad)" : "(good)");
      if (a > 15) {
         return false;
      }

      // calculate extrude width
      double dist = fgest.length()/fvec.length();
      if (fvec*fgest<0)
         dist=-dist; // Get the sign right

      _preview_dist = dist;
   }

   // get here if nothing happened...
   // don't cancel (which would deactivate the widget),
   // just return 1 to indicate that we used up the gesture:
   return 1;
}
예제 #18
0
/*****************************************************************
 * UVdata
 *****************************************************************/
UVdata::UVdata(Bsimplex* s) :
   SimplexData(key(), s),
   _uv_valid(false),
   _calc_type(SIMPLE_CALC),     // default: simple subdiv scheme
   _did_subdiv(false),
   _mapping(0) 
{
   // The constructor is called (internally) only when the given
   // simplex does not already have a UVdata associated with it.
   //
   // UVdata gets looked-up by its classname.

   // For face data, slather UVdatas all over the
   // vertices and edges:
   if (is_face(s)) {
      Bface* f = (Bface*)s;
      get_data(f->v1());
      get_data(f->v2());
      get_data(f->v3());
      get_data(f->e1());
      get_data(f->e2());
      get_data(f->e3());

      // XXX - 
      //   If the UVdata is created on a pre-existing face
      //   that has already generated its subdivision
      //   elements, then currently the UVdatas won't be
      //   created on those subdivision elements ...
   }
}
예제 #19
0
void 
OctreeNode::build_octree(int height) 
{

   if (_leaf || _height == height) 
      return;

   int i, j;
   Wpt_list pts;
   points(pts);

   for (i = 0; i < 8; i++) {
      _children[i] = new OctreeNode((pts[0]+pts[i])/2, (pts[i]+pts[7])/2, 
                                    _height+1, this);
   }

   for (j = 0; j < _intersects.num(); j++) {
      Bface* f = _intersects[j];

      for (i = 0; i < 8; i++) {
         OctreeNode* n = _children[i];
         if (n->contains(f->v1()->loc()) && n->contains(f->v2()->loc())
             && n->contains(f->v3()->loc())) {
            n->intersects() += f;
            break;
         } else if (n->overlaps(bface_bbox(f))) {
            n->intersects() += f;
         }
      }
   }

   for (i = 0; i < 8; i++) {
      if (_height+1 == height) {
         _children[i]->set_leaf(true);
         _children[i]->set_disp(true);
      }
      if (_children[i]->intersects().empty()) {
         _children[i]->set_leaf(true);
         _children[i]->set_disp(false);
      }
      _children[i]->build_octree(height);
   }

}
예제 #20
0
bool
SELECT_WIDGET::select_faces(CPIXEL_list& pts)
{
   err_adv(debug, "SELECT_WIDGET::select_faces:");

   if (pts.num() < 2) {
      err_adv(debug, "  too few points: %d", pts.num());
      return false;
   }

   Bface* f = find_face(pts[0], 0.25, MIN_PIX_AREA);
   if (!(f && f->is_selected())) {
      err_adv(debug, "  bad starter face");
      return false;
   }
   
//    // XXX - Old code (to be deleted).  Selects faces individually instead of as
//    // a group:
//    for (int i=0; i<pts.num(); i++)
//       try_select_face(pts[i], 0.1);

   Bface_list flist;

   for(int i = 0; i < pts.num(); ++i){
      
      f = find_face(pts[i], 0.1, MIN_PIX_AREA);
      if (!f || f->is_selected())
         continue;
      flist += f;
      
   }
   
   if(flist.num() > 0){
      
      WORLD::add_command(new MESH_SELECT_CMD(flist));
      err_adv(debug, "  succeeded");
      return true;
      
   }
   
   err_adv(debug, "  no faces selected");
   return false;
   
}
예제 #21
0
파일: bedge.C 프로젝트: karmakat/jot-lib
Bface*
Bedge::lookup_face(CBedge *e) const
{
    // Reject null pointers.
    // Also, it's not helpful if the edge is this one.
    if (!e || this == e)
        return 0;

    // Return an adjacent Bface containing the edge

    if (_f1 && _f1->contains(e))
        return _f1;
    if (_f2 && _f2->contains(e))
        return _f2;
    if (_adj) {
        for (int i=0; i<_adj->num(); i++) {
            Bface* f = (*_adj)[i];
            if (f->contains(e))
                return f;
        }
    }
    return 0;
}
예제 #22
0
/////////////////////////////////////
// clip_to_patch()
/////////////////////////////////////
void    
HatchingGroupFixed::clip_to_patch(
   CNDCpt_list &pts,        NDCpt_list &cpts,
   const vector<double>&prl, vector<double>&cprl )
{
   NDCpt_list::size_type k;
   bool started = false;
   Bface *f;
   Wpt foo;

   for (k=0; k<pts.size(); k++) {
      f = find_face_vis(pts[k], foo);
      if (f && f->patch() == _patch) {
         started = true;
         cpts.push_back(pts[k]);
         cprl.push_back(prl[k]);
      } else {
         if (started) {
            k=pts.size();
         }
      }
   }
}
예제 #23
0
파일: bedge.C 프로젝트: karmakat/jot-lib
Bface*
Bedge::lookup_face(CBvert *v) const
{
    // Reject null pointers.
    // Also, it's not helpful if the vertex belongs
    // to this edge.
    if (!v || this->contains(v))
        return 0;
    // Return an adjacent Bface containing the vertex

    if (_f1 && _f1->contains(v))
        return _f1;
    if (_f2 && _f2->contains(v))
        return _f2;
    if (_adj) {
        for (int i=0; i<_adj->num(); i++) {
            Bface* f = (*_adj)[i];
            if (f->contains(v))
                return f;
        }
    }
    return 0;
}
예제 #24
0
int 
SELECT_WIDGET:: slash_cb(CGESTUREptr& gest, DrawState*& s)
{

	
	if (_mode==SEL_FACE) //widget is in face selection mode 
	{
		select_list.clear();

		Bface* f = find_face(gest->start(),0.25,MIN_PIX_AREA);
		// f should be the currently selected face
		if (f && f->is_selected() && f->is_quad())
		{
	
			f=f->quad_rep();

		//line in screen space coresponding to the slash
		PIXELline slash(gest->start(),gest->end());

		Bedge *e1,*e2,*e3,*e4;
		Bedge* edge = 0;
		
		//get and test the quad edges against the stroke line 
		f->get_quad_edges(e1,e2,e3,e4);

		if( e1->pix_line().intersect_segs(slash) )
		{
			edge=e1;
		}
		else
		if( e2->pix_line().intersect_segs(slash) )
		{
			edge=e2;
		}
		else
		if( e3->pix_line().intersect_segs(slash) )
		{
			edge=e3;
		}
		else
		if( e4->pix_line().intersect_segs(slash) )
		{
			edge=e4;
		}
		else
		{
			//error
			cerr << "ERROR no intersection" << endl;
			return 1;
		}
		
			//walk the geometry and select faces

			Bface* fn = f;
			do
			{			
				if (!fn->is_selected())	
					select_list +=fn;
		
				assert(edge);			//I'm paranoid too
				assert(edge->f1()!=edge->f2());

				//grabs the face on the other side of the edge
				//even if we are not directly adjacent to this edge
				fn=fn->other_quad_face(edge);
				
				if (fn) //if a valid face than advance the edge pointer
				{
					assert(edge!=fn->opposite_quad_edge(edge));
					edge = fn->opposite_quad_edge(edge);
	
					fn = fn->quad_rep(); //all faces on the selection list are rep faces

				}
				else
					cerr << "No face on the other side of the edge" << endl;

			} //quit if not a valid face or not a quad
			while(fn&&(fn->is_quad())&&edge&&(fn!=f));


			_mode=SLASH_SEL;  //go into pattern editing mode
			end_face=0;		//prepare the 2nd step data
			pattern=0;
			for (int i=0; i<MAX_PATTERN_SIZE; i++)
				pattern_array[i]=1; //fill the default pattern with ones
		}
		else
			cerr << "This is not a quad" << endl;

	}
	else
		if( _mode==SLASH_SEL)//pattern editing mode
		{ //activates upon second slash motion


			//adds the entire list to the selected group
			//undo deselects the entire group

			Bface_list final_list;

			//copy the face pointers to the final list 
			//using the pattern as a repeating template
			//and stop at the end face
			for (int i = 0; i<(end_face ? (end_face) : select_list.num()) ; i++)
			{
				if (pattern ? pattern_array[(i%pattern)+1] : 1)
				final_list+=select_list[i];
			}

		
		   WORLD::add_command(new MESH_SELECT_CMD(final_list));
		
		   return cancel_cb(gest,s);

		}
		else
			cerr << "wrong mode " << endl;
	return 1;
}
예제 #25
0
bool
TriStrip::build(
   Bface* start,        // build a new strip starting here.
   Bface_list& stack    // used to build nearby parallel strips
   )
{
   // a set flag means this face is already in a TriStrip
   assert(!start->flag());

   // start fresh
   reset();

   // repeat 1st vertex if needed
   if (!start->orient_strip())
      start->orient_strip(start->v1());

   // get the starting vert. i.e., the strip will
   // continue onto the next face across the edge
   // opposite this vertex.
   Bvert *a = start->orient_strip(), *b, *c;

   // squash and stretch
   start = backup_strip(start,a);

   if (!start) {
      // should never happen, but can happen
      // if there are inconsistently oriented faces
      err_msg("TriStrip::build: error: backup_strip() failed");
      err_msg("*** check mesh for inconsistently oriented faces ***");

      return 0;
   }

   // claim it
   claim_face(start);

   // record direction of strip on 1st face:
   start->orient_strip(a);

   // faces alternate CCW / CW
   if (_orientation) {
      c = start->next_vert_ccw(a);
      b = start->next_vert_ccw(c);
   } else {
      b = start->next_vert_ccw(a);
      c = start->next_vert_ccw(b);
   }

   add(a,start);
   add(b,start);
   add(c,start);

   Bface* opp;
   if ((opp = start->opposite_face(b)) && is_cleared(opp) &&
      opp->patch() == start->patch()) {
      opp->orient_strip(_orientation ? a : c);
      stack.push_back(opp);
   }

   int i=_orientation;
   Bface* cur = start;
   while ((cur = cur->next_strip_face()) && !is_claimed(cur)) {
      claim_face(cur);
      i++;
      a = b;
      b = c;
      c = cur->other_vertex(a,b);
      cur->orient_strip(a);

      if ((opp = cur->opposite_face(b)) && is_cleared(opp) &&
          opp->patch() == start->patch()) {
         opp->orient_strip(i % 2 ? a : c);
         stack.push_back(opp);
      }

      add(c,cur);
   }

   return 1;
}
예제 #26
0
/////////////////////////////////////
// recurse()
/////////////////////////////////////
void      
UVMapping::recurse(Bface *seed_f, rec_fun_t fun )
{           
   int k;
//   bool done = false;

   Bface*                  f;
   ARRAY<Bface*>   faces;

   assert(seed_f);

   faces.push(seed_f);

   while (faces.num()>0)
      {
         //Remove oldest face from end of queue
         f = faces.pop();

         //Skip if already seen
         if (!f->is_set(1))
            {
               f->set_bit(1);

               //If we get here, then this face *should* have uvdata
               //and *should* be unmapped
               UVdata* uvdata = UVdata::lookup(f);
               assert(uvdata);
               assert(uvdata->mapping()==0);

               //Do the action (add to map, or update limits, etc.)
               (this->*fun)(f);

               for (k=1; k<=3; k++)
                  {
                     Bedge *nxt_e = f->e(k);
                     assert(nxt_e);
                     Bface *nxt_f = nxt_e->other_face(f);
                     if (nxt_f)
                        {
                           UVdata *nxt_uvdata = UVdata::lookup(nxt_f);
                           if (nxt_uvdata)
                              {
                                 UVpt uva = uvdata->uv(k);
                                 UVpt uvb = uvdata->uv((k==3)?(1):(k+1));
                                 int nxt_k = ( (nxt_f->e1()==nxt_e)?(1):
                                               ((nxt_f->e2()==nxt_e)?(2):
                                                ((nxt_f->e3()==nxt_e)?(3):(0))));
                                 assert(nxt_k);
                                 UVpt nxt_uva = nxt_uvdata->uv(nxt_k);
                                 UVpt nxt_uvb = nxt_uvdata->uv((nxt_k==3)?(1):(nxt_k+1));

                                 //If neighboring face has uv, and the they match
                                 //we recurse into this next face
                                 if ((uva==nxt_uvb)&&(uvb==nxt_uva))
                                    {
                                       //Add to front of queue
                                       faces.push(nxt_f);
                                    }
                                 else {
                                    //Nothing
                                 }
                              }
                        }
                  }
            }
      }
}
예제 #27
0
/////////////////////////////////////
// recurse_wrapping()
/////////////////////////////////////
void      
UVMapping::recurse_wrapping(Bface *seed_f)
{           
   int k;

   Bface *f;
   ARRAY<Bface*> faces;

   assert(seed_f);

   faces.push(seed_f);

   while (faces.num()>0)
   {
      //Remove face from end of queue
      f = faces.pop();

      //Skip if already seen
      if (!f->is_set(1))
      {
         f->set_bit(1);

         //If we get here, then this face *should* have uvdata
         //and *should* be allready be mapped to us!
         UVdata* uvdata = UVdata::lookup(f);
         assert(uvdata);
         assert(uvdata->mapping()==this);

         for (k=1; k<=3; k++)
         {
            Bedge *nxt_e = f->e(k);
            assert(nxt_e);
            Bface *nxt_f = nxt_e->other_face(f);
            if (nxt_f)
            {
               UVdata *nxt_uvdata = UVdata::lookup(nxt_f);
               if (nxt_uvdata)
               {
                  UVpt uva = uvdata->uv(k);
                  UVpt uvb = uvdata->uv((k==3)?(1):(k+1));
                  int nxt_k = ( (nxt_f->e1()==nxt_e)?(1):
                                ((nxt_f->e2()==nxt_e)?(2):
                                 ((nxt_f->e3()==nxt_e)?(3):(0))));
                  assert(nxt_k);
                  UVpt nxt_uva = nxt_uvdata->uv(nxt_k);
                  UVpt nxt_uvb = nxt_uvdata->uv((nxt_k==3)?(1):(nxt_k+1));

                  //If neighboring face has uv, and the they match
                  //we recurse into this next face
                  if ((uva==nxt_uvb)&&(uvb==nxt_uva))
                  {
                     //Stick face on start of queue
                     faces.push(nxt_f);
                  }
                  //But if not, let's see if the other face is
                  //part of this mapping. If it is, then we found
                  //a seam.  Find the direction (u or v) and if
                  //its consistent with the _min_u/_max_u (or v)
                  //Then set the wrap flag for u or v appropriately
                  //or just turn all wrapping off if something's amiss
                  else 
                  {
                     //Here's a seam!
                     if (nxt_uvdata->mapping() == this)
                     {               
                        //We support 2 kinds of wrapping:
                        //-Wrap on a line of constant u (wrap at _min_u,_max_u)
                        //-Wrap on a line of constant v (wrap at _min_v,_max_v)
                        //If neither is seen, or if the discontinuity isn't
                        //at the extrema, we found something anomolous, abort!
                        //Note - There can be holes at the seam without problems.

                        if ((uva[0]==uvb[0])&&(nxt_uva[0]==nxt_uvb[0]))
                        {
                           //This looks like wrapping on a line of const. u
                           //Let's make sure the discontinuity is at the extrema

                           if ( (uva[0]==_min_u && nxt_uva[0]==_max_u) ||
                                (uva[0]==_max_u && nxt_uva[0]==_min_u))
                           {
                              //It's all good
                              if (!_wrap_u)
                                 {
                                    err_mesg(ERR_LEV_SPAM, "UVMapping::recurse_wrapping() - Found a valid wrapping seam in u.");
                                    _wrap_u = true;
                                 }
                           }
                           else
                           {
                              //We aren't at the extrema, so set the bad flag
                              //to avoid further checking

                              _wrap_bad = true;
                              _wrap_u = false;
                              _wrap_v = false;

                              err_mesg(ERR_LEV_WARN,
                                 "UVMapping::recurse_wrapping() - Found an INVALID wrapping seam in u: (%f,%f) since u extrema are: (%f,%f)",
                                   uva[0], nxt_uva[0], _min_u, _max_u);
                              err_mesg(ERR_LEV_WARN, "UVMapping::recurse_wrapping() - Aborting all wrapping.");
                           }
                        }
                        else if ((uva[1]==uvb[1])&&(nxt_uva[1]==nxt_uvb[1]))
                        {
                           //This looks like wrapping on a line of const. v
                           //Let's make sure the discontinuity is at the extrema

                           if ( (uva[1]==_min_v && nxt_uva[1]==_max_v) ||
                                (uva[1]==_max_v && nxt_uva[1]==_min_v))
                           {
                              //It's all good
                              if (!_wrap_v)
                              {
                                 err_mesg(ERR_LEV_INFO, "UVMapping::recurse_wrapping() - Found a valid wrapping seam in v.");
                                 _wrap_v = true;
                              }
                           }
                           else
                           {
                              //We aren't at the extrema, so set the bad flag
                              //to avoid further checking

                              _wrap_bad = true;
                              _wrap_u = false;
                              _wrap_v = false;
                                        
                              err_mesg(ERR_LEV_WARN,
                                 "UVMapping::recurse_wrapping() - Found an INVALID wrapping seam in v: (%f,%f) since v extrema are: (%f,%f)",
                                   uva[1], nxt_uva[1], _min_v, _max_v);
                              err_mesg(ERR_LEV_WARN, "UVMapping::recurse_wrapping() - Aborting all wrapping.");
                           }
                        }
                        else
                        {
                           //One or both edges failed to show constant u or v
                           //Abort any further wrapping...

                           _wrap_bad = true;
                           _wrap_u = false;
                           _wrap_v = false;

                           err_mesg(ERR_LEV_WARN,
                              "UVMapping::recurse_wrapping() - Found an INVALID wrapping. The seam wasn't constant in u or v.");
                           err_mesg(ERR_LEV_WARN,
                              "UVMapping::recurse_wrapping() - Edge #1 (%f,%f)-(%f,%f) Edge #2 (%f,%f)-(%f,%f)",
                                 uva[0], uva[1], uvb[0], uvb[1], nxt_uvb[0], nxt_uvb[1], nxt_uva[0], nxt_uva[1]);
                           err_mesg(ERR_LEV_WARN,
                              "UVMapping::recurse_wrapping() - Aborting all wrapping.");
                        }
                     }
                  }
               }
            }
         }
      }
   }
}
예제 #28
0
/////////////////////////////////////
// add()
/////////////////////////////////////
bool
HatchingGroupFixed::add(
   CNDCpt_list &pl,
   const vector<double>&prl,
   int curve_type
   )
{
   size_t 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.size() != prl.size()) {
      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;
   vector<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;
   vector<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.size(); k++) {
         f = HatchingGroupBase::find_face_vis(NDCpt(ndcpts[k]),wloc);
         if ((f) && (f->patch() == _patch) && (f->front_facing())) {
            wlProjList.push_back(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.size()<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.size()*
                             (double)wlProjList.length())/(double)wlClipList.length());
      size_t num = max(guess,5);
      double step = 1.0/((double)(num-1));
      for (k=0 ; k<num ; k++) wlScaledList.push_back(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
      size_t num = max(wlClipList.size(), 5UL);
      double step = 1.0/((double)(num-1));
      for (k=0 ; k<num ; k++) wlScaledList.push_back(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.size();k++) ndczlScaledList.push_back(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;
   }

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

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

   for (k=0; k<ndczlScaledList.size(); 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.push_back(HatchingFixedVertex(f->index(), bc));
            f->bc2norm_blend(bc,norm);
            pts.push_back(wloc);
            norms.push_back(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.size()>1) {
      //XXX  - Okay, using the gesture pressure, but no offsets.
      //Need to go back and add offset generation...

      BaseStrokeOffsetLISTptr ol = make_shared<BaseStrokeOffsetLIST>();

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

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

      ol->push_back(BaseStrokeOffset(1.0, 0.0, finalprl[finalprl.size()-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;

}
예제 #29
0
파일: bface.cpp 프로젝트: QuLogic/jot-lib
Bsimplex*
Bface::ndc_walk(
   CNDCpt& target, 
   CWvec &passed_bc, 
   CNDCpt &nearest,
   int is_on_tri, 
   bool use_passed_in_params) const
{
  // just like local_search, but in NDC space
   //
   // start from this face, move in NDC space
   // across the mesh to reach the target
   //
   // if reached, return the simplex that contains
   // the target point.
   // we only move if it will get us closer to the goal.  Hence, we
   // can never wander off forever.

   // if can't reach it, return 0

   NDCpt y (nearest);
   Wvec  bc(passed_bc);

   if (!use_passed_in_params) {
      y = nearest_pt_ndc(target, bc, is_on_tri);
   }
   Bsimplex* sim   = bc2sim(bc);

   if (is_on_tri) {
      // target is on this triangle
      // return the lowest-dimensional
      // simplex on which it lies
      return sim;
   }

   if (is_edge(sim)) {
      Bedge* e = (Bedge*)sim;
      Bface* f = e->is_sil() ? nullptr : e->other_face(this);
      if (f) {
         Wvec new_bc;
         int  new_on_tri;
         NDCpt new_best = f->nearest_pt_ndc(target, new_bc, new_on_tri);
         if (new_best.dist_sqrd(target) < y.dist_sqrd(target))
            return f->ndc_walk(target, new_bc, new_best, new_on_tri, true); 
         else 
            return nullptr;
      } else {
         return nullptr;
      }
   }

   // better be a vertex
   assert(is_vert(sim));
   Bvert* v = (Bvert*)sim;
   if (v->degree(SilEdgeFilter()) > 0)
      return nullptr;

   Bface_list nbrs(16);
   ((Bvert*)sim)->get_faces(nbrs);
   double dist_sqrd = 1e50;
   Bface* best = nullptr;
   Wvec   best_bc;
   NDCpt  best_nearest;
   int    best_on_tri = 0;
   Wvec   curr_bc;
   NDCpt  curr_nearest;
   int    curr_on_tri=0;

   for (Bface_list::size_type k = 0; k < nbrs.size(); k++) {
      if (nbrs[k] != this) {
         curr_nearest = nbrs[k]->nearest_pt_ndc(target, curr_bc, curr_on_tri);
         if (curr_nearest.dist_sqrd(target) < dist_sqrd ) {
            dist_sqrd = curr_nearest.dist_sqrd(target);
            best_bc = curr_bc;
            best_on_tri = curr_on_tri;
            best_nearest = curr_nearest;
            best = nbrs[k];
         }
      }
   }

   if (dist_sqrd < y.dist_sqrd(target)) {
      return best->ndc_walk(target, best_bc, best_nearest, best_on_tri, true); 
   }

   return nullptr;
}