Primitive* PAPER_DOLL::build_primitive(CBface_list& o_faces) { LMESHptr skel_mesh = dynamic_pointer_cast<LMESH>(o_faces.mesh()); assert(skel_mesh); LMESHptr mesh = get_inflate_mesh(skel_mesh); assert(mesh); // create vertices for top and bottom parts: Bvert_list o_verts = o_faces.get_verts(); // original verts Bvert_list t_verts = copy_verts(o_verts, mesh); // top verts Bvert_list b_verts = copy_verts(o_verts, mesh); // bottom verts // set up mappings: // original --> top // original --> bottom VertMapper t_map(o_verts, t_verts, true); VertMapper b_map(o_verts, b_verts, true); Primitive* ret = new Primitive(mesh, skel_mesh); // build dependencies Bsurface_list surfs = Bsurface::get_surfaces(o_faces); for (int i = 0; i < surfs.num(); i++) ret->absorb_skel(surfs[i]); // generate top faces and bottom faces Bface_list t_faces = copy_faces(o_faces, t_map, ret, false); Bface_list b_faces = copy_faces(o_faces, b_map, ret, true); // create the sides: create_sides(o_faces, t_map, b_map, ret); Wvec n = o_faces.avg_normal(); if (1) { define_offsets(o_verts, t_map, b_map, n, ret); } else { // under construction... // for now just offset the top and bottom uniformly for testing... const double k = Config::get_var_dbl("PAPER_DOLL_OFFSET_SCALE",1.0); double h = o_faces.get_edges().strong_edges().avg_len(); t_verts.transform(Wtransf::translation( 0.5*k*h*n)); b_verts.transform(Wtransf::translation(-0.5*k*h*n)); } // make it all undoable: MULTI_CMDptr cmd = make_shared<MULTI_CMD>(); // finish build ret->finish_build(cmd); //cmd->add(hide_surfs_cmd(Bsurface::get_surfaces(o_faces))); cmd->add(make_shared<SHOW_BBASE_CMD>(ret)); WORLD::add_command(cmd); return _prim = ret; }
inline bool join(CBvert_list& o, CBvert_list& c, MULTI_CMDptr& cmd, Cstr_ptr& msg) { // Used in Skin::join_to_skel() to join seams of a mesh together. JOIN_SEAM_CMDptr join = new JOIN_SEAM_CMD(o, c); if (join->doit()) { err_adv(debug, " joined %s (%d verts to %d verts)", **msg, o.num(), c.num()); cmd->add(join); return true; } else { err_adv(debug, " error: can't join %s", **msg); return false; } }
bool SWEEP_DISK::build_box(CWpt& o, CWvec& t, CWpt_list& spts, MULTI_CMDptr cmd) { // Editing or Creating bool is_editing = !(_surfs.empty()); // get list of Bpoints around the boundary of the base surface: ARRAY<Bpoint*> bot_pts = is_editing ? _points.extract(_points.num()/2, _points.num()/2) : Bpoint::get_points(_boundary.verts()); assert ( bot_pts.num() > 2 ); int n = bot_pts.num(); Bpoint_list top_pts(n); // If surface normals of base surface point along the sweep // direction, they have to be reversed. otherwise, the // boundary runs CW, so we reverse the order of the bottom // points to get them to run CCW: // // XXX - needs fix to work on embedded region, similar to // build_tube() above. if ( _plane.normal()*t < 0 ) { if (!is_editing) bot_pts.reverse(); } else { reverse_faces( _enclosed_faces ); if(cmd) cmd->add(make_shared<REVERSE_FACES_CMD>(_enclosed_faces, true)); } double avg_len = _boundary.edges().avg_len(); if ( isZero(avg_len) ) { cerr << "SWEEP_DISK::build_box(): ERROR, boundary avg len is zero" << endl; return false; } int num_edges = max( (int)round( spts.length()/avg_len ), 1 ); int res_level = Config::get_var_int("BOX_RES_LEVEL", 2,true); // we'll keep lists of all curves and surfaces for the box, // for setting their res level uniformly: Bcurve_list curves = Bcurve::get_curves(_boundary.edges()); Bsurface_list surfaces = Bsurface::get_surfaces(_enclosed_faces); if (!surfaces.empty()) res_level = surfaces.min_res_level(); else if (!curves.empty()) res_level = curves.min_res_level(); curves.clear(); surfaces.clear(); // XXX - Zachary: add SWEEP_CMD (BOX_CMD?) here: // Create/Edit the top points matching the bottom points // and the curves running vertically between them int i = 0; for ( i=0; i<n; i++ ) { Wvec n = (bot_pts[i]->loc() - o).orthogonalized(t).normalized(); Wvec b = cross(n,t); Wpt_list cpts = spts; cpts.xform( Wtransf(o, t, b, n) ); // XXX - should be undoable if (is_editing) { Wpt_listMap* m = dynamic_cast<Wpt_listMap*>(_curves[i]->map()); cmd->add(make_shared<WPT_LIST_RESHAPE_CMD>(m,cpts)); } else { top_pts += BpointAction::create(_mesh, cpts.back(), b, n, res_level, cmd); curves += BcurveAction::create(_mesh, cpts, b, num_edges , res_level, bot_pts[i], top_pts[i], cmd); } } if (!is_editing) { // Create curves joining each top point to the next. for ( i=0; i<n; i++ ) { int j = (i+1) % n; Bcurve* c = bot_pts[i]->lookup_curve( bot_pts[j] ); if ( !c ) { cerr << "SWEEP_DISK::build_box(): ERROR, can't find boundary curve" << endl; continue; } // Ensure orientation of top and bottom curves is the same. int i1 = i; int i2 = j; if ( c->b1() != bot_pts[i1] ) swap( i1, i2 ); assert( c->b1() == bot_pts[i1] && c->b2() == bot_pts[i2] ); // Create the new top curve with the same shape as the bottom curve. Wpt_list cpts = c->get_wpts(); cpts.fix_endpoints( top_pts[i1]->loc(), top_pts[i2]->loc() ); curves += BcurveAction::create(_mesh, cpts, t, c->num_edges(), res_level, top_pts[i1], top_pts[i2], cmd); build_coons(bot_pts[i], bot_pts[j], top_pts[j], top_pts[i], surfaces, cmd); } // Slap on a top if base is quadrilateral // XXX - should handle other cases if (n == 4) build_coons(top_pts[0], top_pts[1], top_pts[2], top_pts[3], surfaces, cmd); } _mesh->changed(); // set the res level uniformly over the box: for (int i = 0; i < bot_pts.num(); i++) bot_pts[i]->set_res_level(res_level); top_pts.set_res_level(res_level); curves.set_res_level(res_level); surfaces.set_res_level(res_level); _mesh->update_subdivision(res_level); // Record data necessary to return to this mode Panel* p = dynamic_cast<Panel*>(Bsurface::get_surface(_enclosed_faces)); assert(p); if (is_editing) { vector<Panel*>::iterator it = std::find(panels.begin(), panels.end(), p); assert(it != panels.end()); int loc = it - panels.begin(); profiles[loc] = _profile; } else { panels.push_back(p); top_pts += bot_pts; bpoints.push_back(top_pts); bcurves.push_back(curves); bsurfaces.push_back(surfaces); profiles.push_back(_profile); } //FLOOR::realign(_mesh->cur_mesh(), cmd); WORLD::add_command(cmd); return true; }
bool Skin::create_inflate( CBface_list& skel, double h, MULTI_CMDptr cmd, bool mode ) { // do a hack to make things more functional "for now" // find the level at which Bbases are operating, // and do the inflate operation there. but sew the // ribbons at this level (skel level). this way, for a small // offset, leading to hi-res skeleton region, we can get // away with a coarse skin. might suck though if the two // skins do smoothing independently and the ribbons don't // match up... double inner_offset = min(h, 0.0); double outer_offset = max(h, 0.0); Skin* inside = create_inflate(skel, inner_offset, 0, true, mode, cmd); Skin* outside = create_inflate(skel, outer_offset, 0, false, mode, cmd); if (!(inside && outside)) { err_adv(debug, "Skin::create_inflate: could not create inside and outside"); return false; } if (!mode) outside->set_partner(inside); if (!mode) push_all_levels(skel, cmd, false); else { push(skel, cmd, false); push(inside->skin_faces(), cmd, false); } // create inf mapper if (!mode) { VertMapper in = inside->_mapper; VertMapper out = outside->_mapper; Bvert_list in_list = in.a_to_b(out.A()); inside->set_inf_mapper(VertMapper(in_list, out.B(), true)); outside->set_inf_mapper(VertMapper(out.B(), in_list, true)); } // create ribbons // get matching boundaries of the two skin regions. // the "outer" one runs CCW as usual, but the "inner" one runs CW. EdgeStrip skel_strip = skel.get_boundary(); if (skel_strip.empty()) { err_adv(debug, "Skin::create_inflate: no boundary -- no ribbons"); return true; } EdgeStrip outer_strip = outside->_mapper.a_to_b(skel_strip); EdgeStrip inner_strip = inside->_mapper.a_to_b(skel_strip); if (outer_strip.empty() || inner_strip.empty()) { err_adv(debug, "Skin::create_inflate: error finding skin boundaries"); return false; } CREATE_RIBBONS_CMDptr rib = new CREATE_RIBBONS_CMD(outer_strip, inner_strip); if (rib->doit()) { cmd->add(rib); if (!mode) { inside->mesh()->compute_creases(); return true; } VertMapper skel_map(inner_strip.verts(), skel_strip.verts(), false); // false means 1-way map if (Skin::create_multi_sleeve(inside->skin_faces()+inside->skel_faces(), skel_map, cmd)) return true; } err_adv(debug, "Skin::create_inflate: no boundary -- no ribbons"); return false; }