diff --git a/avogadro/qtplugins/spectra/CMakeLists.txt b/avogadro/qtplugins/spectra/CMakeLists.txt index 6de1349ff8..0bba92cf24 100644 --- a/avogadro/qtplugins/spectra/CMakeLists.txt +++ b/avogadro/qtplugins/spectra/CMakeLists.txt @@ -12,3 +12,5 @@ avogadro_plugin(Spectra "${plugin_srcs}" "vibrationdialog.ui" ) + +target_link_libraries(Spectra PRIVATE Avogadro::Vtk) diff --git a/avogadro/qtplugins/spectra/spectra.cpp b/avogadro/qtplugins/spectra/spectra.cpp index d893d4e0be..bd9720604e 100644 --- a/avogadro/qtplugins/spectra/spectra.cpp +++ b/avogadro/qtplugins/spectra/spectra.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include + #include #include #include @@ -27,6 +30,12 @@ Spectra::Spectra(QObject* p) action->setText(tr("Vibrational Modes…")); connect(action, SIGNAL(triggered()), SLOT(openDialog())); m_actions.push_back(action); + + action = new QAction(this); + action->setEnabled(false); + action->setText(tr("Spectra…")); + connect(action, SIGNAL(triggered()), SLOT(showSpectraChart())); + m_actions.push_back(action); } Spectra::~Spectra() {} @@ -50,6 +59,7 @@ void Spectra::setMolecule(QtGui::Molecule* mol) isVibrational = true; m_actions[0]->setEnabled(isVibrational); + m_actions[1]->setEnabled(isVibrational); m_molecule = mol; if (m_dialog) m_dialog->setMolecule(mol); @@ -212,6 +222,55 @@ void Spectra::openDialog() m_dialog->show(); } +void Spectra::showSpectraChart() +{ + if (m_molecule == nullptr || m_molecule->vibrationFrequencies().empty()) + return; + + std::vector xData; + std::vector yData; + // generate the raw stick spectrum + float maxIntensity = 0.0f; + for (unsigned int x = 0; x < 4000; ++x) { + xData.push_back(static_cast(x)); + // check if x is near a frequency and add a peak + bool found = false; + for (auto index = 0; index < m_molecule->vibrationFrequencies().size(); + ++index) { + auto freq = m_molecule->vibrationFrequencies()[index]; + if (std::abs(static_cast(x) - static_cast(freq)) < 2) { + yData.push_back(m_molecule->vibrationIRIntensities()[index]); + if (m_molecule->vibrationIRIntensities()[index] > maxIntensity) + maxIntensity = m_molecule->vibrationIRIntensities()[index]; + + found = true; + break; + } + } + if (!found) + yData.push_back(0.0f); + } + + auto xTitle = tr("Wavenumbers (cm⁻¹)"); + auto yTitle = tr("Transmission"); + auto windowName = tr("Vibrational Spectra"); + + if (!m_chartDialog) { + m_chartDialog.reset( + new VTK::ChartDialog(qobject_cast(this->parent()))); + } + + m_chartDialog->setWindowTitle(windowName); + auto* chart = m_chartDialog->chartWidget(); + chart->clearPlots(); + chart->setXAxisTitle(xTitle.toStdString()); + chart->setYAxisTitle(yTitle.toStdString()); + chart->addPlot(xData, yData, VTK::color4ub{ 255, 0, 0, 255 }); + chart->setXAxisLimits(4000.0, 0.0); + chart->setYAxisLimits(maxIntensity, 0.0); + m_chartDialog->show(); +} + void Spectra::advanceFrame() { if (++m_currentFrame >= m_totalFrames) diff --git a/avogadro/qtplugins/spectra/spectra.h b/avogadro/qtplugins/spectra/spectra.h index 588210a66a..aa79028012 100644 --- a/avogadro/qtplugins/spectra/spectra.h +++ b/avogadro/qtplugins/spectra/spectra.h @@ -13,6 +13,11 @@ class QDialog; class QTimer; namespace Avogadro { + +namespace VTK { + class ChartDialog; +} + namespace QtPlugins { class VibrationDialog; @@ -53,6 +58,7 @@ public slots: void startVibrationAnimation(); void stopVibrationAnimation(); void openDialog(); + void showSpectraChart(); private slots: void advanceFrame(); @@ -63,6 +69,7 @@ private slots: QtGui::Molecule* m_molecule; VibrationDialog* m_dialog; + QScopedPointer m_chartDialog; QTimer* m_timer; diff --git a/avogadro/vtk/chartwidget.cpp b/avogadro/vtk/chartwidget.cpp index 8275e91594..eaf6044108 100644 --- a/avogadro/vtk/chartwidget.cpp +++ b/avogadro/vtk/chartwidget.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -113,16 +114,26 @@ void ChartWidget::clearPlots() m_table->RemoveAllColumns(); } -void ChartWidget::setXAxisTitle(const char* title) +void ChartWidget::setXAxisTitle(const std::string title) { auto* axis = m_chart->GetAxis(vtkAxis::BOTTOM); axis->SetTitle(title); + axis->SetTitleVisible(true); + axis->GetTitleProperties()->SetFontSize(18); + axis->GetTitleProperties()->SetBold(true); + + axis->GetLabelProperties()->SetFontSize(14); } -void ChartWidget::setYAxisTitle(const char* title) +void ChartWidget::setYAxisTitle(const std::string title) { auto* axis = m_chart->GetAxis(vtkAxis::LEFT); axis->SetTitle(title); + axis->SetTitleVisible(true); + axis->GetTitleProperties()->SetFontSize(18); + axis->GetTitleProperties()->SetBold(true); + + axis->GetLabelProperties()->SetFontSize(14); } void ChartWidget::setTickLabels(Axis a, const std::vector& tickPositions, @@ -161,6 +172,27 @@ void ChartWidget::setAxisLimits(Axis a, float min, float max) customAxis->SetBehavior(vtkAxis::FIXED); } +void ChartWidget::setXAxisLimits(float min, float max) +{ + setAxisLimits(Axis::x, min, max); +} + +void ChartWidget::setYAxisLimits(float min, float max) +{ + setAxisLimits(Axis::y, min, max); +} + +void ChartWidget::setAxisLogScale(Axis a, bool logScale) +{ + auto customAxis = axis(a); + + // We need a valid axis and equal sizes vectors of points/labels. + if (!customAxis) + return; + + customAxis->SetLogScale(logScale); +} + vtkAxis* ChartWidget::axis(Axis a) { if (a == Axis::x) diff --git a/avogadro/vtk/chartwidget.h b/avogadro/vtk/chartwidget.h index f25559333f..031143afe7 100644 --- a/avogadro/vtk/chartwidget.h +++ b/avogadro/vtk/chartwidget.h @@ -6,7 +6,9 @@ #include "avogadrovtkexport.h" #include + #include +#include #include @@ -43,14 +45,18 @@ class AVOGADROVTK_EXPORT ChartWidget : public QWidget void clearPlots(); - void setXAxisTitle(const char* title); + void setXAxisTitle(const std::string title); - void setYAxisTitle(const char* title); + void setYAxisTitle(const std::string title); void setTickLabels(Axis a, const std::vector& tickPositions, const std::vector& tickLabels); void setAxisLimits(Axis a, float min, float max); + void setXAxisLimits(float min, float max); + void setYAxisLimits(float min, float max); + + void setAxisLogScale(Axis a, bool logScale); private: void renderViews();