diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItem.h b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItem.h index 47ae1fd79b..9e7fcec130 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItem.h +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItem.h @@ -1,54 +1,57 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkAnimationItem_h #define QmitkAnimationItem_h #include class QmitkAnimationItem : public QStandardItem { public: enum AnimationItemDataRole { WidgetKeyRole = Qt::UserRole + 2, DurationRole, DelayRole, StartWithPreviousRole, RenderWindowRole, FromRole, ToRole, ReverseRole, - AngleRole + StartAngleRole, + OrbitRole }; explicit QmitkAnimationItem(const QString& widgetKey, double duration = 2.0, double delay = 0.0, bool startWithPrevious = false); virtual ~QmitkAnimationItem(); QString GetWidgetKey() const; void SetWidgetKey(const QString& widgetKey); double GetDuration() const; void SetDuration(double duration); double GetDelay() const; void SetDelay(double delay); bool GetStartWithPrevious() const; void SetStartWithPrevious(bool startWithPrevious); + + virtual void Animate(double s) = 0; }; #endif diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItemDelegate.cpp b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItemDelegate.cpp index ce038cf4ec..781aeb37af 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItemDelegate.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkAnimationItemDelegate.cpp @@ -1,123 +1,123 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkAnimationItem.h" #include "QmitkAnimationItemDelegate.h" #include #include #include #include QmitkAnimationItemDelegate::QmitkAnimationItemDelegate(QObject* parent) : QStyledItemDelegate(parent) { } QmitkAnimationItemDelegate::~QmitkAnimationItemDelegate() { } void QmitkAnimationItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { painter->save(); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); if (index.column() == 0) { QStyleOptionViewItem opt = option; this->initStyleOption(&opt, index); QRect rect = QRect(opt.rect.x() + 3, opt.rect.y(), opt.rect.width() - 6, opt.rect.height()); QString text = option.fontMetrics.elidedText(index.data().toString(), Qt::ElideRight, rect.width()); QPalette::ColorRole colorRole = (option.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text; QApplication::style()->drawItemText(painter, rect, 0, option.palette, true, text, colorRole); } else if (index.column() == 1) { const QStandardItemModel* model = dynamic_cast(index.model()); const QmitkAnimationItem* thisItem = dynamic_cast(model->item(index.row(), 1)); QList items; const int rowCount = model->rowCount(); for (int i = 0; i < rowCount; ++i) items << dynamic_cast(model->item(i, 1)); double totalDuration = 0.0; double previousStart = 0.0; double thisStart = 0.0; Q_FOREACH(const QmitkAnimationItem* item, items) { if (item->GetStartWithPrevious()) { totalDuration = std::max(totalDuration, previousStart + item->GetDelay() + item->GetDuration()); } else { previousStart = totalDuration; totalDuration += item->GetDelay() + item->GetDuration(); } if (item == thisItem) thisStart = previousStart; } + const QRect& rect = option.rect; + const double widthPerSecond = rect.width() / totalDuration; + QColor color = thisItem->GetStartWithPrevious() ? QColor("DarkGray") : QColor("DimGray"); painter->setBrush(color); painter->setPen(Qt::NoPen); - const QRect& rect = option.rect; - const double widthPerSecond = rect.width() / totalDuration; - painter->drawRect( rect.x() + static_cast(widthPerSecond * (thisStart + thisItem->GetDelay())), rect.y() + 1, static_cast(widthPerSecond * thisItem->GetDuration()), rect.height() - 2); if (thisItem->GetDelay() > std::numeric_limits::min()) { QPen pen(color); painter->setPen(pen); painter->drawLine( rect.x() + static_cast(widthPerSecond * thisStart), rect.y() + 1, rect.x() + static_cast(widthPerSecond * thisStart), rect.y() + rect.height() - 2); pen.setStyle(Qt::DashLine); painter->setPen(pen); painter->drawLine( rect.x() + static_cast(widthPerSecond * thisStart), rect.y() + rect.height() / 2, rect.x() + static_cast(widthPerSecond * (thisStart + thisItem->GetDelay())) - 1, rect.y() + rect.height() / 2); } } painter->restore(); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.cpp b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.cpp index 84f5ac92c1..6a4c4c911a 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.cpp @@ -1,508 +1,604 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkAnimationItemDelegate.h" #include "QmitkFFmpegWriter.h" #include "QmitkMovieMaker2View.h" #include "QmitkOrbitAnimationItem.h" #include "QmitkOrbitAnimationWidget.h" #include "QmitkSliceAnimationItem.h" #include "QmitkSliceAnimationWidget.h" #include #include #include #include #include #include #include static QmitkAnimationItem* CreateDefaultAnimation(const QString& widgetKey) { if (widgetKey == "Orbit") - return new QmitkOrbitAnimationItem(360.0, false); + return new QmitkOrbitAnimationItem; if (widgetKey == "Slice") return new QmitkSliceAnimationItem(0, 0, 999, false); return NULL; } static QString GetFFmpegPath() { berry::IPreferencesService::Pointer preferencesService = berry::Platform::GetServiceRegistry().GetServiceById(berry::IPreferencesService::ID); berry::IPreferences::Pointer preferences = preferencesService->GetSystemPreferences()->Node("/org.mitk.gui.qt.ext.externalprograms"); return QString::fromStdString(preferences->Get("ffmpeg", "")); } static unsigned char* ReadPixels(vtkRenderWindow* renderWindow, int x, int y, int width, int height) { if (renderWindow == NULL) return NULL; unsigned char* frame = new unsigned char[width * height * 3]; renderWindow->MakeCurrent(); glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, frame); return frame; } const std::string QmitkMovieMaker2View::VIEW_ID = "org.mitk.views.moviemaker2"; QmitkMovieMaker2View::QmitkMovieMaker2View() : m_FFmpegWriter(NULL), m_Ui(new Ui::QmitkMovieMaker2View), m_AnimationModel(NULL), m_AddAnimationMenu(NULL), m_RecordMenu(NULL) { } QmitkMovieMaker2View::~QmitkMovieMaker2View() { } void QmitkMovieMaker2View::CreateQtPartControl(QWidget* parent) { m_FFmpegWriter = new QmitkFFmpegWriter(parent); m_Ui->setupUi(parent); this->InitializeAnimationWidgets(); this->InitializeAnimationTreeViewWidgets(); this->InitializePlaybackAndRecordWidgets(); m_Ui->animationWidgetGroupBox->setVisible(false); } void QmitkMovieMaker2View::InitializeAnimationWidgets() { m_AnimationWidgets["Orbit"] = new QmitkOrbitAnimationWidget; m_AnimationWidgets["Slice"] = new QmitkSliceAnimationWidget; Q_FOREACH(QWidget* widget, m_AnimationWidgets.values()) { if (widget != NULL) { widget->setVisible(false); m_Ui->animationWidgetGroupBoxLayout->addWidget(widget); } } this->ConnectAnimationWidgets(); } void QmitkMovieMaker2View::InitializeAnimationTreeViewWidgets() { this->InitializeAnimationModel(); this->InitializeAddAnimationMenu(); this->ConnectAnimationTreeViewWidgets(); } void QmitkMovieMaker2View::InitializePlaybackAndRecordWidgets() { this->InitializeRecordMenu(); this->ConnectPlaybackAndRecordWidgets(); } void QmitkMovieMaker2View::InitializeAnimationModel() { m_AnimationModel = new QStandardItemModel(m_Ui->animationTreeView); m_AnimationModel->setHorizontalHeaderLabels(QStringList() << "Animation" << "Timeline"); m_Ui->animationTreeView->setModel(m_AnimationModel); m_Ui->animationTreeView->setItemDelegate(new QmitkAnimationItemDelegate(m_Ui->animationTreeView)); } void QmitkMovieMaker2View::InitializeAddAnimationMenu() { m_AddAnimationMenu = new QMenu(m_Ui->addAnimationButton); Q_FOREACH(const QString& key, m_AnimationWidgets.keys()) { m_AddAnimationMenu->addAction(key); } } void QmitkMovieMaker2View::InitializeRecordMenu() { typedef QPair PairOfStrings; m_RecordMenu = new QMenu(m_Ui->recordButton); QVector renderWindows; renderWindows.push_back(qMakePair(QString("Axial"), QString("stdmulti.widget1"))); renderWindows.push_back(qMakePair(QString("Sagittal"), QString("stdmulti.widget2"))); renderWindows.push_back(qMakePair(QString("Coronal"), QString("stdmulti.widget3"))); renderWindows.push_back(qMakePair(QString("3D"), QString("stdmulti.widget4"))); Q_FOREACH(const PairOfStrings& renderWindow, renderWindows) { QAction* action = new QAction(m_RecordMenu); action->setText(renderWindow.first); action->setData(renderWindow.second); m_RecordMenu->addAction(action); } } void QmitkMovieMaker2View::ConnectAnimationTreeViewWidgets() { this->connect(m_AnimationModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this, SLOT(OnAnimationTreeViewRowsInserted(const QModelIndex&, int, int))); this->connect(m_AnimationModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(OnAnimationTreeViewRowsRemoved(const QModelIndex&, int, int))); this->connect(m_Ui->animationTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(OnAnimationTreeViewSelectionChanged(const QItemSelection&, const QItemSelection&))); this->connect(m_Ui->moveAnimationUpButton, SIGNAL(clicked()), this, SLOT(OnMoveAnimationUpButtonClicked())); this->connect(m_Ui->moveAnimationDownButton, SIGNAL(clicked()), this, SLOT(OnMoveAnimationDownButtonClicked())); this->connect(m_Ui->addAnimationButton, SIGNAL(clicked()), this, SLOT(OnAddAnimationButtonClicked())); this->connect(m_Ui->removeAnimationButton, SIGNAL(clicked()), this, SLOT(OnRemoveAnimationButtonClicked())); } void QmitkMovieMaker2View::ConnectAnimationWidgets() { this->connect(m_Ui->startComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnStartComboBoxCurrentIndexChanged(int))); this->connect(m_Ui->durationSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnDurationSpinBoxValueChanged(double))); this->connect(m_Ui->delaySpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnDelaySpinBoxValueChanged(double))); } void QmitkMovieMaker2View::ConnectPlaybackAndRecordWidgets() { this->connect(m_Ui->playButton, SIGNAL(toggled(bool)), this, SLOT(OnPlayButtonToggled(bool))); this->connect(m_Ui->recordButton, SIGNAL(clicked()), this, SLOT(OnRecordButtonClicked())); } void QmitkMovieMaker2View::SetFocus() { m_Ui->addAnimationButton->setFocus(); } void QmitkMovieMaker2View::OnMoveAnimationUpButtonClicked() { const QItemSelection selection = m_Ui->animationTreeView->selectionModel()->selection(); if (!selection.isEmpty()) { const int selectedRow = selection[0].top(); if (selectedRow > 0) m_AnimationModel->insertRow(selectedRow - 1, m_AnimationModel->takeRow(selectedRow)); } } void QmitkMovieMaker2View::OnMoveAnimationDownButtonClicked() { const QItemSelection selection = m_Ui->animationTreeView->selectionModel()->selection(); if (!selection.isEmpty()) { const int rowCount = m_AnimationModel->rowCount(); const int selectedRow = selection[0].top(); if (selectedRow < rowCount - 1) m_AnimationModel->insertRow(selectedRow + 1, m_AnimationModel->takeRow(selectedRow)); } } void QmitkMovieMaker2View::OnAddAnimationButtonClicked() { QAction* action = m_AddAnimationMenu->exec(QCursor::pos()); if (action != NULL) { const QString widgetKey = action->text(); m_AnimationModel->appendRow(QList() << new QStandardItem(widgetKey) << CreateDefaultAnimation(widgetKey)); m_Ui->playbackAndRecordingGroupBox->setEnabled(true); } } -void QmitkMovieMaker2View::OnPlayButtonToggled(bool /*checked*/) +void QmitkMovieMaker2View::OnPlayButtonToggled(bool checked) // TODO: Refactor { - vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"); + typedef QPair AnimationIterpolationFactorPair; + if (checked) + { + m_Ui->playButton->setIcon(QIcon(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-pause.svg")); + m_Ui->playButton->repaint(); + + const double totalDuration = this->CalculateTotalDuration(); // TODO totalDuration == 0 + const int numFrames = static_cast(totalDuration * m_Ui->fpsSpinBox->value()); // TODO numFrames < 2 + const double deltaT = totalDuration / (numFrames - 1); + + for (int i = 0; i < numFrames; ++i) + { + QVector activeAnimations = this->GetActiveAnimations(i * deltaT); + + Q_FOREACH(const AnimationIterpolationFactorPair& animation, activeAnimations) + { + animation.first->Animate(animation.second); + } + + mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); + } + } + else + { + m_Ui->playButton->setIcon(QIcon(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg")); + m_Ui->playButton->repaint(); + } + + /*vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"); mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetCameraRotationController()->GetSlice(); unsigned int startPos = stepper->GetPos(); for (unsigned int i = 1; i < 30; ++i) { unsigned int newPos = startPos + 360.0 / 29.0 * i; if (newPos > 360) newPos -= 360; stepper->SetPos(newPos); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(renderWindow); - } + }*/ } void QmitkMovieMaker2View::OnRecordButtonClicked() // TODO: Refactor { m_FFmpegWriter->SetFFmpegPath(GetFFmpegPath()); if (m_FFmpegWriter->GetFFmpegPath().isEmpty()) { QMessageBox::critical(NULL, "Movie Maker 2", "Path to FFmpeg executable is not set in preferences!"); return; } QAction* action = m_RecordMenu->exec(QCursor::pos()); if (action == NULL) return; vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName(action->data().toString().toStdString()); if (renderWindow == NULL) return; const int border = 3; const int x = border; const int y = border; int width = renderWindow->GetSize()[0] - border * 2; int height = renderWindow->GetSize()[1] - border * 2; if (width & 1) --width; if (height & 1) --height; if (width < 16 || height < 16) return; m_FFmpegWriter->SetSize(width, height); m_FFmpegWriter->SetFramerate(m_Ui->fpsSpinBox->value()); QString saveFileName = QFileDialog::getSaveFileName(NULL, "Specify a filename", "", "Movie (*.mp4)"); if (saveFileName.isEmpty()) return; if(!saveFileName.endsWith(".mp4")) saveFileName += ".mp4"; m_FFmpegWriter->SetOutputPath(saveFileName); try { m_FFmpegWriter->Start(); // TODO: Play animation for (int i = 0; i < 30; ++i) { renderWindow->MakeCurrent(); unsigned char* frame = ReadPixels(renderWindow, x, y, width, height); m_FFmpegWriter->WriteFrame(frame); delete[] frame; mitk::BaseRenderer::GetInstance(renderWindow)->GetCameraRotationController()->GetSlice()->Next(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(renderWindow); } m_FFmpegWriter->Stop(); } catch (const mitk::Exception& exception) { QMessageBox::critical(NULL, "Movie Maker 2", exception.GetDescription()); } } void QmitkMovieMaker2View::OnRemoveAnimationButtonClicked() { const QItemSelection selection = m_Ui->animationTreeView->selectionModel()->selection(); if (!selection.isEmpty()) m_AnimationModel->removeRow(selection[0].top()); } void QmitkMovieMaker2View::OnAnimationTreeViewRowsInserted(const QModelIndex& parent, int start, int) { m_Ui->animationTreeView->selectionModel()->select( m_AnimationModel->index(start, 0, parent), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void QmitkMovieMaker2View::OnAnimationTreeViewRowsRemoved(const QModelIndex&, int, int) { this->UpdateWidgets(); } void QmitkMovieMaker2View::OnAnimationTreeViewSelectionChanged(const QItemSelection&, const QItemSelection&) { this->UpdateWidgets(); } void QmitkMovieMaker2View::OnStartComboBoxCurrentIndexChanged(int index) { QmitkAnimationItem* item = this->GetSelectedAnimationItem(); if (item != NULL) { item->SetStartWithPrevious(index); this->RedrawTimeline(); } } void QmitkMovieMaker2View::OnDurationSpinBoxValueChanged(double value) { QmitkAnimationItem* item = this->GetSelectedAnimationItem(); if (item != NULL) { item->SetDuration(value); this->RedrawTimeline(); } } void QmitkMovieMaker2View::OnDelaySpinBoxValueChanged(double value) { QmitkAnimationItem* item = this->GetSelectedAnimationItem(); if (item != NULL) { item->SetDelay(value); this->RedrawTimeline(); } } void QmitkMovieMaker2View::UpdateWidgets() { const QItemSelection selection = m_Ui->animationTreeView->selectionModel()->selection(); if (selection.isEmpty()) { m_Ui->moveAnimationUpButton->setEnabled(false); m_Ui->moveAnimationDownButton->setEnabled(false); m_Ui->removeAnimationButton->setEnabled(false); m_Ui->playbackAndRecordingGroupBox->setEnabled(false); this->HideCurrentAnimationWidget(); } else { const int rowCount = m_AnimationModel->rowCount(); const int selectedRow = selection[0].top(); m_Ui->moveAnimationUpButton->setEnabled(rowCount > 1 && selectedRow != 0); m_Ui->moveAnimationDownButton->setEnabled(rowCount > 1 && selectedRow < rowCount - 1); m_Ui->removeAnimationButton->setEnabled(true); m_Ui->playbackAndRecordingGroupBox->setEnabled(true); this->ShowAnimationWidget(dynamic_cast(m_AnimationModel->item(selectedRow, 1))); } this->UpdateAnimationWidgets(); } void QmitkMovieMaker2View::UpdateAnimationWidgets() { QmitkAnimationItem* item = this->GetSelectedAnimationItem(); if (item != NULL) { m_Ui->startComboBox->setCurrentIndex(item->GetStartWithPrevious()); m_Ui->durationSpinBox->setValue(item->GetDuration()); m_Ui->delaySpinBox->setValue(item->GetDelay()); m_Ui->animationGroupBox->setEnabled(true); } else { m_Ui->animationGroupBox->setEnabled(false); } } void QmitkMovieMaker2View::HideCurrentAnimationWidget() { if (m_Ui->animationWidgetGroupBox->isVisible()) { m_Ui->animationWidgetGroupBox->setVisible(false); int numWidgets = m_Ui->animationWidgetGroupBoxLayout->count(); for (int i = 0; i < numWidgets; ++i) m_Ui->animationWidgetGroupBoxLayout->itemAt(i)->widget()->setVisible(false); } } void QmitkMovieMaker2View::ShowAnimationWidget(QmitkAnimationItem* animationItem) { this->HideCurrentAnimationWidget(); if (animationItem == NULL) return; const QString widgetKey = animationItem->GetWidgetKey(); QmitkAnimationWidget* animationWidget = NULL; if (m_AnimationWidgets.contains(widgetKey)) { animationWidget = m_AnimationWidgets[widgetKey]; if (animationWidget != NULL) { m_Ui->animationWidgetGroupBox->setTitle(widgetKey); animationWidget->SetAnimationItem(animationItem); animationWidget->setVisible(true); } } m_Ui->animationWidgetGroupBox->setVisible(animationWidget != NULL); } void QmitkMovieMaker2View::RedrawTimeline() { if (m_AnimationModel->rowCount() > 1) { m_Ui->animationTreeView->dataChanged( m_AnimationModel->index(0, 1), m_AnimationModel->index(m_AnimationModel->rowCount() - 1, 1)); } } QmitkAnimationItem* QmitkMovieMaker2View::GetSelectedAnimationItem() const { const QItemSelection selection = m_Ui->animationTreeView->selectionModel()->selection(); return !selection.isEmpty() ? dynamic_cast(m_AnimationModel->item(selection[0].top(), 1)) : NULL; } + +double QmitkMovieMaker2View::CalculateTotalDuration() const +{ + const int rowCount = m_AnimationModel->rowCount(); + + double totalDuration = 0.0; + double previousStart = 0.0; + + for (int i = 0; i < rowCount; ++i) + { + QmitkAnimationItem* item = dynamic_cast(m_AnimationModel->item(i, 1)); + + if (item == NULL) + continue; + + if (item->GetStartWithPrevious()) + { + totalDuration = std::max(totalDuration, previousStart + item->GetDelay() + item->GetDuration()); + } + else + { + previousStart = totalDuration; + totalDuration += item->GetDelay() + item->GetDuration(); + } + } + + return totalDuration; +} + +QVector > QmitkMovieMaker2View::GetActiveAnimations(double t) const +{ + const int rowCount = m_AnimationModel->rowCount(); + + QVector > activeAnimations; + + double totalDuration = 0.0; + double previousStart = 0.0; + + for (int i = 0; i < rowCount; ++i) + { + QmitkAnimationItem* item = dynamic_cast(m_AnimationModel->item(i, 1)); + + if (item == NULL) + continue; + + if (item->GetDuration() > 0.0) + { + double start = item->GetStartWithPrevious() + ? previousStart + item->GetDelay() + : totalDuration + item->GetDelay(); + + if (start <= t && t <= start + item->GetDuration()) + activeAnimations.push_back(qMakePair(item, (t - start) / item->GetDuration())); + } + + if (item->GetStartWithPrevious()) + { + totalDuration = std::max(totalDuration, previousStart + item->GetDelay() + item->GetDuration()); + } + else + { + previousStart = totalDuration; + totalDuration += item->GetDelay() + item->GetDuration(); + } + } + + return activeAnimations; +} diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.h b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.h index 0eb93b23ec..4a368dc2ee 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.h +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkMovieMaker2View.h @@ -1,85 +1,87 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkMovieMaker2View_h #define QmitkMovieMaker2View_h #include class QmitkAnimationItem; class QmitkAnimationWidget; class QmitkFFmpegWriter; class QMenu; class QStandardItemModel; namespace Ui { class QmitkMovieMaker2View; } class QmitkMovieMaker2View : public QmitkAbstractView { Q_OBJECT public: static const std::string VIEW_ID; QmitkMovieMaker2View(); ~QmitkMovieMaker2View(); void CreateQtPartControl(QWidget* parent); void SetFocus(); private slots: void OnMoveAnimationUpButtonClicked(); void OnMoveAnimationDownButtonClicked(); void OnAddAnimationButtonClicked(); void OnRemoveAnimationButtonClicked(); void OnAnimationTreeViewRowsInserted(const QModelIndex& parent, int start, int end); void OnAnimationTreeViewRowsRemoved(const QModelIndex& parent, int start, int end); void OnAnimationTreeViewSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void OnStartComboBoxCurrentIndexChanged(int index); void OnDurationSpinBoxValueChanged(double value); void OnDelaySpinBoxValueChanged(double value); void OnPlayButtonToggled(bool checked); void OnRecordButtonClicked(); private: void InitializeAnimationWidgets(); void InitializeAnimationTreeViewWidgets(); void InitializeAnimationModel(); void InitializeAddAnimationMenu(); void InitializePlaybackAndRecordWidgets(); void InitializeRecordMenu(); void ConnectAnimationTreeViewWidgets(); void ConnectAnimationWidgets(); void ConnectPlaybackAndRecordWidgets(); void UpdateWidgets(); void UpdateAnimationWidgets(); void HideCurrentAnimationWidget(); void ShowAnimationWidget(QmitkAnimationItem* animationItem); void RedrawTimeline(); + double CalculateTotalDuration() const; QmitkAnimationItem* GetSelectedAnimationItem() const; + QVector > QmitkMovieMaker2View::GetActiveAnimations(double t) const; QmitkFFmpegWriter* m_FFmpegWriter; Ui::QmitkMovieMaker2View* m_Ui; QStandardItemModel* m_AnimationModel; QMap m_AnimationWidgets; QMenu* m_AddAnimationMenu; QMenu* m_RecordMenu; }; #endif diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.cpp b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.cpp index c063526cb5..314d9fb57e 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.cpp @@ -1,48 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkOrbitAnimationItem.h" +#include -QmitkOrbitAnimationItem::QmitkOrbitAnimationItem(double angle, bool reverse, double duration, double delay, bool startWithPrevious) +QmitkOrbitAnimationItem::QmitkOrbitAnimationItem(int startAngle, int orbit, bool reverse, double duration, double delay, bool startWithPrevious) : QmitkAnimationItem("Orbit", duration, delay, startWithPrevious) { - this->SetAngle(angle); + this->SetStartAngle(startAngle); + this->SetOrbit(orbit); this->SetReverse(reverse); } QmitkOrbitAnimationItem::~QmitkOrbitAnimationItem() { } -int QmitkOrbitAnimationItem::GetAngle() const +int QmitkOrbitAnimationItem::GetStartAngle() const { - return this->data(AngleRole).toInt(); + return this->data(StartAngleRole).toInt(); } -void QmitkOrbitAnimationItem::SetAngle(int angle) +void QmitkOrbitAnimationItem::SetStartAngle(int angle) { - this->setData(angle, AngleRole); + this->setData(angle, StartAngleRole); +} + +int QmitkOrbitAnimationItem::GetOrbit() const +{ + return this->data(OrbitRole).toInt(); +} + +void QmitkOrbitAnimationItem::SetOrbit(int angle) +{ + this->setData(angle, OrbitRole); } bool QmitkOrbitAnimationItem::GetReverse() const { return this->data(ReverseRole).toBool(); } void QmitkOrbitAnimationItem::SetReverse(bool reverse) { this->setData(reverse, ReverseRole); } + +void QmitkOrbitAnimationItem::Animate(double s) +{ + vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"); + mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetCameraRotationController()->GetSlice(); + + int newPos = this->GetReverse() + ? this->GetStartAngle() - this->GetOrbit() * s + : this->GetStartAngle() + this->GetOrbit() * s; + + while (newPos < 0) + newPos += 360; + + while (newPos > 360) + newPos -= 360; + + stepper->SetPos(static_cast(newPos)); +} diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.h b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.h index c37a63afcb..4198afba73 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.h +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationItem.h @@ -1,35 +1,40 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkOrbitAnimationItem_h #define QmitkOrbitAnimationItem_h #include "QmitkAnimationItem.h" class QmitkOrbitAnimationItem : public QmitkAnimationItem { public: - QmitkOrbitAnimationItem(double angle, bool reverse, double duration = 2.0, double delay = 0.0, bool startWithPrevious = false); + explicit QmitkOrbitAnimationItem(int startAngle = 180, int orbit = 360, bool reverse = false, double duration = 2.0, double delay = 0.0, bool startWithPrevious = false); virtual ~QmitkOrbitAnimationItem(); - int GetAngle() const; - void SetAngle(int angle); + int GetStartAngle() const; + void SetStartAngle(int angle); + + int GetOrbit() const; + void SetOrbit(int angle); bool GetReverse() const; void SetReverse(bool reverse); + + void Animate(double s); }; #endif diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.cpp b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.cpp index 6aef423902..a13aa66424 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.cpp @@ -1,71 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkOrbitAnimationItem.h" #include "QmitkOrbitAnimationWidget.h" #include QmitkOrbitAnimationWidget::QmitkOrbitAnimationWidget(QWidget* parent) : QmitkAnimationWidget(parent), m_Ui(new Ui::QmitkOrbitAnimationWidget) { m_Ui->setupUi(this); - this->connect(m_Ui->angleSlider, SIGNAL(valueChanged(int)), - m_Ui->angleSpinBox, SLOT(setValue(int))); + m_Ui->orbitLineEdit->setValidator(new QIntValidator(0, std::numeric_limits::max(), this)); - this->connect(m_Ui->angleSpinBox, SIGNAL(valueChanged(int)), - m_Ui->angleSlider, SLOT(setValue(int))); + this->connect(m_Ui->startAngleSlider, SIGNAL(valueChanged(int)), + m_Ui->startAngleSpinBox, SLOT(setValue(int))); - this->connect(m_Ui->angleSpinBox, SIGNAL(valueChanged(int)), - this, SLOT(OnAngleChanged(int))); + this->connect(m_Ui->startAngleSpinBox, SIGNAL(valueChanged(int)), + m_Ui->startAngleSlider, SLOT(setValue(int))); + + this->connect(m_Ui->startAngleSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(OnStartAngleChanged(int))); + + this->connect(m_Ui->orbitLineEdit, SIGNAL(editingFinished()), + this, SLOT(OnOrbitEditingFinished())); this->connect(m_Ui->reverseCheckBox, SIGNAL(clicked(bool)), this, SLOT(OnReverseChanged(bool))); } QmitkOrbitAnimationWidget::~QmitkOrbitAnimationWidget() { } void QmitkOrbitAnimationWidget::SetAnimationItem(QmitkAnimationItem* orbitAnimationItem) { m_AnimationItem = dynamic_cast(orbitAnimationItem); if (m_AnimationItem == NULL) return; - m_Ui->angleSlider->setValue(m_AnimationItem->GetAngle()); + m_Ui->startAngleSlider->setValue(m_AnimationItem->GetStartAngle()); + m_Ui->orbitLineEdit->setText(QString("%1").arg(m_AnimationItem->GetOrbit())); m_Ui->reverseCheckBox->setChecked(m_AnimationItem->GetReverse()); } -void QmitkOrbitAnimationWidget::OnAngleChanged(int angle) +void QmitkOrbitAnimationWidget::OnStartAngleChanged(int angle) { if (m_AnimationItem == NULL) return; - if (m_AnimationItem->GetAngle() != angle) - m_AnimationItem->SetAngle(angle); + if (m_AnimationItem->GetStartAngle() != angle) + m_AnimationItem->SetStartAngle(angle); +} + +void QmitkOrbitAnimationWidget::OnOrbitEditingFinished() +{ + if (m_AnimationItem == NULL) + return; + + int angle = m_Ui->orbitLineEdit->text().toInt(); + + if (m_AnimationItem->GetOrbit() != angle) + m_AnimationItem->SetOrbit(angle); } void QmitkOrbitAnimationWidget::OnReverseChanged(bool reverse) { if (m_AnimationItem == NULL) return; if (m_AnimationItem->GetReverse() != reverse) m_AnimationItem->SetReverse(reverse); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.h b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.h index cc6306683b..e343cfd47f 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.h +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.h @@ -1,48 +1,49 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkOrbitAnimationWidget_h #define QmitkOrbitAnimationWidget_h #include "QmitkAnimationWidget.h" class QmitkOrbitAnimationItem; namespace Ui { class QmitkOrbitAnimationWidget; } class QmitkOrbitAnimationWidget : public QmitkAnimationWidget { Q_OBJECT public: explicit QmitkOrbitAnimationWidget(QWidget* parent = NULL); ~QmitkOrbitAnimationWidget(); void SetAnimationItem(QmitkAnimationItem* orbitAnimationItem); private slots: - void OnAngleChanged(int angle); + void OnStartAngleChanged(int angle); + void OnOrbitEditingFinished(); void OnReverseChanged(bool reverse); private: Ui::QmitkOrbitAnimationWidget* m_Ui; QmitkOrbitAnimationItem* m_AnimationItem; }; #endif diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.ui b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.ui index 4ba7cc31c6..d07661f51c 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.ui +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkOrbitAnimationWidget.ui @@ -1,91 +1,107 @@ QmitkOrbitAnimationWidget 0 0 - 345 - 63 + 192 + 87 QmitkOrbitAnimationWidget - + 0 0 - Angle: + Start angle: - angleSlider + startAngleSlider - - - - - - 360 - - - 360 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - ° - - - 360 - - - 360 - - - - + + + + 360 + + + 180 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + 360 + + + 180 + + + + + + + + 0 + 0 + + + + Orbit: + + + startAngleSlider + + + + + + + 360 + + - + 0 0 Reverse - angleSlider - angleSpinBox + startAngleSlider + startAngleSpinBox reverseCheckBox diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.cpp b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.cpp index 19114b60c4..296a4575e6 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.cpp @@ -1,70 +1,74 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkSliceAnimationItem.h" QmitkSliceAnimationItem::QmitkSliceAnimationItem(int renderWindow, int from, int to, bool reverse, double duration, double delay, bool startWithPrevious) : QmitkAnimationItem("Slice", duration, delay, startWithPrevious) { this->SetRenderWindow(renderWindow); this->SetFrom(from); this->SetTo(to); this->SetReverse(reverse); } QmitkSliceAnimationItem::~QmitkSliceAnimationItem() { } int QmitkSliceAnimationItem::GetRenderWindow() const { return this->data(RenderWindowRole).toInt(); } void QmitkSliceAnimationItem::SetRenderWindow(int renderWindow) { this->setData(renderWindow, RenderWindowRole); } int QmitkSliceAnimationItem::GetFrom() const { return this->data(FromRole).toInt(); } void QmitkSliceAnimationItem::SetFrom(int from) { this->setData(from, FromRole); } int QmitkSliceAnimationItem::GetTo() const { return this->data(ToRole).toInt(); } void QmitkSliceAnimationItem::SetTo(int to) { this->setData(to, ToRole); } bool QmitkSliceAnimationItem::GetReverse() const { return this->data(ReverseRole).toBool(); } void QmitkSliceAnimationItem::SetReverse(bool reverse) { this->setData(reverse, ReverseRole); } + +void QmitkSliceAnimationItem::Animate(double /*s*/) +{ +} diff --git a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.h b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.h index 31975d8a08..74d0d0128d 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.h +++ b/Plugins/org.mitk.gui.qt.moviemaker2/src/internal/QmitkSliceAnimationItem.h @@ -1,41 +1,43 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkSliceAnimationItem_h #define QmitkSliceAnimationItem_h #include "QmitkAnimationItem.h" class QmitkSliceAnimationItem : public QmitkAnimationItem { public: QmitkSliceAnimationItem(int renderWindow, int from, int to, bool reverse, double duration = 2.0, double delay = 0.0, bool startWithPrevious = false); virtual ~QmitkSliceAnimationItem(); int GetRenderWindow() const; void SetRenderWindow(int renderWindow); int GetFrom() const; void SetFrom(int from); int GetTo() const; void SetTo(int to); bool GetReverse() const; void SetReverse(bool reverse); + + void Animate(double s); }; #endif