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 #include "SmootherPlugin.hh"
44 
45 
48 
49 #include <QGridLayout>
50 #include <QPushButton>
51 #include <QLabel>
52 
53 SmootherPlugin::SmootherPlugin() :
54  iterationsSpinbox_(0)
55 {
56 
57 }
58 
59 SmootherPlugin::~SmootherPlugin()
60 {
61 
62 }
63 
64 void SmootherPlugin::initializePlugin()
65 {
66  // Create the Toolbox Widget
67  QWidget* toolBox = new QWidget();
68  QGridLayout* layout = new QGridLayout(toolBox);
69 
70  QPushButton* smoothButton = new QPushButton("&Smooth",toolBox);
71  smoothButton->setToolTip(tr("Smooths an Object using Laplacian Smoothing."));
72  smoothButton->setWhatsThis(tr("Smooths an Object using Laplacian Smoothing. Use the Smooth Plugin for more options."));
73 
74 
75 
76  iterationsSpinbox_ = new QSpinBox(toolBox) ;
77  iterationsSpinbox_->setMinimum(1);
78  iterationsSpinbox_->setMaximum(1000);
79  iterationsSpinbox_->setSingleStep(1);
80  iterationsSpinbox_->setToolTip(tr("The number of the smooting operations."));
81  iterationsSpinbox_->setWhatsThis(tr("Give the number, how often the Laplacian Smoothing should modify the object."));
82 
83  QLabel* label = new QLabel("Iterations:");
84 
85  layout->addWidget( label , 0, 0);
86  layout->addWidget( smoothButton , 1, 1);
87  layout->addWidget( iterationsSpinbox_, 0, 1);
88 
89  layout->addItem(new QSpacerItem(10,10,QSizePolicy::Expanding,QSizePolicy::Expanding),2,0,1,2);
90 
91  connect( smoothButton, SIGNAL(clicked()), this, SLOT(simpleLaplace()) );
92 
93  QIcon* toolIcon = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"smoother1.png");
94  emit addToolbox( tr("Simple Smoother") , toolBox, toolIcon );
95 }
96 
98 
99  // Emit slot description
100  emit setSlotDescription(tr("simpleLaplace(int)"), tr("Smooth mesh using the Laplace operator with uniform weights."),
101  QStringList(tr("iterations")), QStringList(tr("Number of iterations")));
102 }
103 
110 
111  int iterations = 1;
112 
113  if(!OpenFlipper::Options::nogui()) {
114  iterations = iterationsSpinbox_->value();
115  }
116 
117  simpleLaplace(iterations);
118 }
119 
127 void SmootherPlugin::simpleLaplace(int _iterations) {
128 
130 
131  bool selectionExists = false;
132 
133  if ( o_it->dataType( DATA_TRIANGLE_MESH ) ) {
134 
135  // Get the mesh to work on
136  TriMesh* mesh = PluginFunctions::triMesh(*o_it);
137 
138  // Property for the active mesh to store original point positions
140 
141  // Add a property to the mesh to store original vertex positions
142  mesh->add_property( origPositions, "SmootherPlugin_Original_Positions" );
143 
144  for ( int i = 0 ; i < _iterations ; ++i ) {
145 
146  // Copy original positions to backup ( in Vertex property )
147  TriMesh::VertexIter v_it, v_end=mesh->vertices_end();
148  for (v_it=mesh->vertices_begin(); v_it!=v_end; ++v_it) {
149  mesh->property( origPositions, *v_it ) = mesh->point(*v_it);
150  // See if at least one vertex has been selected
151  selectionExists |= mesh->status(*v_it).selected();
152  }
153 
154  // Do one smoothing step (For each point of the mesh ... )
155  for (v_it=mesh->vertices_begin(); v_it!=v_end; ++v_it) {
156 
157  if(selectionExists && mesh->status(*v_it).selected() == false) {
158  continue;
159  }
160 
161  TriMesh::Point point = TriMesh::Point(0.0,0.0,0.0);
162 
163  // Flag, to skip boundary vertices
164  bool skip = false;
165 
166  // ( .. for each Outoing halfedge .. )
167  TriMesh::VertexOHalfedgeIter voh_it(*mesh,*v_it);
168  for ( ; voh_it.is_valid(); ++voh_it ) {
169  // .. add the (original) position of the Neighbour ( end of the outgoing halfedge )
170  point += mesh->property( origPositions, mesh->to_vertex_handle(*voh_it) );
171 
172  // Check if the current Halfedge is a boundary halfedge
173  // If it is, abort and keep the current vertex position
174  if ( mesh->is_boundary( *voh_it ) ) {
175  skip = true;
176  break;
177  }
178 
179  }
180 
181  // Devide by the valence of the current vertex
182  point /= mesh->valence( *v_it );
183 
184  if ( ! skip ) {
185  // Set new position for the mesh if its not on the boundary
186  mesh->point(*v_it) = point;
187  }
188  }
189 
190  }// Iterations end
191 
192  // Remove the property
193  mesh->remove_property( origPositions );
194 
195  mesh->update_normals();
196 
197  emit updatedObject( o_it->id(), UPDATE_GEOMETRY );
198 
199  // Create backup
200  emit createBackup(o_it->id(), "Simple Smoothing", UPDATE_GEOMETRY );
201 
202  } else if ( o_it->dataType( DATA_POLY_MESH ) ) {
203 
204  // Get the mesh to work on
205  PolyMesh* mesh = PluginFunctions::polyMesh(*o_it);
206 
207  // Property for the active mesh to store original point positions
209 
210  // Add a property to the mesh to store original vertex positions
211  mesh->add_property( origPositions, "SmootherPlugin_Original_Positions" );
212 
213  for ( int i = 0 ; i < _iterations ; ++i ) {
214 
215  // Copy original positions to backup ( in Vertex property )
216  PolyMesh::VertexIter v_it, v_end=mesh->vertices_end();
217  for (v_it=mesh->vertices_begin(); v_it!=v_end; ++v_it) {
218  mesh->property( origPositions, *v_it ) = mesh->point(*v_it);
219  // See if at least one vertex has been selected
220  selectionExists |= mesh->status(*v_it).selected();
221  }
222 
223  // Do one smoothing step (For each point of the mesh ... )
224  for (v_it=mesh->vertices_begin(); v_it!=v_end; ++v_it) {
225 
226  if(selectionExists && mesh->status(*v_it).selected() == false) {
227  continue;
228  }
229 
230  PolyMesh::Point point = PolyMesh::Point(0.0,0.0,0.0);
231 
232  // Flag, to skip boundary vertices
233  bool skip = false;
234 
235  // ( .. for each Outoing halfedge .. )
236  PolyMesh::VertexOHalfedgeIter voh_it(*mesh,*v_it);
237  for ( ; voh_it.is_valid(); ++voh_it ) {
238  // .. add the (original) position of the Neighbour ( end of the outgoing halfedge )
239  point += mesh->property( origPositions, mesh->to_vertex_handle(*voh_it) );
240 
241  // Check if the current Halfedge is a boundary halfedge
242  // If it is, abort and keep the current vertex position
243  if ( mesh->is_boundary( *voh_it ) ) {
244  skip = true;
245  break;
246  }
247 
248  }
249 
250  // Devide by the valence of the current vertex
251  point /= mesh->valence( *v_it );
252 
253  if ( ! skip ) {
254  // Set new position for the mesh if its not on the boundary
255  mesh->point(*v_it) = point;
256  }
257  }
258 
259  }// Iterations end
260 
261  // Remove the property
262  mesh->remove_property( origPositions );
263 
264  mesh->update_normals();
265 
266  emit updatedObject( o_it->id() , UPDATE_GEOMETRY);
267 
268  // Create backup
269  emit createBackup(o_it->id(), "Simple Smoothing", UPDATE_GEOMETRY);
270 
271  } else {
272 
273  emit log(LOGERR, "DataType not supported.");
274  }
275  }
276 
277  // Show script logging
278  emit scriptInfo("simpleLaplace(" + QString::number(_iterations) + ")");
279 
280  emit updateView();
281 }
282 
283 
284 
285 
286 
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
void simpleLaplace()
simpleLaplace
void pluginsInitialized()
Set the scripting slot descriptions.
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
QSpinBox * iterationsSpinbox_
SpinBox for Number of iterations.