/*===========================================================================*\ * * * OpenFlipper * * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * * www.openflipper.org * * * *--------------------------------------------------------------------------- * * This file is part of OpenFlipper. * * * * OpenFlipper is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of * * the License, or (at your option) any later version with the * * following exceptions: * * * * If other files instantiate templates or use macros * * or inline functions from this file, or you compile this file and * * link it with other files to produce an executable, this file does * * not by itself cause the resulting executable to be covered by the * * GNU Lesser General Public License. This exception does not however * * invalidate any other reasons why the executable file might be * * covered by the GNU Lesser General Public License. * * * * OpenFlipper is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU LesserGeneral Public * * License along with OpenFlipper. If not, * * see . * * * \*===========================================================================*/ /*===========================================================================*\ * * * $Revision$ * * $LastChangedBy$ * * $Date$ * * * \*===========================================================================*/ //============================================================================= // // CLASS BaseRemesherT - IMPLEMENTATION // //============================================================================= #define BASE_REMESHERT_C //== INCLUDES ================================================================= #include "BaseRemesherT.hh" // Remshing #include "DiffGeoT.hh" // OpenMesh #include #include // Selection Stuff #include //== NAMESPACES =============================================================== namespace Remeshing { //== IMPLEMENTATION ========================================================== template BaseRemesherT:: BaseRemesherT(Mesh& _mesh, ProgressEmitter* _progress) : mesh_(_mesh), refmesh_(0), bsp_(0), progress_(_progress) { } //----------------------------------------------------------------------------- template BaseRemesherT:: ~BaseRemesherT() { delete bsp_; delete refmesh_; } //----------------------------------------------------------------------------- template void BaseRemesherT:: init_reference() { typename Mesh::VIter v_it, v_end; typename Mesh::FIter f_it, f_end; typename Mesh::CFVIter fv_it; typename Mesh::VHandle v0, v1, v2; // clear old stuff first if (bsp_) delete_reference(); // tag non-locked vertices + one ring for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) mesh_.status(v_it).set_tagged(!mesh_.status(v_it).locked()); MeshSelection::growVertexSelection(&mesh_); // create reference mesh refmesh_ = new Mesh(); OpenMesh::VPropHandleT vhandles; mesh_.add_property(vhandles); for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) if (mesh_.status(v_it).tagged()) mesh_.property(vhandles, v_it) = refmesh_->add_vertex(mesh_.point(v_it)); for (f_it=mesh_.faces_begin(), f_end=mesh_.faces_end(); f_it!=f_end; ++f_it) { fv_it = mesh_.cfv_iter(f_it); v0 = fv_it.handle(); v1 = (++fv_it).handle(); v2 = (++fv_it).handle(); if (mesh_.status(v0).tagged() && mesh_.status(v1).tagged() && mesh_.status(v2).tagged()) { refmesh_->add_face( mesh_.property(vhandles, v0), mesh_.property(vhandles, v1), mesh_.property(vhandles, v2) ); } } MeshSelection::clearVertexSelection(&mesh_); mesh_.remove_property(vhandles); // need vertex normals refmesh_->request_face_normals(); refmesh_->request_vertex_normals(); refmesh_->update_normals(/*Mesh::ANGLE_WEIGHTED*/); // build BSP bsp_ = new BSP(*refmesh_); bsp_->reserve(refmesh_->n_faces()); for (f_it=refmesh_->faces_begin(), f_end=refmesh_->faces_end(); f_it!=f_end; ++f_it) bsp_->push_back(f_it.handle()); bsp_->build(10, 100); } //----------------------------------------------------------------------------- template void BaseRemesherT:: delete_reference() { delete bsp_; bsp_ = 0; delete refmesh_; refmesh_ = 0; } //----------------------------------------------------------------------------- template void BaseRemesherT:: project_to_reference(typename Mesh::VertexHandle _vh) const { typename Mesh::Point p = mesh_.point(_vh); typename Mesh::FaceHandle fh = bsp_->nearest(p).handle; if ( ! fh.is_valid() ) return; typename Mesh::CFVIter fv_it = refmesh_->cfv_iter(fh); const typename Mesh::Point& p0 = refmesh_->point(fv_it); typename Mesh::Normal n0 = refmesh_->normal(fv_it); const typename Mesh::Point& p1 = refmesh_->point(++fv_it); typename Mesh::Normal n1 = refmesh_->normal(fv_it); const typename Mesh::Point& p2 = refmesh_->point(++fv_it); typename Mesh::Normal n2 = refmesh_->normal(fv_it); // project ACG::Geometry::distPointTriangle(p, p0, p1, p2, p); mesh_.set_point(_vh, p); // get barycentric coordinates if (!ACG::Geometry::baryCoord(p, p0, p1, p2, p)) p[0] = p[1] = p[2] = 1.0/3.0; // interpolate normal typename Mesh::Normal n; n = (n0 *= p[0]); n += (n1 *= p[1]); n += (n2 *= p[2]); n.normalize(); mesh_.set_normal(_vh, n); } //----------------------------------------------------------------------------- template void BaseRemesherT:: remesh(unsigned int _iters, unsigned int _area_iters, bool _use_projection) { try { prepare(); remeshh(_iters, _area_iters, _use_projection); } catch (std::bad_alloc) { mesh_.clear(); omerr() << "Remeshig: Out of memory\n"; } cleanup(); } //----------------------------------------------------------------------------- template void BaseRemesherT:: prepare() { typename Mesh::EIter e_it, e_end; typename Mesh::VIter v_it, v_end; typename Mesh::FIter f_it, f_end; typename Mesh::CFVIter fv_it; typename Mesh::CVOHIter vh_it; typename Mesh::VHandle v0, v1; typename Mesh::FHandle f0, f1; // need vertex and edge status mesh_.request_vertex_status(); mesh_.request_edge_status(); mesh_.request_face_status(); // if nothing selected -> select all nothing_selected_ = true; for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) if (mesh_.status(v_it).selected()) { nothing_selected_ = false; break; } if (nothing_selected_) for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) mesh_.status(v_it).set_selected(true); // lock un-selected vertices & edges for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) mesh_.status(v_it).set_locked(!mesh_.status(v_it).selected()); for (e_it=mesh_.edges_begin(), e_end=mesh_.edges_end(); e_it!=e_end; ++e_it) { v0 = mesh_.to_vertex_handle(mesh_.halfedge_handle(e_it, 0)); v1 = mesh_.to_vertex_handle(mesh_.halfedge_handle(e_it, 1)); mesh_.status(e_it).set_locked(mesh_.status(v0).locked() || mesh_.status(v1).locked()); } // handle feature corners: // lock corner vertices (>2 feature edges) and endpoints // of feature lines (1 feature edge) for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) { if (mesh_.status(v_it).feature()) { int c=0; for (vh_it=mesh_.cvoh_iter(v_it); vh_it; ++vh_it) if (mesh_.status(mesh_.edge_handle(vh_it.handle())).feature()) ++c; if (c!=2) mesh_.status(v_it).set_locked(true); } } // build reference mesh init_reference(); // if (emit_progress_) Progress().step(5); // add properties mesh_.add_property(valences_); mesh_.add_property(update_); mesh_.add_property(area_); } //----------------------------------------------------------------------------- template void BaseRemesherT:: remeshh(unsigned int _iters, unsigned int _area_iters, bool _use_projection) { double progress_step = 100.0 / (((double)_iters/4.0 + (double)_area_iters)); double total_progress = 0.0; for (unsigned int i = 0; i < _iters; ++i) { split_long_edges(); if(progress_ != NULL) { total_progress += progress_step; progress_->sendProgressSignal(total_progress); } collapse_short_edges(); if(progress_ != NULL) { total_progress += progress_step; progress_->sendProgressSignal(total_progress); } flip_edges(); if(progress_ != NULL) { total_progress += progress_step; progress_->sendProgressSignal(total_progress); } tangential_smoothing(_use_projection); if(progress_ != NULL) { total_progress += progress_step; progress_->sendProgressSignal(total_progress); } } if (_area_iters) { balanace_area(_area_iters, _use_projection); if(progress_ != NULL) { total_progress += progress_step; progress_->sendProgressSignal(total_progress); } } // feature edges block flips -> might lead to caps remove_caps(); } //----------------------------------------------------------------------------- template void BaseRemesherT:: cleanup() { typename Mesh::EIter e_it, e_end; typename Mesh::VIter v_it, v_end; typename Mesh::FIter f_it, f_end; typename Mesh::CFVIter fv_it; typename Mesh::CVOHIter vh_it; typename Mesh::VHandle v0, v1; typename Mesh::FHandle f0, f1; // remove properties mesh_.remove_property(valences_); mesh_.remove_property(update_); mesh_.remove_property(area_); // remove reference again delete_reference(); // unlock all vertices & edges for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) { mesh_.status(v_it).set_locked(false); } for (e_it=mesh_.edges_begin(), e_end=mesh_.edges_end(); e_it!=e_end; ++e_it) { mesh_.status(e_it).set_locked(false); } // de-select if nothing was selected before if (nothing_selected_) for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); v_it!=v_end; ++v_it) mesh_.status(v_it).set_selected(false); // free vertex and edge status mesh_.release_vertex_status(); mesh_.release_edge_status(); mesh_.release_face_status(); } //----------------------------------------------------------------------------- template void BaseRemesherT:: split_long_edges() { typename Mesh::VIter v_it, v_end; typename Mesh::EIter e_it, e_end; typename Mesh::VHandle v0, v1, vh; typename Mesh::EHandle eh, e0, e1; typename Mesh::FHandle f0, f1, f2, f3; bool ok, is_feature, is_boundary; int i; // un-tagg for (v_it = mesh_.vertices_begin(), v_end = mesh_.vertices_end(); v_it != v_end; ++v_it) mesh_.status(v_it).set_tagged(false); // handle Nastran PIDs during refinement OpenMesh::FPropHandleT pids; mesh_.get_property_handle(pids, "Nastran PIDs"); // split long edges for (ok = false, i = 0; !ok && i < 100; ++i) { ok = true; for (e_it = mesh_.edges_begin(), e_end = mesh_.edges_end(); e_it != e_end; ++e_it) { v0 = mesh_.to_vertex_handle(mesh_.halfedge_handle(e_it, 0)); v1 = mesh_.to_vertex_handle(mesh_.halfedge_handle(e_it, 1)); if (!mesh_.status(e_it).locked() && is_too_long(v0, v1)) { const typename Mesh::Point& p0 = mesh_.point(v0); const typename Mesh::Point& p1 = mesh_.point(v1); is_feature = mesh_.status(e_it).feature(); is_boundary = mesh_.is_boundary(e_it); vh = mesh_.add_vertex((p0 + p1) * 0.5); mesh_.split(e_it, vh); mesh_.status(vh).set_selected(true); if (is_feature) { eh = (is_boundary ? mesh_.edge_handle(mesh_.n_edges() - 2) : mesh_.edge_handle(mesh_.n_edges() - 3)); mesh_.status(eh).set_feature(true); mesh_.status(vh).set_feature(true); } else { project_to_reference(vh); } if (pids.is_valid()) { e0 = e_it.handle(); e1 = (is_boundary ? mesh_.edge_handle(mesh_.n_edges() - 2) : mesh_.edge_handle(mesh_.n_edges() - 3)); f0 = mesh_.face_handle(mesh_.halfedge_handle(e0, 0)); f1 = mesh_.face_handle(mesh_.halfedge_handle(e0, 1)); f2 = mesh_.face_handle(mesh_.halfedge_handle(e1, 0)); f3 = mesh_.face_handle(mesh_.halfedge_handle(e1, 1)); if (f0.is_valid() && f3.is_valid()) mesh_.property(pids, f3) = mesh_.property(pids, f0); if (f1.is_valid() && f2.is_valid()) mesh_.property(pids, f2) = mesh_.property(pids, f1); } ok = false; } } } if (i == 100) omlog() << "split break\n"; } //----------------------------------------------------------------------------- template void BaseRemesherT:: collapse_short_edges() { typename Mesh::EIter e_it, e_end; typename Mesh::CVVIter vv_it; typename Mesh::VHandle v0, v1; typename Mesh::HHandle h0, h1, h01, h10; typename Mesh::Point p; bool ok, skip, b0, b1, l0, l1, f0, f1; int i; bool hcol01, hcol10; for (ok = false, i = 0; !ok && i < 100; ++i) { ok = true; for (e_it = mesh_.edges_begin(), e_end = mesh_.edges_end(); e_it != e_end; ++e_it) { if (!mesh_.status(e_it).deleted() && !mesh_.status(e_it).locked()) { h10 = mesh_.halfedge_handle(e_it, 0); h01 = mesh_.halfedge_handle(e_it, 1); v0 = mesh_.to_vertex_handle(h10); v1 = mesh_.to_vertex_handle(h01); if (is_too_short(v0, v1)) { // get status b0 = mesh_.is_boundary(v0); b1 = mesh_.is_boundary(v1); l0 = mesh_.status(v0).locked(); l1 = mesh_.status(v1).locked(); f0 = mesh_.status(v0).feature(); f1 = mesh_.status(v1).feature(); hcol01 = hcol10 = true; if (mesh_.status(e_it).feature() && !f0 && !f1) std::cerr << "Bad luck" << std::endl; // boundary rules if (b0 && b1) { if (!mesh_.is_boundary(e_it)) continue; } else if (b0) hcol01 = false; else if (b1) hcol10 = false; // locked rules if (l0 && l1) continue; else if (l0) hcol01 = false; else if (l1) hcol10 = false; // feature rules // the other two edges removed by collapse must not be features h0 = mesh_.prev_halfedge_handle(h01); h1 = mesh_.next_halfedge_handle(h10); if (mesh_.status(mesh_.edge_handle(h0)).feature() || mesh_.status(mesh_.edge_handle(h1)).feature()) hcol01 = false; h0 = mesh_.prev_halfedge_handle(h10); h1 = mesh_.next_halfedge_handle(h01); if (mesh_.status(mesh_.edge_handle(h0)).feature() || mesh_.status(mesh_.edge_handle(h1)).feature()) hcol10 = false; if (f0 && f1) { // edge must be feature if (!mesh_.status(e_it).feature()) continue; // the other two edges removed by collapse must not be features h0 = mesh_.prev_halfedge_handle(h01); h1 = mesh_.next_halfedge_handle(h10); if (mesh_.status(mesh_.edge_handle(h0)).feature() || mesh_.status(mesh_.edge_handle(h1)).feature()) hcol01 = false; h0 = mesh_.prev_halfedge_handle(h10); h1 = mesh_.next_halfedge_handle(h01); if (mesh_.status(mesh_.edge_handle(h0)).feature() || mesh_.status(mesh_.edge_handle(h1)).feature()) hcol10 = false; } else if (f0) hcol01 = false; else if (f1) hcol10 = false; // topological rules if (hcol01) hcol01 = mesh_.is_collapse_ok(h01); if (hcol10) hcol10 = mesh_.is_collapse_ok(h10); // both collapses possible: collapse into vertex w/ higher valence if (hcol01 && hcol10) { if (mesh_.valence(v0) < mesh_.valence(v1)) hcol10 = false; else hcol01 = false; } // try v1 -> v0 if (hcol10) { // don't create too long edges skip = false; for (vv_it = mesh_.cvv_iter(v1); vv_it && !skip; ++vv_it) if (is_too_long(v0, vv_it)) skip = true; if (!skip) { mesh_.collapse(h10); ok = false; } } // try v0 -> v1 else if (hcol01) { // don't create too long edges skip = false; for (vv_it = mesh_.cvv_iter(v0); vv_it && !skip; ++vv_it) if (is_too_long(v1, vv_it)) skip = true; if (!skip) { mesh_.collapse(h01); ok = false; } } } } } } mesh_.garbage_collection(); if (i == 100) omlog() << "collapse break\n"; } //----------------------------------------------------------------------------- template void BaseRemesherT:: flip_edges() { typename Mesh::EIter e_it, e_end; typename Mesh::VIter v_it, v_end; typename Mesh::VHandle v0, v1, v2, v3, vh; typename Mesh::HHandle hh; typename Mesh::Point p; typename Mesh::FHandle fh; int val0, val1, val2, val3; int val_opt0, val_opt1, val_opt2, val_opt3; int ve0, ve1, ve2, ve3, ve_before, ve_after; bool ok; int i; // compute vertex valences for (v_it = mesh_.vertices_begin(), v_end = mesh_.vertices_end(); v_it != v_end; ++v_it) mesh_.property(valences_, v_it) = mesh_.valence(v_it); // flip all edges for (ok = false, i = 0; !ok && i < 100; ++i) { ok = true; for (e_it = mesh_.edges_begin(), e_end = mesh_.edges_end(); e_it != e_end; ++e_it) { if (!mesh_.status(e_it).locked() && !mesh_.status(e_it).feature()) { hh = mesh_.halfedge_handle(e_it, 0); v0 = mesh_.to_vertex_handle(hh); v2 = mesh_.to_vertex_handle(mesh_.next_halfedge_handle(hh)); if ( !mesh_.next_halfedge_handle(hh).is_valid() ) { std::cerr << "Error v2" << std::endl; continue; } hh = mesh_.halfedge_handle(e_it, 1); v1 = mesh_.to_vertex_handle(hh); v3 = mesh_.to_vertex_handle(mesh_.next_halfedge_handle(hh)); if ( !mesh_.next_halfedge_handle(hh).is_valid() ) { std::cerr << "Error v3" << std::endl; continue; } if ( !v2.is_valid()) continue; if (!mesh_.status(v0).locked() && !mesh_.status(v1).locked() && !mesh_.status(v2).locked() && !mesh_.status(v3).locked()) { val0 = mesh_.property(valences_, v0); val1 = mesh_.property(valences_, v1); val2 = mesh_.property(valences_, v2); val3 = mesh_.property(valences_, v3); val_opt0 = (mesh_.is_boundary(v0) ? 4 : 6); val_opt1 = (mesh_.is_boundary(v1) ? 4 : 6); val_opt2 = (mesh_.is_boundary(v2) ? 4 : 6); val_opt3 = (mesh_.is_boundary(v3) ? 4 : 6); ve0 = (val0 - val_opt0); ve0 *= ve0; ve1 = (val1 - val_opt1); ve1 *= ve1; ve2 = (val2 - val_opt2); ve2 *= ve2; ve3 = (val3 - val_opt3); ve3 *= ve3; ve_before = ve0 + ve1 + ve2 + ve3; --val0; --val1; ++val2; ++val3; ve0 = (val0 - val_opt0); ve0 *= ve0; ve1 = (val1 - val_opt1); ve1 *= ve1; ve2 = (val2 - val_opt2); ve2 *= ve2; ve3 = (val3 - val_opt3); ve3 *= ve3; ve_after = ve0 + ve1 + ve2 + ve3; if (ve_before > ve_after && mesh_.is_flip_ok(e_it)) { mesh_.flip(e_it); --mesh_.property(valences_, v0); --mesh_.property(valences_, v1); ++mesh_.property(valences_, v2); ++mesh_.property(valences_, v3); ok = false; } } } } } if (i == 100) omlog() << "flip break\n"; } //----------------------------------------------------------------------------- template void BaseRemesherT:: tangential_smoothing(bool _use_projection) { typename Mesh::VIter v_it, v_end(mesh_.vertices_end()); typename Mesh::CVVIter vv_it; typename Mesh::Scalar valence; typename Mesh::Point u, n; // tag active vertices for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) mesh_.status(v_it).set_tagged( !mesh_.status(v_it).locked() && !mesh_.status(v_it).feature() && !mesh_.is_boundary(v_it) ); // smooth for (int iters=0; iters<10; ++iters) { for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) { if (mesh_.status(v_it).tagged()) { u.vectorize(0.0); valence = 0; for (vv_it=mesh_.cvv_iter(v_it); vv_it; ++vv_it) { u += mesh_.point(vv_it); ++valence; } if (valence) { u *= (1.0/valence); u -= mesh_.point(v_it); n = mesh_.normal(v_it); n *= (u | n); u -= n; } mesh_.property(update_, v_it) = u; } } for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) if (mesh_.status(v_it).tagged()) mesh_.point(v_it) += mesh_.property(update_, v_it); } // reset tagged status for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) mesh_.status(v_it).set_tagged(false); // project if (_use_projection) for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) if (!mesh_.status(v_it).locked() && !mesh_.status(v_it).feature()) project_to_reference(v_it); } //----------------------------------------------------------------------------- template void BaseRemesherT:: balanace_area(unsigned int _iters, bool _use_projection) { typename Mesh::VIter v_it, v_end(mesh_.vertices_end()); typename Mesh::CVVIter vv_it; typename Mesh::Scalar w, ww; typename Mesh::Point u, n; bool omit; DiffGeoT diffgeo(mesh_); // tag active vertices for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) { bool active = ( !mesh_.status(v_it).locked() && !mesh_.status(v_it).feature() && !mesh_.is_boundary(v_it) ); // don't move neighbors of boundary vertices for (vv_it=mesh_.cvv_iter(v_it); active && vv_it; ++vv_it) if (mesh_.is_boundary(vv_it)) active = false; mesh_.status(v_it).set_tagged( active ); } // tag2 vertices for which area has to be computed for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) { mesh_.status(v_it).set_tagged2(false); for (vv_it=mesh_.cvv_iter(v_it); vv_it; ++vv_it) { if (mesh_.status(vv_it).tagged()) { mesh_.status(v_it).set_tagged2(true); break; } } } for (unsigned int bla=0; bla<_iters; ++bla) { // compute vertex areas for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) if (mesh_.status(v_it).tagged2()) mesh_.property(area_, v_it) = pow(diffgeo.compute_area(v_it), 2); // smooth for (int iters=0; iters<10; ++iters) { for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) { if (mesh_.status(v_it).tagged()) { u.vectorize(0.0); ww = 0; omit = false; for (vv_it=mesh_.cvv_iter(v_it); vv_it; ++vv_it) { w = mesh_.property(area_, vv_it); u += mesh_.point(vv_it) * w; ww += w; } if (ww) { u *= (1.0/ww); u -= mesh_.point(v_it); n = mesh_.normal(v_it); n *= (u | n); u -= n; u *= 0.1; } mesh_.property(update_, v_it) = u; } } for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) if (!mesh_.status(v_it).locked() && !mesh_.status(v_it).feature() && !mesh_.is_boundary(v_it)) mesh_.point(v_it) += mesh_.property(update_, v_it); } // project if (_use_projection) for (v_it=mesh_.vertices_begin(); v_it!=v_end; ++v_it) if (!mesh_.status(v_it).locked() && !mesh_.status(v_it).feature()) project_to_reference(v_it); // if (emit_progress_) // if (!Progress().step(3)) // break; } } //----------------------------------------------------------------------------- template void BaseRemesherT:: remove_caps() { typename Mesh::EdgeIter e_it, e_end(mesh_.edges_end()); typename Mesh::HalfedgeHandle h; typename Mesh::Scalar a0, a1, amin, aa(cos(170.0 * M_PI / 180.0)); typename Mesh::VHandle v, vb, vd; typename Mesh::FHandle fb, fd; typename Mesh::Point a, b, c, d; // handle Nastran PIDs for edge flips OpenMesh::FPropHandleT pids; mesh_.get_property_handle(pids, "Nastran PIDs"); for (e_it=mesh_.edges_begin(); e_it!=e_end; ++e_it) { if (!mesh_.status(e_it).locked() && mesh_.is_flip_ok(e_it)) { h = mesh_.halfedge_handle(e_it.handle(), 0); a = mesh_.point(mesh_.to_vertex_handle(h)); h = mesh_.next_halfedge_handle(h); b = mesh_.point(vb=mesh_.to_vertex_handle(h)); h = mesh_.halfedge_handle(e_it.handle(), 1); c = mesh_.point(mesh_.to_vertex_handle(h)); h = mesh_.next_halfedge_handle(h); d = mesh_.point(vd=mesh_.to_vertex_handle(h)); a0 = ( (a-b).normalize() | (c-b).normalize() ); a1 = ( (a-d).normalize() | (c-d).normalize() ); if (a0 < a1) { amin = a0; v = vb; } else { amin = a1; v = vd; } // is it a cap? if (amin < aa) { // feature edge and feature vertex -> seems to be intended if (mesh_.status(e_it).feature() && mesh_.status(v).feature()) continue; // handle PIDs: flip = split + collapse if (pids.is_valid()) { fb = mesh_.face_handle(mesh_.halfedge_handle(e_it, 0)); fd = mesh_.face_handle(mesh_.halfedge_handle(e_it, 1)); if (v == vb) mesh_.property(pids, fb) = mesh_.property(pids, fd); else mesh_.property(pids, fd) = mesh_.property(pids, fb); } // project v onto feature edge if (mesh_.status(e_it).feature()) mesh_.set_point(v, (a+c)*0.5); // flip mesh_.flip(e_it); } } } } //============================================================================= } // namespace Remeshing //=============================================================================