Commit 8f68b49e authored by Matthias Möller's avatar Matthias Möller

- fix slot descriptions

- add remeshing with face selection (code provided by Vladimir Chalupecky. thanks)
- add switch selection face/vertex selection in gui and add scriptfunctions
- add more doc

refs #1572

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@17441 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 65c581eb
...@@ -166,7 +166,8 @@ remesh(Scalar _error, ...@@ -166,7 +166,8 @@ remesh(Scalar _error,
Scalar _emin, Scalar _emin,
Scalar _emax, Scalar _emax,
unsigned int _iters, unsigned int _iters,
bool _use_projection) bool _use_projection,
Selection _selection)
{ {
// set thesholds // set thesholds
error_ = _error; error_ = _error;
...@@ -174,7 +175,7 @@ remesh(Scalar _error, ...@@ -174,7 +175,7 @@ remesh(Scalar _error,
emax_ = _emax; emax_ = _emax;
// do it // do it
Base::remesh(_iters, 0, _use_projection); Base::remesh(_iters, 0, _use_projection, _selection);
// free curvature property (refmesh has been deleted already) // free curvature property (refmesh has been deleted already)
Base::mesh_.remove_property(curvature_); Base::mesh_.remove_property(curvature_);
......
...@@ -70,6 +70,7 @@ namespace Remeshing { ...@@ -70,6 +70,7 @@ namespace Remeshing {
template <class Mesh> template <class Mesh>
class AdaptiveRemesherT : public BaseRemesherT<Mesh> class AdaptiveRemesherT : public BaseRemesherT<Mesh>
{ {
typedef typename BaseRemesherT<Mesh>::Selection Selection;
public: public:
typedef BaseRemesherT<Mesh> Base; typedef BaseRemesherT<Mesh> Base;
...@@ -85,7 +86,8 @@ public: ...@@ -85,7 +86,8 @@ public:
Scalar _min_edge_length, Scalar _min_edge_length,
Scalar _max_edge_length, Scalar _max_edge_length,
unsigned int _iters, unsigned int _iters,
bool _use_projection = true); bool _use_projection = true,
Selection _selection=BaseRemesherT<Mesh>::SELECT_VERTEX);
......
...@@ -231,11 +231,15 @@ void ...@@ -231,11 +231,15 @@ void
BaseRemesherT<Mesh>:: BaseRemesherT<Mesh>::
remesh(unsigned int _iters, remesh(unsigned int _iters,
unsigned int _area_iters, unsigned int _area_iters,
bool _use_projection) { bool _use_projection,
Selection _selection) {
try try
{ {
prepare(); if (_selection == VERTEX_SELECTION)
prepare_vertex_selection();
else if (_selection == FACE_SELECTION)
prepare_face_selection();
remeshh(_iters, _area_iters, _use_projection); remeshh(_iters, _area_iters, _use_projection);
} }
catch (std::bad_alloc&) catch (std::bad_alloc&)
...@@ -247,14 +251,12 @@ remesh(unsigned int _iters, ...@@ -247,14 +251,12 @@ remesh(unsigned int _iters,
cleanup(); cleanup();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <class Mesh> template <class Mesh>
void void
BaseRemesherT<Mesh>:: BaseRemesherT<Mesh>::
prepare() prepare_vertex_selection()
{ {
typename Mesh::EIter e_it, e_end; typename Mesh::EIter e_it, e_end;
typename Mesh::VIter v_it, v_end; typename Mesh::VIter v_it, v_end;
...@@ -281,7 +283,7 @@ prepare() ...@@ -281,7 +283,7 @@ prepare()
if (nothing_selected_) if (nothing_selected_)
for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end(); for (v_it=mesh_.vertices_begin(), v_end=mesh_.vertices_end();
v_it!=v_end; ++v_it) v_it!=v_end; ++v_it)
mesh_.status(*v_it).set_selected(true); mesh_.status(*v_it).set_selected(true);
...@@ -297,11 +299,115 @@ prepare() ...@@ -297,11 +299,115 @@ prepare()
v0 = mesh_.to_vertex_handle(mesh_.halfedge_handle(*e_it, 0)); v0 = mesh_.to_vertex_handle(mesh_.halfedge_handle(*e_it, 0));
v1 = mesh_.to_vertex_handle(mesh_.halfedge_handle(*e_it, 1)); v1 = mesh_.to_vertex_handle(mesh_.halfedge_handle(*e_it, 1));
mesh_.status(*e_it).set_locked(mesh_.status(v0).locked() || mesh_.status(*e_it).set_locked(mesh_.status(v0).locked() ||
mesh_.status(v1).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.is_valid(); ++vh_it)
if (mesh_.status(mesh_.edge_handle(*vh_it)).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 <class Mesh>
void
BaseRemesherT<Mesh>::
prepare_face_selection()
{
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 (f_it = mesh_.faces_begin(), f_end = mesh_.faces_end(); f_it != f_end;
++f_it)
{
if (mesh_.status(*f_it).selected())
{
nothing_selected_ = false;
break;
}
}
if (nothing_selected_)
MeshSelection::selectAllFaces(&mesh_);
// lock un-selected vertices & edges
for (v_it = mesh_.vertices_begin(), v_end = mesh_.vertices_end();
v_it != v_end; ++v_it)
{
bool all_faces_selected = true;
for (typename Mesh::ConstVertexFaceIter vf_it = mesh_.cvf_iter(*v_it);
vf_it.is_valid(); ++vf_it)
{
if (!mesh_.status(*vf_it).selected())
{
all_faces_selected = false;
break;
}
}
mesh_.status(*v_it).set_locked(!all_faces_selected);
}
for (e_it = mesh_.edges_begin(), e_end = mesh_.edges_end();
e_it != e_end; ++e_it)
{
if (mesh_.is_boundary(*e_it))
{
mesh_.status(*e_it).set_locked(true);
}
else
{
f0 = mesh_.face_handle(mesh_.halfedge_handle(*e_it, 0));
f1 = mesh_.face_handle(mesh_.halfedge_handle(*e_it, 1));
mesh_.status(*e_it).set_locked(!(mesh_.status(f0).selected() && mesh_.status(f1).selected()));
}
}
MeshSelection::clearFaceSelection(&mesh_);
// handle feature corners: // handle feature corners:
// lock corner vertices (>2 feature edges) and endpoints // lock corner vertices (>2 feature edges) and endpoints
// of feature lines (1 feature edge) // of feature lines (1 feature edge)
......
...@@ -50,6 +50,12 @@ ...@@ -50,6 +50,12 @@
#ifndef BASE_REMESHERT_HH #ifndef BASE_REMESHERT_HH
#define BASE_REMESHERT_HH #define BASE_REMESHERT_HH
/**
* BaseRemesher implements a modified version of a remesher approach presented by Mario Botsch and Leif Kobbelt in
* "A Remeshing Approach to Multiresolution Modeling" published at Symposium on Geometry Processing 2004 p. 189-196
* Paper can found at: http://www.graphics.rwth-aachen.de/publications/0000/37/
*/
//== INCLUDES ================================================================= //== INCLUDES =================================================================
...@@ -74,6 +80,12 @@ class BaseRemesherT ...@@ -74,6 +80,12 @@ class BaseRemesherT
{ {
public: public:
enum Selection
{
VERTEX_SELECTION,
FACE_SELECTION
};
typedef typename Mesh::Scalar Scalar; typedef typename Mesh::Scalar Scalar;
typedef typename Mesh::Point Point; typedef typename Mesh::Point Point;
typedef typename Mesh::EdgeHandle EdgeHandle; typedef typename Mesh::EdgeHandle EdgeHandle;
...@@ -85,13 +97,17 @@ public: ...@@ -85,13 +97,17 @@ public:
void remesh(unsigned int _iters, void remesh(unsigned int _iters,
unsigned int _area_iters, unsigned int _area_iters,
bool _use_projection = true); bool _use_projection = true,
Selection _selection=VERTEX_SELECTION);
protected: protected:
void prepare(); /// prepare for remeshing only selected vertices (if no vertex was selected, remesh whole mesh)
void prepare_vertex_selection();
/// prepare for remeshing only vertices which are fully surrounded by selected faces (if no face was selected, remesh whole mesh)
void prepare_face_selection();
void remeshh(unsigned int _iters, unsigned int _aiters, bool _proj); void remeshh(unsigned int _iters, unsigned int _aiters, bool _proj);
void cleanup(); void cleanup();
......
...@@ -67,6 +67,7 @@ namespace Remeshing { ...@@ -67,6 +67,7 @@ namespace Remeshing {
template <class Mesh> template <class Mesh>
class UniformRemesherT : public BaseRemesherT<Mesh> class UniformRemesherT : public BaseRemesherT<Mesh>
{ {
typedef typename BaseRemesherT<Mesh>::Selection Selection;
public: public:
typedef BaseRemesherT<Mesh> Base; typedef BaseRemesherT<Mesh> Base;
...@@ -82,13 +83,14 @@ public: ...@@ -82,13 +83,14 @@ public:
void remesh(Scalar _edge_length, void remesh(Scalar _edge_length,
unsigned int _iters, unsigned int _iters,
unsigned int _area_iters, unsigned int _area_iters,
bool _use_projection = true) bool _use_projection = true,
Selection _selection = BaseRemesherT<Mesh>::VERTEX_SELECTION)
{ {
// emin_ = 4.0/5.0 * _edge_length; sqr_emin_ = emin_ * emin_; // emin_ = 4.0/5.0 * _edge_length; sqr_emin_ = emin_ * emin_;
// emax_ = 4.0/3.0 * _edge_length; sqr_emax_ = emax_ * emax_; // emax_ = 4.0/3.0 * _edge_length; sqr_emax_ = emax_ * emax_;
emin_ = 0.7 * _edge_length; sqr_emin_ = emin_ * emin_; emin_ = 0.7 * _edge_length; sqr_emin_ = emin_ * emin_;
emax_ = 1.4 * _edge_length; sqr_emax_ = emax_ * emax_; emax_ = 1.4 * _edge_length; sqr_emax_ = emax_ * emax_;
Base::remesh(_iters, _area_iters, _use_projection); Base::remesh(_iters, _area_iters, _use_projection,_selection);
} }
......
...@@ -79,12 +79,34 @@ RemesherPlugin::~RemesherPlugin() { ...@@ -79,12 +79,34 @@ RemesherPlugin::~RemesherPlugin() {
/// Initialize the plugin /// Initialize the plugin
void RemesherPlugin::pluginsInitialized(){ void RemesherPlugin::pluginsInitialized(){
emit setSlotDescription("adaptiveRemeshing(int,double,double,double,int,bool)", "Adaptive Remeshing", emit setSlotDescription("adaptiveRemeshing(int,double,double,double,uint,bool)", "Adaptive Remeshing with vertex selection",
QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","), QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","),
QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(",")); QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(","));
emit setSlotDescription("uniformRemeshing(int,double,unsigned int,unsigned int,bool)", "Uniform Remeshing", emit setSlotDescription("adaptiveRemeshing(int,double,double,double,uint)", "Adaptive Remeshing with vertex selection and projection method",
QString("object_id,error,min_edge_length,max_edge_length,iterations").split(","),
QString("id of an object,error,minimal target edge length,maximal target edge length,iterations").split(","));
emit setSlotDescription("adaptiveRemeshingFaceSelection(int,double,double,double,uint,bool)", "Adaptive Remeshing with face selection",
QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","),
QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(","));
emit setSlotDescription("adaptiveRemeshingFaceSelection(int,double,double,double,uint)", "Adaptive Remeshing with face selection and projection method",
QString("object_id,error,min_edge_length,max_edge_length,iterations").split(","),
QString("id of an object,error,minimal target edge length,maximal target edge length,iterations").split(","));
emit setSlotDescription("uniformRemeshing(int,double,uint,uint,bool)", "Uniform Remeshing with vertex selection",
QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","), QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","),
QString("id of an object,target edge length,iterations,area iterations,use projection method").split(",")); QString("id of an object,target edge length,iterations,area iterations,use projection method").split(","));
emit setSlotDescription("uniformRemeshing(int,double,uint,uint)", "Uniform Remeshing with vertex selection and projection method",
QString("object_id,edge_length,iterations,area_iterations").split(","),
QString("id of an object,target edge length,iterations,area iterations").split(","));
emit setSlotDescription("uniformRemeshingFaceSelection(int,double,uint,uint,bool)", "Uniform Remeshing with face selection",
QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","),
QString("id of an object,target edge length,iterations,area iterations,use projection method").split(","));
emit setSlotDescription("uniformRemeshingFaceSelection(int,double,uint,uint)", "Uniform Remeshing with face selection and projection method",
QString("object_id,edge_length,iterations,area_iterations").split(","),
QString("id of an object,target edge length,iterations,area iterations").split(","));
} }
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
...@@ -271,8 +293,9 @@ void RemesherPlugin::adaptiveRemeshing() { ...@@ -271,8 +293,9 @@ void RemesherPlugin::adaptiveRemeshing() {
double max_edge = tool_->adaptive_max_edge->value(); double max_edge = tool_->adaptive_max_edge->value();
unsigned int iters = tool_->adaptive_iters->text().toInt(); unsigned int iters = tool_->adaptive_iters->text().toInt();
bool projection = tool_->adaptive_projection->isChecked(); bool projection = tool_->adaptive_projection->isChecked();
bool vertexSelection = (tool_->adaptive_selection->currentIndex() == 0);
slotAdaptiveRemeshing(o_it->id(), error, min_edge, max_edge, iters, projection); slotAdaptiveRemeshing(o_it->id(), error, min_edge, max_edge, iters, projection,vertexSelection);
} }
} }
...@@ -284,7 +307,8 @@ void RemesherPlugin::slotAdaptiveRemeshing(int _objectID, ...@@ -284,7 +307,8 @@ void RemesherPlugin::slotAdaptiveRemeshing(int _objectID,
double _min_edge_length, double _min_edge_length,
double _max_edge_length, double _max_edge_length,
unsigned int _iters, unsigned int _iters,
bool _use_projection) { bool _use_projection,
bool _vertex_selection) {
operation_ = REMESH_ADAPTIVE; operation_ = REMESH_ADAPTIVE;
...@@ -299,7 +323,9 @@ void RemesherPlugin::slotAdaptiveRemeshing(int _objectID, ...@@ -299,7 +323,9 @@ void RemesherPlugin::slotAdaptiveRemeshing(int _objectID,
Remeshing::AdaptiveRemesherT<TriMesh> remesher(*mesh, progress_); Remeshing::AdaptiveRemesherT<TriMesh> remesher(*mesh, progress_);
remesher.remesh(_error, _min_edge_length, _max_edge_length, _iters, _use_projection); Remeshing::BaseRemesherT<TriMesh>::Selection selection = (_vertex_selection) ? Remeshing::BaseRemesherT<TriMesh>::VERTEX_SELECTION : Remeshing::BaseRemesherT<TriMesh>::FACE_SELECTION;
remesher.remesh(_error, _min_edge_length, _max_edge_length, _iters, _use_projection, selection);
mesh->update_normals(); mesh->update_normals();
...@@ -348,6 +374,7 @@ void RemesherPlugin::uniformRemeshing(){ ...@@ -348,6 +374,7 @@ void RemesherPlugin::uniformRemeshing(){
unsigned int iters = tool_->uniform_iters->text().toInt(); unsigned int iters = tool_->uniform_iters->text().toInt();
unsigned int area_iters = tool_->uniform_area_iters->text().toInt(); unsigned int area_iters = tool_->uniform_area_iters->text().toInt();
bool projection = tool_->uniform_projection->isChecked(); bool projection = tool_->uniform_projection->isChecked();
bool vertex_selection = (tool_->uniform_selection->currentIndex() == 0);
//read one target objects //read one target objects
for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH)) ; for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH)) ;
...@@ -369,7 +396,7 @@ void RemesherPlugin::uniformRemeshing(){ ...@@ -369,7 +396,7 @@ void RemesherPlugin::uniformRemeshing(){
// on edge flips which are only defined // on edge flips which are only defined
// for triangle configurations. // for triangle configurations.
slotUniformRemeshing(o_it->id(), edge_length, iters, area_iters, projection); slotUniformRemeshing(o_it->id(), edge_length, iters, area_iters, projection,vertex_selection);
} }
} }
...@@ -379,7 +406,8 @@ void RemesherPlugin::slotUniformRemeshing(int _objectID, ...@@ -379,7 +406,8 @@ void RemesherPlugin::slotUniformRemeshing(int _objectID,
double _edge_length, double _edge_length,
unsigned int _iters, unsigned int _iters,
unsigned int _area_iters, unsigned int _area_iters,
bool _use_projection) { bool _use_projection,
bool _vertex_selection) {
operation_ = REMESH_UNIFORM; operation_ = REMESH_UNIFORM;
...@@ -394,7 +422,9 @@ void RemesherPlugin::slotUniformRemeshing(int _objectID, ...@@ -394,7 +422,9 @@ void RemesherPlugin::slotUniformRemeshing(int _objectID,
Remeshing::UniformRemesherT<TriMesh> remesher(*mesh, progress_); Remeshing::UniformRemesherT<TriMesh> remesher(*mesh, progress_);
remesher.remesh(_edge_length, _iters, _area_iters, _use_projection); Remeshing::BaseRemesherT<TriMesh>::Selection selection = (_vertex_selection) ? Remeshing::BaseRemesherT<TriMesh>::VERTEX_SELECTION : Remeshing::BaseRemesherT<TriMesh>::FACE_SELECTION;
remesher.remesh(_edge_length, _iters, _area_iters, _use_projection, selection);
mesh->update_normals(); mesh->update_normals();
...@@ -446,6 +476,35 @@ void RemesherPlugin::uniformRemeshing(int _objectID, ...@@ -446,6 +476,35 @@ void RemesherPlugin::uniformRemeshing(int _objectID,
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
void RemesherPlugin::adaptiveRemeshingFaceSelection(int _objectID,
double _error,
double _min_edge_length,
double _max_edge_length,
unsigned int _iters,
bool _use_projection) {
slotAdaptiveRemeshing(_objectID,_error,_min_edge_length,_max_edge_length,_iters,_use_projection,false);
emit updatedObject(_objectID, UPDATE_TOPOLOGY );
emit createBackup(_objectID, "Adaptive remeshing", UPDATE_TOPOLOGY);
}
// ----------------------------------------------------------------------------------------
void RemesherPlugin::uniformRemeshingFaceSelection(int _objectID,
double _edge_length,
unsigned int _iters,
unsigned int _area_iters,
bool _use_projection) {
slotUniformRemeshing(_objectID,_edge_length,_iters,_area_iters,_use_projection,false);
emit updatedObject(_objectID, UPDATE_TOPOLOGY );
emit createBackup(_objectID, "Uniform remeshing", UPDATE_TOPOLOGY);
}
// ----------------------------------------------------------------------------------------
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2( remesherplugin, RemesherPlugin ); Q_EXPORT_PLUGIN2( remesherplugin, RemesherPlugin );
#endif #endif
......
...@@ -85,7 +85,7 @@ signals: ...@@ -85,7 +85,7 @@ signals:
void updatedObject(int _id, const UpdateType& _type); void updatedObject(int _id, const UpdateType& _type);
void setSlotDescription(QString _slotName, QString _slotDescription, void setSlotDescription(QString _slotName, QString _slotDescription,
QStringList _parameters, QStringList _descriptions); QStringList _parameters, QStringList _descriptions);
//LoggingInterface: //LoggingInterface:
void log( Logtype _type, QString _message ); void log( Logtype _type, QString _message );
...@@ -166,13 +166,15 @@ private slots: ...@@ -166,13 +166,15 @@ private slots:
double _min_edge_length, double _min_edge_length,
double _max_edge_length, double _max_edge_length,
unsigned int _iters, unsigned int _iters,
bool _use_projection = true); bool _use_projection = true,
bool _vertex_selection = true);
void slotUniformRemeshing(int _objectId, void slotUniformRemeshing(int _objectId,
double _edge_length, double _edge_length,
unsigned int _iters, unsigned int _iters,
unsigned int _area_iters, unsigned int _area_iters,
bool _use_projection = true); bool _use_projection = true,
bool _vertex_selection = true);
//scripting functions //scripting functions
...@@ -185,12 +187,25 @@ public slots: ...@@ -185,12 +187,25 @@ public slots:
unsigned int _iters, unsigned int _iters,
bool _use_projection = true); bool _use_projection = true);
void adaptiveRemeshingFaceSelection(int _objectId,
double _error,
double _min_edge_length,
double _max_edge_length,
unsigned int _iters,
bool _use_projection = true);
void uniformRemeshing(int _objectId, void uniformRemeshing(int _objectId,
double _edge_length, double _edge_length,
unsigned int _iters, unsigned int _iters,
unsigned int _area_iters, unsigned int _area_iters,
bool _use_projection = true); bool _use_projection = true);
void uniformRemeshingFaceSelection(int _objectId,
double _edge_length,
unsigned int _iters,
unsigned int _area_iters,
bool _use_projection = true);
public slots: public slots:
QString version() { return QString("1.0"); }; QString version() { return QString("1.0"); };
}; };
......
...@@ -22,6 +22,15 @@ ...@@ -22,6 +22,15 @@
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Give the selection mode which will be used.&lt;/p&gt;&lt;p&gt;If nothing of the specified is selected, the whole mesh will be remeshed.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Vertex Selection: &lt;/span&gt;Remesh all selected vertices.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Face Selection: &lt;/span&gt;Remesh all vertices surrounded by the selected face.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="statusTip">
<string>Give the selection mode which will be used. If nothing of the specified is selected, the whole mesh will be remeshed. Vertex Selection: Remesh all selected vertices. Face Selection: Remesh all vertices surrounded by the selected face.</string>
</property>