SMILX  1.01
milxQtAnimateModel.cpp
1 /*=========================================================================
2  The Software is copyright (c) Commonwealth Scientific and Industrial Research Organisation (CSIRO)
3  ABN 41 687 119 230.
4  All rights reserved.
5 
6  Licensed under the CSIRO BSD 3-Clause License
7  You may not use this file except in compliance with the License.
8  You may obtain a copy of the License in the file LICENSE.md or at
9 
10  https://stash.csiro.au/projects/SMILI/repos/smili/browse/license.txt
11 
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 =========================================================================*/
18 #include "milxQtAnimateModel.h"
19 
20 #include <time.h>
21 //VTK
22 #include <vtkWindowToImageFilter.h>
23 #include <vtkFFMPEGWriter.h>
24 #include <vtkCamera.h>
25 
26 milxQtAnimateModel::milxQtAnimateModel(QWidget *theParent, bool contextSystem) : milxQtModel(theParent, contextSystem)
27 {
28  m_loaded = false;
29  m_pause = true;
30  m_currentID = 0;
31  m_interval = 250;
32  m_rotationInterval = 5;
33 
34  milxQtWindow::prefix = "aniMdl: ";
35 
36  createActions();
37 
38  createConnections();
39 }
40 
41 milxQtAnimateModel::~milxQtAnimateModel()
42 {
43  //dtor
44 }
45 
46 void milxQtAnimateModel::SetInputCollection(vtkPolyDataCollection* meshes, QStringList &filenames, const int idIndex)
47 {
48  const int n = meshes->GetNumberOfItems();
49 
50  working(-1);
51  m_meshes = meshes;
52 
53  printInfo("Extracting IDs");
56  int index = 0;
57  QDialog *casePossibilities = new QDialog(this);
58  QComboBox *integerValues = new QComboBox(this);
59  QPushButton *okButton = new QPushButton(this);
60  okButton->setText("Ok");
61  QLabel *lblMessage = new QLabel(this);
62  lblMessage->setText("Choose Case ID from possibilities.");
63  QFormLayout *formLayout = new QFormLayout(this);
64  formLayout->addRow(lblMessage);
65  formLayout->addRow(tr("&Select Case ID from first file: "), integerValues);
66  formLayout->addRow(okButton);
67  casePossibilities->setLayout(formLayout);
68 
69  connect(okButton, SIGNAL(clicked(bool)), casePossibilities, SLOT(accept()));
70 
71  meshes->InitTraversal();
72  milxQtModel::SetInput(meshes->GetNextItem());
73  reset();
74  updateAnimation();
75 
76  for(int j = 0; j < n; j ++)
77  {
78  QFileInfo fi(filenames[j]);
79  QRegExp rx("(\\d+)", Qt::CaseSensitive, QRegExp::RegExp2);
80 
82  int pos = 0;
83  QStringList list;
84  while ((pos = rx.indexIn(fi.baseName(), pos)) != -1)
85  {
86  list << rx.cap(1);
87  pos += rx.matchedLength(); //Move
88  }
89 
91  if(j == 0)
92  {
93  if(list.size() > 1 && idIndex < 0)
94  {
95  printInfo("Please choose Case ID from possibilities for first file.");
96  for(int k = 0; k < list.size(); k ++)
97  integerValues->addItem(list[k]);
98 
99  casePossibilities->exec();
100 
101  index = integerValues->currentIndex();
102  }
103  else if(list.size() > 0 && idIndex < list.size() && idIndex > 0)
104  {
105  index = idIndex;
106  }
107  else if(list.size() > 0 && idIndex < list.size() && idIndex < 0)
108  {
109  index = 0;
110  }
111  else
112  {
113  printError("Index provided for expected case IDs is incorrect. Ignoring operation.");
114  m_loaded = false;
115  return;
116  }
117  }
118 
119  int caseID = 0;
120  printDebug("Frame Index to be used: " + QString::number(index));
121  if(!list.isEmpty())
122  caseID = list[index].toInt();
123 
124  m_caseIDs.append(caseID);
125 
126  qApp->processEvents();
127  }
128  done(-1);
129 
130  qDebug() << "IDs: " << m_caseIDs << endl;
131 
132  printInfo("Starting Animation Loop");
133  connect(&timer, SIGNAL(timeout()), this, SLOT(updateAnimation()));
134  timer.start(m_interval);
135 
136  m_loaded = true;
137 }
138 
140 {
141  if(!menu)
142  return;
143 
144  menu->clear();
145  foreach(QAction *currAct, milxQtModel::actionsToAdd)
146  {
147  menu->addAction(currAct);
148  }
149  foreach(QMenu *currMenu, menusToAdd)
150  {
151  menu->addMenu(currMenu);
152  }
153  menu->addSeparator()->setText("Animation");
154  menu->addAction(startAct);
155  menu->addAction(pauseAct);
156  menu->addAction(rotationAct);
157  menu->addAction(intervalAct);
158  menu->addAction(rotationIntervalAct);
159  menu->addAction(movieAct);
160  menu->addSeparator()->setText(tr("Display"));
161  menu->addAction(milxQtModel::pointsAct);
162  menu->addAction(milxQtModel::wireframeAct);
163  menu->addAction(milxQtModel::surfaceAct);
165  menu->addMenu(milxQtRenderWindow::viewMenu);
174  menu->addMenu(milxQtRenderWindow::colourMapMenu);
175  milxQtModel::showMenu = menu->addMenu("Show");
181  menu->addSeparator()->setText(tr("Properties"));
182  menu->addAction(milxQtModel::colourAct);
183  menu->addAction(milxQtModel::interpAct);
184  menu->addSeparator();
185  menu->addAction(milxQtModel::scaleAct);
187  menu->addAction(milxQtRenderWindow::refreshAct);
188  menu->addAction(milxQtRenderWindow::resetAct);
189 }
190 
191 void milxQtAnimateModel::reset()
192 {
193  m_currentID = 0;
194  m_meshes->InitTraversal();
195 }
196 
198 {
199  if(m_pause)
200  return;
201 
202  const int n = m_meshes->GetNumberOfItems();
203 
204  m_currentID ++;
205  if(m_currentID >= n) //Wrap around animation frames
206  reset();
207 
208  //camera rotation
209  vtkCamera* srcCamera = milxQtRenderWindow::GetRenderer()->GetActiveCamera();
210  if(rotationAct->isChecked())
211  srcCamera->Azimuth(m_rotationInterval);
212 
213  printDebug("Animating frame " + QString::number(m_currentID) + " of " + QString::number(n));
214  milxQtModel::setName("ID " + QString::number(m_caseIDs[m_currentID]));
215 
216  vtkSmartPointer<vtkPolyData> mesh = m_meshes->GetNextItem();
217  milxQtModel::SetInput(mesh);
219  qApp->processEvents();
220 }
221 
222 void milxQtAnimateModel::startAnimation()
223 {
224  m_pause = false;
225  reset();
226  updateAnimation();
227 }
228 
229 void milxQtAnimateModel::pauseAnimation()
230 {
231  if(!m_pause)
232  m_pause = true;
233  else
234  m_pause = false;
235 
236  updateAnimation();
237 }
238 
239 void milxQtAnimateModel::interval(int newInterval)
240 {
241  bool ok = false;
242 
243  if(newInterval == 0)
244  {
245  newInterval = QInputDialog::getInt(this, tr("Please Provide the delay between frames"),
246  tr("Interval:"), m_interval, 0, 1000000, 1, &ok);
247  }
248  else
249  ok = true;
250 
251  if(ok && newInterval > 0)
252  {
253  m_interval = newInterval;
254  timer.start(m_interval);
255  }
256 }
257 
258 void milxQtAnimateModel::intervalRotation(int newInterval)
259 {
260  bool ok = false;
261 
262  if(newInterval == 0)
263  {
264  newInterval = QInputDialog::getDouble(this, tr("Please Provide the angle increment"),
265  tr("Angle:"), 5, 0, 360, 2, &ok);
266  }
267  else
268  ok = true;
269 
270  if(ok && newInterval > 0)
271  m_rotationInterval = newInterval;
272 }
273 
274 void milxQtAnimateModel::movie(QString filename, int frames)
275 {
276  m_pause = true; //Force pause of animation
277  timer.stop(); //Pause current animation
278 
279  if(filename.isEmpty())
280  {
281  QSettings settings("Shekhar Chandra", "milxQt");
282  QString path = settings.value("recentPath").toString();
283  QFileDialog *fileSaver = new QFileDialog(this);
284  filename = fileSaver->getSaveFileName(this,
285  tr("Select File Name to Save"),
286  path,
287  tr("Movie Files (*.avi)"));
288  }
289 
290  if(!filename.isEmpty())
291  {
292  const int n = m_meshes->GetNumberOfItems();
293  const int frameRate = 1000.0/m_interval;
294  bool ok = false;
295 
296  if(frames == 0)
297  {
298  frames = static_cast<int>( QInputDialog::getInt(this, tr("Please Provide the total frames to write"),
299  tr("Frames:"), n, 0, 8192, 1, &ok) );
300 
301  if(!ok)
302  return;
303  }
304 
305  milxQtRenderWindow::OffScreenRenderingOn(); //Ensure no UI stuff interfers
306 
307  vtkWindowToImageFilter *windowToImage = vtkWindowToImageFilter::New(); //Manually deleted deliberately
308  windowToImage->SetInput(milxQtRenderWindow::GetRenderWindow());
309  linkProgressEventOf(windowToImage);
310 
311  vtkSmartPointer<vtkFFMPEGWriter> writer = vtkSmartPointer<vtkFFMPEGWriter>::New();
312  writer->SetFileName(filename.toStdString().c_str());
313  writer->SetInputConnection(windowToImage->GetOutputPort());
314  writer->SetRate(frameRate);
315  linkProgressEventOf(writer);
316  writer->Start();
317 
318  //camera rotation
319  vtkCamera* srcCamera = milxQtRenderWindow::GetRenderer()->GetActiveCamera();
320 
321  printDebug("Movie Write Begin");
322  for(int j = 0; j < frames; j ++)
323  {
324  printDebug("Loading Frame " + QString::number(j));
325  if( (j % n) == 0)
326  reset(); //cycle through collection if frames > n
327 
328  vtkPolyData *mesh = m_meshes->GetNextItem();
329 
330  milxQtModel::setName("ID " + QString::number(m_caseIDs[j % n]));
331  milxQtModel::SetInput(mesh);
332  milxQtModel::refresh(); //update display
333  qApp->processEvents();
334 
335  windowToImage->Update();
336 
337  printDebug("Writing Frame " + QString::number(j));
338  writer->SetInputConnection(windowToImage->GetOutputPort()); //Force update of pointer
339  writer->Write();
340 
341  printDebug("Prep next Frame ");
342  windowToImage->Delete();
343  windowToImage = vtkWindowToImageFilter::New();
344  windowToImage->SetInput(milxQtRenderWindow::GetRenderWindow());
345  linkProgressEventOf(windowToImage);
346  windowToImage->Update();
347 
348  if(rotationAct->isChecked())
349  srcCamera->Azimuth(m_rotationInterval);
350  printDebug("Finish Loop");
351  }
352  printDebug("Movie Written Successfully");
353  writer->End();
354  printDebug("Movie File Closed Sucessfully");
355  windowToImage->Delete();
356 
358  refresh();
359 
360  printInfo("Movie Operation Completed");
361  }
362 
363  timer.start(m_interval); //Restart timer
364 }
365 
366 void milxQtAnimateModel::createActions()
367 {
368  contextMenu = new QMenu(this);
369  contextMenu->setTitle(QApplication::translate("MainWindow", "Animation", 0, QApplication::UnicodeUTF8));
370 
371  startAct = new QAction(this);
372  startAct->setText(QApplication::translate("Model", "Start/Restart", 0, QApplication::UnicodeUTF8));
373  startAct->setShortcut(tr("Alt+s"));
374  pauseAct = new QAction(this);
375  pauseAct->setText(QApplication::translate("Model", "Pause/Unpause", 0, QApplication::UnicodeUTF8));
376  pauseAct->setShortcut(tr("Alt+p"));
377  intervalAct = new QAction(this);
378  intervalAct->setText(QApplication::translate("Model", "Change Interval", 0, QApplication::UnicodeUTF8));
379  intervalAct->setShortcut(tr("Alt+i"));
380  rotationIntervalAct = new QAction(this);
381  rotationIntervalAct->setText(QApplication::translate("Model", "Change Rotation Interval", 0, QApplication::UnicodeUTF8));
382  rotationIntervalAct->setShortcut(tr("Shift+Alt+r"));
383  rotationAct = new QAction(this);
384  rotationAct->setText(QApplication::translate("Model", "Rotate View", 0, QApplication::UnicodeUTF8));
385  rotationAct->setShortcut(tr("Alt+r"));
386  rotationAct->setCheckable(true);
387  rotationAct->setChecked(false);
388  movieAct = new QAction(this);
389  movieAct->setText(QApplication::translate("Model", "Record as Movie", 0, QApplication::UnicodeUTF8));
390  movieAct->setShortcut(tr("Alt+m"));
391 }
392 
393 void milxQtAnimateModel::createConnections()
394 {
395  //Operations
396  connect(startAct, SIGNAL(triggered()), this, SLOT(startAnimation()));
397  connect(pauseAct, SIGNAL(triggered()), this, SLOT(pauseAnimation()));
398  connect(intervalAct, SIGNAL(triggered()), this, SLOT(interval()));
399  connect(rotationIntervalAct, SIGNAL(triggered()), this, SLOT(intervalRotation()));
400  connect(movieAct, SIGNAL(triggered()), this, SLOT(movie()));
401 
403 }
404 
405 void milxQtAnimateModel::contextMenuEvent(QContextMenuEvent *currentEvent)
406 {
407  createMenu(contextMenu);
408 
409  contextMenu->exec(currentEvent->globalPos());
410 }
QAction * scaleAct
Action for the scale bar of the display.
QAction * normalsAct
Action for showing the normals of a model.
Definition: milxQtModel.h:1047
QAction * outlineAct
show outline action
Definition: milxQtModel.h:1049
QAction * saveViewFileAct
Save camera view to file.
QMenu * viewMenu
Context Menu.
void setName(const QString filename)
Set the name of the data.
QAction * pointsAct
Show points.
Definition: milxQtModel.h:1073
QAction * viewZY
Change view to zy-plane (Coronal)
void createConnections()
Create the connections for context menu etc.
QAction * surfaceAct
Show surface.
Definition: milxQtModel.h:1075
void SetInputCollection(vtkPolyDataCollection *meshes, QStringList &filenames, const int idIndex=-1)
Animate the collection provided.
QAction * wireframeAct
Show wireframe.
Definition: milxQtModel.h:1074
void SetInput(vtkSmartPointer< vtkPolyData > mesh)
Assigns the mesh provided to the class, preparing for display. Call generateModel() and then show() t...
QString prefix
Prefix of the data.
Definition: milxQtWindow.h:242
QAction * centroidAct
show centroid action
Definition: milxQtModel.h:1048
This class represents the MILX Qt Model/Mesh Display object using VTK.
Definition: milxQtModel.h:115
void OffScreenRenderingOn()
Enable off-screen rendering, which is faster for large datasets in some instances.
void OffScreenRenderingOff()
Disable off-screen rendering, which is faster for large datasets in some instances.
void refresh()
Refresh the display of the model.
QAction * colourAct
Action for changing colours of a model.
Definition: milxQtModel.h:1066
QAction * cubeAxesAct
show cube axes action
Definition: milxQtModel.h:1050
QAction * refreshAct
Action for refreshing the display.
void enableActionBasedOnView()
Enables the view actions corresponding to current view set.
QMenu * arraysMenu()
Return the arrays menu with the milxQtModel class ordering. This is for the benefit of derived class ...
QAction * resetAct
Action for refreshing the display.
QMenu * colourMapMenu
Colour map menu.
QAction * saveViewAct
Save camera view.
QAction * viewXY
Change view to xy-plane (Axial)
QList< QAction * > actionsToAdd
Context actions to add.
Definition: milxQtWindow.h:251
void movie(QString filename="", int frames=0)
QAction * viewZX
Change view to zx-plane (Sagittal)
QMenu * showMenu
Camera Menu.
Definition: milxQtModel.h:1046
virtual void createMenu(QMenu *menu)
Create the menu for the data in this object. Used for context menu and file menus.
QAction * interpAct
Action for changing the interpolation of a model.
Definition: milxQtModel.h:1067
QMenu * windowPropertiesMenu
Context Menu.
vtkRenderer * GetRenderer()
Returns the VTK Renderer object.
QAction * loadViewAct
Load camera view.
QAction * loadViewFileAct
Load camera view to file.