inline void clear_face_flags(CBvert_list& verts) { // Helper method used below in get_parents(); // clear flags of faces adjacent to the given vertices, // including faces that are not stricly adjacent, but // that are part of a quad that contains the vertex. Bface_list star; for (int i=0; i<verts.num(); i++) { verts[i]->get_q_faces(star); star.clear_flags(); } }
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; }
bool OVERSKETCH::apply_offsets(CBvert_list& sil_verts, const vector<double>& sil_offsets) { // XXX - preliminary... assert(sil_verts.size() == sil_offsets.size()); // Expand region around oversketched silhouette verts. // XXX - Should compute the one-ring size, not use "3" Bface_list region = sil_verts.one_ring_faces().n_ring_faces(3); // Find the minimum distance to the silhouette verts from the // outer boundary of the region Wpt_list sil_path = sil_verts.pts(); double R = min_dist(region.get_boundary().verts().pts(), sil_path); Bvert_list region_verts = region.get_verts(); Wpt_list new_locs = region_verts.pts(); vector<double> offsets; for (Bvert_list::size_type i=0; i<region_verts.size(); i++) { Wpt foo; int k = -1; double d = sil_path.closest(region_verts[i]->loc(), foo, k); if (k < 0 || k >= (int)sil_offsets.size()) { err_adv(debug, "OVERSKETCH::apply_offsets: error: can't find closest"); continue; } double s = swell_profile(d/R); double h = sil_offsets[k] * s; // err_adv(debug, " d: %f, d/R: %f, s: %f", d, d/R, s); offsets.push_back(h); new_locs[i] += region_verts[i]->norm()*h; // WORLD::show(region_verts[i]->loc(), new_locs[i], 1); } // now apply new locs // FIT_VERTS_CMDptr cmd = make_shared<FIT_VERTS_CMD>(region_verts, new_locs); SUBDIV_OFFSET_CMDptr cmd = make_shared<SUBDIV_OFFSET_CMD>(region_verts, offsets); cmd->doit(); WORLD::add_command(cmd); return true; }
inline void try_append(Bface_list& A, Bface* f) { // Helper method used below in try_append(); if (f && !f->flag()) { f->set_flag(); A.push_back(f); } }
int INFLATE::tap_cb(CGESTUREptr& gest, DrawState*& state) { err_adv(debug, "INFLATE::tap_cb"); // Tracks if the tap was near a guideline bool near_guidelines = false; PIXEL pdummy; int idummy; if ( PIXEL_list(_lines).closest( PIXEL(gest->start()), pdummy, idummy ) < 5 ) near_guidelines = true; // Check if gesture hits a BFace Bface* face = 0; Bsurface::hit_ctrl_surface(gest->start(), 1, &face); // Fail if Gesture missed guidelines and geometry if ( !face && !near_guidelines ) return cancel_cb(gest,state); // Check that we are trying to inflate if ( _orig_face ) { // Find the reachable faces from the starting point Bface_list set = _mode ? _faces : Bface_list::reachable_faces(_orig_face); // verify that the user tapped a face that is part of the inflation region if ( face && !set.contains( face ) ) { return cancel_cb(gest,state); } // Attempt to inflate the surface INFLATE_CMDptr cmd = _mode ? (new INFLATE_CMD( _faces, _preview_dist )) : (new INFLATE_CMD( _orig_face, _preview_dist )); WORLD::add_command(cmd); } // On fail, cancel return cancel_cb(gest,state); }
void TriStrip::get_strips( Bface* start, vector<TriStrip*>& strips ) { // if starting face was visited already, stop if (!is_cleared(start)) return; // stack is used to record faces adjacent to // the current strip, in order to build additional // strips that align with the current one static Bface_list stack(1024); stack.clear(); stack.push_back(start); BMESHptr mesh = start->mesh(); while (!stack.empty()) { start = stack.back(); stack.pop_back(); if (is_cleared(start)) { TriStrip* strip = mesh->new_tri_strip(); strip->build(start, stack); strips.push_back(strip); } } }
Bface_list Bface_list::reachable_faces(Bface* f, CSimplexFilter& pass) { // Returns the list of faces reachable from f, crossing // only edges accepted by the filter. It sets needed // flags (on the entire mesh) then calls grow_connected(). Bface_list ret; if (!(f && f->mesh())) return ret; // Ensure all reachable face flags are set to 1: // XXX - // touches every face in the mesh; a better // implementation would just touch reachable ones // (or is that impossible to implement?) f->mesh()->faces().set_flags(1); ret.grow_connected(f, pass); return ret; }
Bvert_list LMESH::get_subdiv_inputs(CBvert_list& verts) { static bool debug = Config::get_var_bool("DEBUG_LMESH_SUBDIV_INPUTS",false); // Given a set of vertices from the same LMESH, return // the vertices of the parent LMESH that affect the // subdivision locations of the given vertices. // Require verts share common LMESH // XXX - could relax this, provided we test each Bvert // to ensure it is really an Lvert. if (!dynamic_pointer_cast<LMESH>(verts.mesh())) return Bvert_list(); // Get direct parent vertices and edges Bvert_list vp; // vertex parents Bedge_list ep; // edge parents get_parents(verts, vp, ep); err_adv(debug, "%d verts: parents: %d verts, %d edges", verts.size(), vp.size(), ep.size()); // Clear flags of all adjacent faces clear_face_flags(vp); ep.clear_flag02(); // Put all adjacent faces into a list Bface_list faces = get_q_faces(vp); err_adv(debug, "parent faces from verts: %d", faces.size()); try_append(faces, ep.get_primary_faces()); err_adv(debug, "parent faces from edges too: %d", faces.size()); // Pull out the vertices: return faces.get_verts(); }
//! Given the starting face "orig_face" and an offset amount "dist", //! determine the appropriate edit level, re-map orig_face to that //! level, and "inflate" the portion of the mesh reachable from //! orig_face by amount dist. The offset may be made relative to the //! local edge length. bool INFLATE::do_inflate( Bface* orig_face, double dist, Bsurface*& output, Bface_list*& reversed_faces, MULTI_CMDptr cmd) { // Reject garbage if (fabs(dist) < epsAbsMath()) { err_adv(debug, "INFLATE::do_inflate: bad offset distance: %f", dist); return 0; } // Throw out trash if (!get_lmesh(orig_face)) { err_adv(debug, "INFLATE::do_inflate: error: bad face"); return 0; } Lface* face = (Lface*)orig_face; err_adv(debug, "INFLATE::do_inflate: face at level %d", subdiv_level(face)); // Decide which level to do inflation -- should match offset dist int rel_level = 0; // XXX - it may not be a good idea //if (!choose_level(face, dist, rel_level)) // defined above // return 0; // Remap face to chosen level face = remap(face, rel_level); if (!face) { err_adv(debug, "INFLATE::do_inflate: can't remap %d from level %d", rel_level, subdiv_level(face)); return 0; } assert(face && face->mesh()); err_adv(debug, "chosen edit level: %d", subdiv_level(face)); // Get set of reachable faces: Bface_list set = Bface_list::reachable_faces(face); assert(set.mesh() != NULL); err_adv(debug, "reachable faces: %d, subdiv level: %d, total faces: %d", set.num(), set.mesh()->subdiv_level(), set.mesh()->nfaces()); if (!set.is_consistently_oriented()) { err_msg("INFLATE::do_inflate: rejecting inconsistently \ oriented surface..."); return 0; }
//! Given boundary curve (near-planar), activate the //! widget to inflate ... bool INFLATE::setup(Bface *bf, double dist, double dur) { reset(); // Given the starting face f and an offset amount h, // determine the appropriate edit level, re-map f to that // level, and "inflate" the portion of the mesh reachable // from f by h, relative to the local edge length. // reject garbage if (!(bf && LMESH::isa(bf->mesh()))) { err_adv(debug, "INFLATE::setup: error: bad face"); return 0; } Lface* f = (Lface*)bf; err_adv(debug, "setup: face at level %d", bf->mesh()->subdiv_level()); // get avg edge length of edges in face: double avg_len = avg_strong_edge_len(f); if (avg_len < epsAbsMath()) { err_adv(debug, "INFLATE::setup: bad average edge length: %f", avg_len); return 0; } // Get set of reachable faces: Bface_list set = Bface_list::reachable_faces(f); assert(set.mesh() != NULL); err_adv(debug, "reachable faces: %d, subdiv level: %d, total faces: %d", set.num(), set.mesh()->subdiv_level(), set.mesh()->nfaces()); // given face set should be an entire connected piece. if (!is_maximal_connected(set )) { err_adv(debug, "INFLATE::setup: rejecting subset of surface..."); return 0; } Bface_list p = set;//get_top_level(set); assert(LMESH::isa(p.mesh())); err_adv(debug, "top level: %d faces (out of %d)", p.num(), p.mesh()->nfaces()); BMESH* m = p.mesh(); if (!m) { err_adv(debug, "INFLATE::setup: Error: null mesh"); return 0; } if (!LMESH::isa(m)) { err_adv(debug, "INFLATE::setup: Error: non-LMESH"); return 0; } if (!p.is_consistently_oriented()) { err_adv(debug, "INFLATE::setup: Error: inconsistently oriented faces"); return 0; } // We ensured the mesh in an LMESH so this is okay: _boundary=p.get_boundary(); if (_boundary.edges().empty()) { err_adv(debug, "INFLATE::setup: Error: No boundary. Quitting."); _boundary.reset(); return 0; } // ******** From here on, we accept it ******** err_adv(debug, "Inflating... %d boundary loops", _boundary.num_line_strips()); _faces = p; _mode = false; _d = _boundary.cur_edges().avg_len(); _mesh = (LMESH*)m; _orig_face = bf; _preview_dist = dist; // Set the timeout duration set_timeout(dur); // Become the active widget and get in the world's DRAWN list: activate(); return true; }
void remove_nodes(Bface_list& flist, ARRAY<Wvec>& blist, double min_dist, ARRAY<OctreeNode*>& t) { // if (flist.num() != blist.num()) { // return; // } assert(flist.num() == blist.num()); Wpt_list pts = get_pts(flist, blist); ARRAY< ARRAY<int> > N; ARRAY<bool> to_remove; for (int i = 0; i < pts.num(); i++) { N += ARRAY<int>(); to_remove += false; } for (int i = 0; i < pts.num(); i++) { for (int j = 0; j < t[i]->neibors().num(); j++) { int index = t[i]->neibors()[j]->get_term_index(); if (index < pts.num()) { if (pts[i].dist(pts[index]) < min_dist) { N[i] += index; N[index] += i; } } else { //cerr << "Sps Warning, index > pts.num()" << endl; } } } priority_queue< Priority, vector<Priority> > queue; ARRAY<int> versions; for (int i = 0; i < pts.num(); i++) { if (!to_remove[i] && !N[i].empty()) { Priority p; p._priority = center(pts, N[i]).dist(pts[i]); p._index = i; p._version = 0; queue.push(p); } versions += 0; } while (!queue.empty()) { Priority p = queue.top(); queue.pop(); int r = p._index; if (p._version == versions[r]) { to_remove[r] = true; for (int i = 0; i < N[r].num(); i++) { N[N[r][i]] -= r; versions[N[r][i]]++; if (!N[N[r][i]].empty()) { Priority q; q._priority = center(pts, N[N[r][i]]).dist(pts[N[r][i]]); q._index = N[r][i]; q._version = versions[N[r][i]]; queue.push(q); } } } } versions.clear(); Bface_list ftemp(flist); ARRAY<Wvec> btemp(blist); flist.clear(); blist.clear(); for (int i = 0; i < ftemp.num(); i++) if (!to_remove[i]) { flist += ftemp[i]; blist += btemp[i]; } }
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; }
void ProxySurface::trim_proxy_surface() { assert(_proxy_mesh); int n = 0; //number of faces outside the bounding box //get all the quads Bface_list faces = _proxy_mesh->faces(); //clear out all the markings for(int i=0; i < faces.num(); ++i) { if(faces[i]) ProxyData::set_mark(faces[i],this, false); //else // cerr << "FACE is NULL" << endl; } //mark all the faces that do not overap bounding box for(int i=0; i < faces.num(); ++i) { if(faces[i]){ bool t1 = (is_inside_bounding_box(faces[i]->e1())) ? true : false; bool t2 = (is_inside_bounding_box(faces[i]->e2())) ? true : false; bool t3 = (is_inside_bounding_box(faces[i]->e3())) ? true : false; // If all the edges are outside, then mark the face if(!t1 && !t2 && !t3){ //cerr << "we can delete this face" << endl; ProxyData::set_mark(faces[i], this, true); n++; } }else { //cerr << "FACE is NULL" << endl; } } if(n < 1) return; //for all verts check to see if all the faces that it is attached to has a marked Bvert_list verts = _proxy_mesh->verts(); for(int i=0; i < verts.num(); ++i) { ARRAY<Bface*> ret; verts[i]->get_quad_faces(ret); // Make sure that all adjasent faces need to be deleted bool do_it=true; for(int k=0; k < ret.num(); ++k) { if(ret[k]){ assert(ret[k]->is_quad()); if(!ProxyData::get_mark(ret[k], this) || !ProxyData::get_mark(ret[k]->quad_partner(), this)) { // cerr << "vert degree " << verts[i]->p_degree() << endl; do_it = false; break; } } } if(do_it){ UVpt remove_uv; UVdata::get_uv(verts[i], remove_uv); remove_vert_grid(remove_uv); _proxy_mesh->remove_vertex(verts[i]); _proxy_mesh->changed(); } } //clean up faces Bface_list faces2 = _proxy_mesh->faces(); for(int i=0; i < faces2.num(); ++i) { if(!(faces2[i]->is_quad()) || !(faces2[i]->quad_partner())){ _proxy_mesh->remove_face(faces2[i]); _proxy_mesh->changed(); } } //debug_grid(); }
Skin* Skin::create_multi_sleeve( Bface_list interior, // interior skeleton surface VertMapper skel_map, // tells how some skel verts are identified MULTI_CMDptr cmd) { err_adv(debug, "Skin::create_multi_sleeve"); //assert(!has_secondary_any_level(interior)); assert(cmd != 0); if (!skel_map.is_valid()) { err_msg("Skin::create_multi_sleeve: invalid skel map"); return 0; } Bface_list exterior = interior.exterior_faces(); Bface_list all_skel_faces = interior + exterior; // need an LMESH that contains all faces LMESH* mesh = LMESH::upcast(all_skel_faces.mesh()); if (!mesh) { err_adv(debug, " bad skel mesh"); return 0; } // Get relative refinement level int R = max_subdiv_edit_level(all_skel_faces, interior); err_adv(debug, " skel region res level: %d", R); LMESH* ctrl = mesh->control_mesh(); int old_lev = ctrl->cur_level(); int ref_lev = mesh->subdiv_level() + R; ctrl->update_subdivision(ref_lev); if (ref_lev < old_lev) ctrl->update_subdivision(old_lev); Skin* cur = new Skin(mesh, exterior, skel_map, R, false, "sleeve", cmd); cur->set_all_sticky(); cur->freeze(cur->skin_faces().get_boundary().edges().get_verts()); for (int k=1; k<=R; k++) { err_adv(debug, " top of loop: k = %d", k); // XXX - temporary if (!check(interior.mesh()->faces())) return cur; // Update the skeleton to the next level LMESH::update_subdivision(interior + exterior, 1); // Map skel faces to next level. Avoid new holes. // Take account of new regions that don't exist at // previous level. err_adv(debug, " getting interior subdiv faces"); int n = interior.num(); interior = get_subdiv_faces(interior, 1); // no holes can form here if (4*n != interior.num()) { err_adv(debug, " *** ERROR *** got %d interior subdiv faces from %d", interior.num(), n); } err_adv(debug, " getting skel subdiv faces"); n = cur->skel_faces().num(); exterior = get_subdiv_faces(cur->skel_faces(), 1); if (4*n != exterior.num()) { err_adv(debug, " *** ERROR *** got %d exterior subdiv faces from %d", exterior.num(), n); } // check for holes: if (exterior.has_any_secondary()) { err_adv(debug, " removing secondary skel faces at level %d", k); exterior = exterior.primary_faces(); // remove holes } // check for new regions: err_adv(debug, " checking for new faces"); Bface_list new_faces = interior.two_ring_faces().minus(interior + exterior); assert(new_faces.is_all_primary()); // must be true (we think) if (!new_faces.empty()) { err_adv(debug, " adding %d new faces at level %d", new_faces.num(), k); exterior.append(new_faces); } // get version of skel_map at new level: cur = new Skin(cur, exterior, cmd); // make the ones on or near the boundary "sticky" cur->set_all_sticky(false); cur->set_sticky( cur->skin_faces(). get_boundary(). edges(). get_verts(). one_ring_faces(). get_verts() ); cur->freeze(cur->skin_faces().get_boundary().edges().get_verts()); // put memes in place, not just use position they inherited // from subdivision cur->do_update(); } // Join skin to skel at level R err_adv(debug, " joining to skel..."); if (cur->join_to_skel(interior, cmd)) { err_adv(debug, " ...succeeded"); } else { err_adv(debug, " ...failed"); } _debug_instance = cur; return upcast(cur->control()); }
static int max_subdiv_edit_level(Bface_list all, Bface_list core=Bface_list()) { // what is the deepest subdiv level that is either: // - controlled by a Bbase // - has holes cut // - has new surface regions attached err_adv(debug, "max_subdiv_edit_level:"); if (all.empty()) { assert(core.empty()); err_adv(debug, " face list is empty"); return 0; } assert(all.mesh() && all.contains_all(core)); int R = 0; // level at which deepest edits happened int k = 0; // current level being examined while (!all.empty()) { k++; all = get_subdiv_faces(all,1); if (all.empty()) break; // check for controllers if (!Bbase::find_owners(all).empty()) { err_adv(debug, " controllers at level %d", k); R = k; } // check for holes at this level: if (all.has_any_secondary()) { all = all.primary_faces(); err_adv(debug, " hole at level %d", k); R = k; // holes exist at this level } // check for new regions if (core.empty()) continue; core = get_subdiv_faces(core,1); Bface_list new_faces = core.two_ring_faces().primary_faces(); // see if any of these new ones are actually new: new_faces.set_flags(1); all.clear_flags(); new_faces = new_faces.filter(SimplexFlagFilter(1)); if (!new_faces.empty()) { all.append(new_faces); err_adv(debug, " new faces at level %d", k); R = k; // new stuff exists at this level } } return R; }
bool UVdata::handle_subdiv_calc() { // Some of you may have been wondering why anyone would // need UVdata on an edge. The whole reason is to pick up // this here callback, which is generated when it is time // to recompute subdivision values for an edge or vertex. // (I.e., this is also why you need UVdata on a vertex). // // XXX - Currently we aren't keeping track of when // subdivision uv-coords need to be recomputed. We // just get this callback whenever subdivision // *locations* need to be recomputed. So the wrong // thing happens if you change the uv-coords of the // control mesh but not the vertex locations or // connectivity. And if you change the vertex // locations but not the uv-coords the latter get // recomputed even though they don't need to be. // XXX - even worser: working on a deadline, we're changing // mesh connectivity at arbitrary subdivision levels // (not previously done), and that's leading to an assertion // failure in subdiv_uv(Bedge*, Bface*) below... // needs to get fixed (later) // lem - 1/6/2002 // return 0; // Don't compute it more than once. Current policy is uv // coords are assigned but not later edited or changed ever. // Why? Because with mesh connectivity edits happening at // arbitrary levels of subdivision, we don't want parent // elements forcing inappropriate uv values on elements at // current level. -- lem 12/29/2004; same deadline :( if (_did_subdiv) { // return false; } _did_subdiv = true; if (is_edge(simplex())) { Ledge* e = (Ledge*) simplex(); if ( !e->get_face() ) return false; if (is_continuous(e)) { // Set a single uv-coord on the subdivision vertex // to be used regardless of the face containing it. _set(e->subdiv_vertex(), subdiv_uv(e, e->get_face())); } else { // Compute 2 separate subdiv uv-coords, set each one // on the 3 sub-faces on each side of the edge. // (If a face is missing or has no uv-coords it's a no-op). set_subdiv_uv(e, (Lface*)e->f1()); set_subdiv_uv(e, (Lface*)e->f2()); } } else if (is_vert(simplex())) { Lvert* v = (Lvert*) simplex(); if ( !v->get_face() ) return false; if (is_continuous(v)) { // Set a single uv-coord on the subdivision vertex // to be used regardless of the face containing it. _set(v->subdiv_vertex(), subdiv_uv(v, v->get_face())); } else { // Compute and set a separate uv-coord for each face // surrounding the subdiv vert: Bface_list faces; v->get_faces(faces); for (int i=0; i<faces.num(); i++) set_subdiv_uv(v, (Lface*)faces[i]); } } else { // We get here if this UVdata is on a Bface or a null simplex. // Neither should ever happen. assert(0); } // Returning 0 means we are not overriding the normal // subdivision calculation. return 0; }