Developer Documentation
Loading...
Searching...
No Matches
SubdivideWidget.cc
1/* ========================================================================= *
2 * *
3 * OpenMesh *
4 * Copyright (c) 2001-2025, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openmesh.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenMesh. *
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 SubdivideWidget - IMPLEMENTATION
47//
48//=============================================================================
49
50#ifndef SUBDIVIDEWIDGET_CC
51#define SUBDIVIDEWIDGET_CC
52
53//== INCLUDES =================================================================
54
55
56// Qt
57#include <QApplication>
58#include <QFileDialog>
59#include <QButtonGroup>
60#include <QRadioButton>
61#include <QVBoxLayout>
62#include <QHBoxLayout>
63#include <QPushButton>
64#include <QLabel>
65#include <QString>
66#include <QMessageBox>
67
68// OpenMesh
69#include <OpenMesh/Core/IO/MeshIO.hh>
70#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
78
79#include <OpenMesh/Apps/Subdivider/SubdivideWidget.hh>
80
81
82using namespace OpenMesh::Subdivider;
83
84//== IMPLEMENTATION ==========================================================
85
86
88SubdivideWidget(QWidget* _parent, const char* _name)
89 : QWidget(_parent),
90 timer_(nullptr), animate_step_(0), max_animate_steps_(4), msecs_(0)
91{
92
93 setWindowTitle( QString(_name) );
94
95 QVBoxLayout* vbox = new QVBoxLayout();
96
97 cur_topo_type = SOP_Undefined;
98 // sel_topo_type will be set when adding the radio button.;
99
100 // examiner widget
101 viewer_widget_ = new MeshViewerWidgetSubdivider();
102
103 vbox->addWidget(viewer_widget_);
104
105 QHBoxLayout* hbox = new QHBoxLayout();
106// hbox->setFixedSize(400, 30);
107 vbox->addLayout(hbox);
108
109 // insert subdivision pushbutton
110 QPushButton* subdiv_button = new QPushButton( "Subdivide");
111 subdiv_button->setMinimumWidth(50);
112 QObject::connect( subdiv_button, SIGNAL( clicked() ),
113 this, SLOT( subdiv_slot() ) );
114 hbox->addWidget(subdiv_button);
115
116 // insert load pushbutton
117 QPushButton* load_button = new QPushButton( "Load Mesh");
118 load_button->setMinimumWidth(50);
119 QObject::connect( load_button, SIGNAL( clicked() ),
120 this, SLOT( load_slot() ) );
121 hbox->addWidget(load_button);
122
123 // insert save pushbutton
124 QPushButton* save_button = new QPushButton( "Save Mesh");
125 save_button->setMinimumWidth(50);
126 QObject::connect( save_button, SIGNAL( clicked() ),
127 this, SLOT( save_slot() ) );
128 hbox->addWidget(save_button);
129
130
131 // insert reset pushbutton
132 QPushButton* reset_button = new QPushButton( "Reset");
133 reset_button->setMinimumWidth(50);
134 QObject::connect( reset_button, SIGNAL( clicked() ),
135 this, SLOT( reset_slot() ) );
136 hbox->addWidget(reset_button);
137
138
139 // Create an exclusive button group: Topology Operators
140// QButtonGroup *bgrp1 = new QButtonGroup( 1, QGroupBox::Vertical,"Subdivision Operators:");
141
142 QButtonGroup* buttonGroup = new QButtonGroup();
143
144 buttonGroup->setExclusive( true );
145
146 // insert 2 radiobuttons
147 QRadioButton* radio1 = new QRadioButton( "Comp. Loop" );
148 QRadioButton* radio2 = new QRadioButton( "Comp. SQRT(3)" );
149 QRadioButton* radio3 = new QRadioButton( "Loop" );
150 QRadioButton* radio4 = new QRadioButton( "Sqrt(3)" );
151 QRadioButton* radio5 = new QRadioButton( "Interpolating Sqrt3" );
152 QRadioButton* radio6 = new QRadioButton( "Modified Butterfly" );
153 // QRadioButton* radio7 = new QRadioButton( "Catmull Clark" ); // Disabled, as it needs a quad mesh!
154 radio3->setChecked( true );
155 sel_topo_type = SOP_UniformLoop;
156
157 buttonGroup->addButton(radio1, SOP_UniformCompositeLoop);
158 buttonGroup->addButton(radio2, SOP_UniformCompositeSqrt3);
159 buttonGroup->addButton(radio3, SOP_UniformLoop);
160 buttonGroup->addButton(radio4, SOP_UniformSqrt3);
161 buttonGroup->addButton(radio5, SOP_UniformInterpolatingSqrt3);
162 buttonGroup->addButton(radio6, SOP_ModifiedButterfly);
163 //buttonGroup->addButton(radio7, SOP_CatmullClark);
164
165 vbox->addWidget(radio1);
166 vbox->addWidget(radio2);
167 vbox->addWidget(radio3);
168 vbox->addWidget(radio4);
169 vbox->addWidget(radio5);
170 vbox->addWidget(radio6);
171 // vbox->addWidget(radio7);
172
173 QObject::connect( buttonGroup, SIGNAL( buttonPressed(int) ),
174 this, SLOT( slot_select_sop(int) ) );
175
176
177 status_bar = new QStatusBar();
178 status_bar->setFixedHeight(20);
179 status_bar->showMessage("0 Faces, 0 Edges, 0 Vertices");
180 vbox->addWidget(status_bar);
181
182
183 setLayout(vbox);
184
185
186 // animation
187 timer_ = new QTimer(this);
188 connect( timer_, SIGNAL( timeout() ), this, SLOT( animate_slot() ) );
189
190 // --------------------
191
192 subdivider_[SOP_UniformCompositeLoop] = new Uniform::CompositeLoopT<Mesh>;
193 subdivider_[SOP_UniformCompositeSqrt3] = new Uniform::CompositeSqrt3T<Mesh>;
194 subdivider_[SOP_UniformLoop] = new Uniform::LoopT<Mesh>;
195 subdivider_[SOP_UniformSqrt3] = new Uniform::Sqrt3T<Mesh>;
196 subdivider_[SOP_UniformInterpolatingSqrt3] = new Uniform::InterpolatingSqrt3LGT< Mesh >;
197 subdivider_[SOP_ModifiedButterfly] = new Uniform::ModifiedButterflyT<Mesh>;
198 subdivider_[SOP_CatmullClark] = new Uniform::CatmullClarkT<Mesh>;
199
200}
201
202
203//-----------------------------------------------------------------------------
204
205void SubdivideWidget::slot_select_sop(int i)
206{
207 switch(i)
208 {
209 case SOP_UniformCompositeLoop:
210 case SOP_UniformCompositeSqrt3:
211 case SOP_UniformLoop:
212 case SOP_UniformSqrt3:
213 case SOP_UniformInterpolatingSqrt3:
214 case SOP_ModifiedButterfly:
215 case SOP_CatmullClark: sel_topo_type = (SOPType)i; break;
216 default: sel_topo_type = SOP_Undefined;
217 }
218}
219
220
221//-----------------------------------------------------------------------------
222
223void SubdivideWidget::keyPressEvent( QKeyEvent *k )
224{
225 bool timerStopped = false;
226 if ( timer_->isActive())
227 {
228 timer_->stop();
229 timerStopped = true;
230 }
231
232 switch ( k->key() )
233 {
234 case Qt::Key_R: // reset
235 reset_slot();
236 break;
237 case Qt::Key_S: // save
238 save_slot();
239 break;
240 case Qt::Key_L: // load
241 load_slot();
242 break;
243
244 case Qt::Key_A:
245
246 if ( timerStopped )
247 break;
248
249 if (timer_->isActive())
250 {
251 timer_->stop();
252 }
253 else
254 {
255 reset_slot();
256 timer_->setSingleShot( true );
257 timer_->start(0);
258 }
259 break;
260
261 case ' ': // subdivide
262 subdiv_slot();
263 }
264}
265
266
267
268//-----------------------------------------------------------------------------
269
270
271void SubdivideWidget::update()
272{
273 size_t n_faces = viewer_widget_->mesh().n_faces();
274 size_t n_edges = viewer_widget_->mesh().n_edges();
275 size_t n_vertices = viewer_widget_->mesh().n_vertices();
276 QString message(""), temp;
277 message.append(temp.setNum(n_faces));
278 message.append(" Faces, ");
279 message.append(temp.setNum(n_edges));
280 message.append(" Edges, ");
281 message.append(temp.setNum(n_vertices));
282 message.append(" Vertices. ");
283 if (msecs_)
284 {
285 message.append(temp.setNum(msecs_/1000.0));
286 message.append("s");
287 }
288 status_bar->showMessage(message);
289}
290
291
292//-----------------------------------------------------------------------------
293
294
295void SubdivideWidget::reset_slot()
296{
297 if (cur_topo_type != SOP_Undefined)
298 subdivider_[cur_topo_type]->detach();
299
300 viewer_widget_->mesh() = viewer_widget_->orig_mesh();
301 viewer_widget_->mesh().update_face_normals();
302 viewer_widget_->mesh().update_vertex_normals();
303 viewer_widget_->updateGL();
304 update();
305 cur_topo_type = SOP_Undefined;
306}
307
308
309
310//-----------------------------------------------------------------------------
311
312
313void SubdivideWidget::subdiv_slot()
314{
315 assert( sel_topo_type != SOP_Undefined );
316
317 //QTime t;
318 using namespace OpenMesh::Subdivider::Uniform;
319
320 status_bar->showMessage( "processing subdivision step...");
321
322 if (cur_topo_type != sel_topo_type)
323 {
324 if (cur_topo_type!=SOP_Undefined)
325 subdivider_[cur_topo_type]->detach();
326 subdivider_[cur_topo_type=sel_topo_type]->attach(viewer_widget_->mesh());
327 }
328
329 std::clog << "subdiving...\n";
330 (*subdivider_[sel_topo_type])(1);
331 std::clog << "subdiving...done\n";
332
333 // Update viewer
334 viewer_widget_->mesh().update_normals();
335 viewer_widget_->updateGL();
336
337 // Update status bar information
338 update();
339}
340
341
342//-----------------------------------------------------------------------------
343
344bool
345SubdivideWidget::open_mesh(const char* _filename)
346{
348
349 if (viewer_widget_->open_mesh(_filename, opt))
350 {
351 update();
352 return true;
353 }
354
355 return false;
356}
357
358
359//-----------------------------------------------------------------------------
360
361void
362SubdivideWidget::save_slot()
363{
365
366 QString write_filter(IOManager().qt_write_filters().c_str());
367 QString filename = QFileDialog::getSaveFileName(this, "", "", write_filter);
368
369 if (!filename.isEmpty()){
370 if (OpenMesh::IO::write_mesh(viewer_widget_->mesh(), filename.toStdString(),
372 std::cerr << "ok\n";
373 else
374 std::cerr << "FAILED\n";
375 }
376}
377
378
379//-----------------------------------------------------------------------------
380
381void
382SubdivideWidget::load_slot()
383{
385
386 QString read_filter(IOManager().qt_read_filters().c_str());
387 QString filename =
388 QFileDialog::getOpenFileName(this, "", "", read_filter);
389
390 if (!filename.isNull())
391 {
392
393 if (cur_topo_type != SOP_Undefined)
394 subdivider_[cur_topo_type]->detach();
395
397 std::string file( filename.toStdString() );
398
399 if ( !viewer_widget_->open_mesh(file.c_str() , opt) )
400 {
401 QString msg = "Cannot read mesh from file ";
402 QMessageBox::critical( this,"", msg + filename, QMessageBox::Ok );
403 }
404
405 update();
406 cur_topo_type = SOP_Undefined;
407 }
408}
409
410
411//-----------------------------------------------------------------------------
412
413void
414SubdivideWidget::animate_slot()
415{
416 if (++animate_step_ < max_animate_steps_)
417 {
418 subdiv_slot();
419 }
420 else
421 {
422 reset_slot();
423 animate_step_ = 0;
424 }
425 timer_->setSingleShot(true);
426 timer_->start( 500 );
427}
428
429//=============================================================================
430#endif //SUBDIVIDEWIDGET_CC deifined
431//=============================================================================
bool open_mesh(const char *_filename, OpenMesh::IO::Options _opt) override
open mesh
Set options for reader/writer modules.
Definition Options.hh:92
@ Binary
Set binary mode for r/w.
Definition Options.hh:101
SubdivideWidget(QWidget *_parent=0, const char *_name=0)
constructor
QStatusBar * status_bar
Updates Status Bar Information.
bool open_mesh(const char *_filename)
open mesh from _filename
bool write_mesh(const Mesh &_mesh, const std::string &_filename, Options _opt=Options::Default, std::streamsize _precision=6)
Write a mesh to the file _filename.
Definition MeshIO.hh:190
_IOManager_ & IOManager()
Definition IOManager.cc:72