Developer Documentation
SmootherPlugin.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 
44 //=============================================================================
45 //
46 // CLASS SmootherPlugin - IMPLEMENTATION
47 //
48 //=============================================================================
49 
50 
51 //== INCLUDES =================================================================
52 
53 
54 #include "SmootherPlugin.hh"
55 
56 #include "SmootherObject.hh"
57 
58 
59 #define SMOOTHER "SmootherData"
60 
61 //== IMPLEMENTATION ==========================================================
62 
63 SmootherPlugin::SmootherPlugin() :
64  tool_(0),
65  toolIcon_(0)
66 {
67 }
68 
69 //-----------------------------------------------------------------------------
70 
71 void
72 SmootherPlugin::
73 initializePlugin()
74 {
75  if ( OpenFlipper::Options::gui() ) {
77  QSize size(100, 100);
78  tool_->resize(size);
79 
80  // connect signals->slots
81  connect(tool_->pB_smooth,SIGNAL(clicked() ),this,SLOT(slot_smooth()));
82 
83  toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"smoother2.png");
84  emit addToolbox( tr("Smoother") , tool_, toolIcon_ );
85  }
86 }
87 
88 //-----------------------------------------------------------------------------
89 
93 void
95 
96  emit setSlotDescription("smooth(int,int,QString,QString,double,bool)", "Smooth an object",
97  QString("object_id,iterations,direction,continuity,maxDistance,respectFeatures").split(","),
98  QString("id of an object, number of smoothing iterations, Smoothing direction. (tangential;normal;tangential+normal), Continuity. (C1 or C2), max distance the smoothed mesh is allowed to differ from the original,Keep features intact").split(","));
99 
100  emit setSlotDescription("smooth(int,int,QString,QString,double)", "Smooth an object",
101  QString("object_id,iterations,direction,continuity,maxDistance").split(","),
102  QString("id of an object, number of smoothing iterations, Smoothing direction. (tangential;normal;tangential+normal), Continuity. (C1 or C2), max distance the smoothed mesh is allowed to differ from the original").split(","));
103 
104  emit setSlotDescription("smooth(int,int,QString,QString)", "Smooth an object",
105  QString("object_id,iterations,direction,continuity").split(","),
106  QString("id of an object, number of smoothing iterations, Smoothing direction. (tangential;normal;tangential+normal), Continuity. (C1 or C2)").split(","));
107 
108 }
109 
110 
111 //-----------------------------------------------------------------------------
112 
118 void
121 {
122  bool found = false;
123 
125  o_it != PluginFunctions::objectsEnd(); ++o_it) {
126 
127  QString jobDescription = "Smoothed (";
128 
130 
131  if ( object == 0 ) {
132  emit log(LOGWARN , "Unable to get object ( Only Triangle Meshes supported)");
133  continue;
134  }
135 
136  found = true;
137 
138  SmootherObject* data = dynamic_cast< SmootherObject* > ( o_it->objectData(SMOOTHER) );
139 
140  // Get triangle mesh
141  TriMesh* mesh = PluginFunctions::triMesh(*o_it);
142 
143  if ( mesh == NULL ) {
144  emit log(LOGERR, "Unable to get mesh from object( Only Triangle Meshes supported)");
145  return;
146  }
147 
148  if (data == 0){
149  data = new SmootherObject();
150  o_it->setObjectData(SMOOTHER, data);
151  }
152 
153  // Create smoother
154  SmootherType smoother(*mesh);
155 
157  if( tool_->rbTangential_and_Normal->isChecked() ) {
159  jobDescription += "tangential and normal,";
160  } else if( tool_->rbNormal->isChecked() ) {
162  jobDescription += "normal,";
163  } else if( tool_->rbTangential->isChecked() ) {
165  jobDescription += "tangential,";
166  }
167 
168  // Set perObjectData
169  data->component(component);
170 
171  OpenMesh::Smoother::SmootherT< TriMesh >::Continuity continuity = OpenMesh::Smoother::SmootherT< TriMesh >::C0;
172  if( tool_->rB_c0->isChecked() ) {
174  jobDescription += "C0";
175  } else if( tool_->rB_c1->isChecked() ) {
177  jobDescription += "C1";
178  }
179 
180  // Set perObjectData
181  data->continuity(continuity);
182 
183  // Read maximum distance Error from lineEdit
184  if ( tool_->cbDistance->isChecked() ) {
185  QString value;
186  value = tool_->distance->text();
187  bool ok = false;
188 
189  double absoluteError = value.toDouble(&ok);
190 
191  if ( ok ) {
192  data->distance(absoluteError);
193  smoother.set_absolute_local_error( absoluteError );
194  } else {
195  emit log(LOGWARN , "Unable to read distance error from LineEdit");
196  }
197 
198  jobDescription += ",max_error: " + QString::number(absoluteError);
199  }
200 
201  // Set perObjectData
202  data->features(tool_->respectFeatures->isChecked());
203  data->iterations(tool_->sB_iter->value());
204 
205  // Initialize smoother
206 
207  if(tool_->cbReinitialize->isChecked() || !data->initialized())
208  {
209  smoother.initialize(component,continuity );
210  data->initialized(true);
211  }
212 
213  smoother.skip_features(data->features());
214 
215  smoother.smooth( data->iterations() );
216 
217  jobDescription += ") " + QString::number(tool_->sB_iter->value()) + " iterations";
218 
219  mesh->update_normals();
220 
221  emit updatedObject( o_it->id(), UPDATE_GEOMETRY );
222  emit createBackup(o_it->id(), "Smoothing", UPDATE_GEOMETRY );
223  }
224 
225  if ( !found )
226  emit log(LOGERR , tr("Unable to smooth. No triangle mesh selected as target!") );
227 
228 }
229 
230 
231 //-----------------------------------------------------------------------------
232 
233 void SmootherPlugin::smooth(int _objectId , int _iterations , QString _direction , QString _continuity, double _maxDistance, bool _respectFeatures ) {
234 
235  BaseObjectData* baseObjectData;
236  if ( ! PluginFunctions::getObject(_objectId,baseObjectData) ) {
237  emit log(LOGERR,"Unable to get Object");
238  return;
239  }
240 
241  if ( baseObjectData->dataType() == DATA_TRIANGLE_MESH ) {
242  TriMeshObject* object = PluginFunctions::triMeshObject(baseObjectData);
243 
244  QString jobDescription = "Smoothed (";
245 
246  if ( object == 0 ) {
247  emit log(LOGWARN , "Unable to get object ( Only Triangle Meshes supported)");
248  return;
249  }
250 
251  SmootherObject* data = dynamic_cast< SmootherObject* > ( object->objectData(SMOOTHER) );
252 
253  // Get triangle mesh
254  TriMesh* mesh = PluginFunctions::triMesh(object);
255 
256  if ( mesh == NULL ) {
257  emit log(LOGERR, "Unable to get mesh from object( Only Triangle Meshes supported)");
258  return;
259  }
260 
261  if (data == 0){
262  data = new SmootherObject();
263  object->setObjectData(SMOOTHER, data);
264  }
265 
266  SmootherType smoother(*mesh);
267 
269  bool tangential = _direction.contains("tangential");
270  bool normal = _direction.contains("normal");
271 
272  if ( tangential && normal ) {
274  jobDescription += "tangential and normal,";
275  } else if ( tangential ) {
277  jobDescription += "normal,";
278  } else {
280  jobDescription += "tangential,";
281  }
282 
283  // Set perObjectData
284  data->component(component);
285 
286  OpenMesh::Smoother::SmootherT< TriMesh >::Continuity continuity = OpenMesh::Smoother::SmootherT< TriMesh >::C0;
287  bool c0 = _continuity.contains("C0");
288  bool c1 = _continuity.contains("C1");
289 
290  if ( c0 && c1 )
291  std::cerr << "Continuity C0 + C1 ? Using C1" << std::endl;
292  if( c1 ) {
294  jobDescription += "C1";
295  } else if( c0 ) {
297  jobDescription += "C0";
298  }
299 
300  // Set perObjectData
301  data->continuity(continuity);
302 
303  if ( _maxDistance > 0.0) {
304  // Set perObjectData
305  data->distance(_maxDistance);
306  smoother.set_absolute_local_error( _maxDistance );
307  jobDescription += ",max_error: " + QString::number(_maxDistance);
308  } else {
309  // Set perObjectData
310  data->distance( FLT_MAX );
311  smoother.set_absolute_local_error( FLT_MAX );
312  }
313 
314  // Keep features?
315  data->features(_respectFeatures);
316  smoother.skip_features(_respectFeatures);
317 
318  smoother.initialize(component,continuity);
319 
320  smoother.smooth( _iterations );
321 
322  jobDescription += ") " + QString::number(_iterations) + " iterations";
323 
324  mesh->update_normals();
325 
326  emit updatedObject( object->id(), UPDATE_GEOMETRY );
327 
328  // Create backup
329  emit createBackup(object->id(),"Smoothing", UPDATE_GEOMETRY );
330 
331  emit scriptInfo(tr("smooth(%1, %2, %3, %4, %5)").arg(QString::number(_objectId), QString::number(_iterations),
332  _direction, _continuity, QString::number(_maxDistance)));
333 
334  } else {
335  emit log(LOGERR,"Unsupported object type for smoother");
336  return;
337  }
338 
339 }
340 
341 //-----------------------------------------------------------------------------
342 
343 
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
void smooth(unsigned int _n)
Do _n smoothing iterations.
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
SmootherToolbarWidget * tool_
Widget for Toolbox.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
void set_absolute_local_error(Scalar _err)
Set local error as an absolute value.
void pluginsInitialized()
Set the scripting slot descriptions.
int id() const
Definition: BaseObject.cc:190
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:67
void slot_smooth()
Slot connected to the smooth button in the toolbox.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
void smooth(int _objectId, int _iterations, QString _direction, QString _continuity, double _maxDistance=-1.0, bool _respectFeatures=true)
smooth an object
void skip_features(bool _state)
enable or disable feature handling
Definition: SmootherT.hh:167