Warning, file /education/kalzium/src/spectrumwidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2005, 2006 Carsten Niehaus <cniehaus@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "spectrumwidget.h" 0008 0009 #include "kalziumutils.h" 0010 0011 #include <config-kalzium.h> 0012 0013 #include <cmath> 0014 #include <element.h> 0015 0016 #include "kalzium_debug.h" 0017 #include <QGlobalStatic> 0018 #include <QKeyEvent> 0019 #include <QPainter> 0020 #include <QPixmap> 0021 #include <QSizePolicy> 0022 0023 #include <KUnitConversion/Converter> 0024 0025 #if defined(HAVE_IEEEFP_H) 0026 #include <ieeefp.h> 0027 #endif 0028 0029 SpectrumWidget::SpectrumWidget(QWidget *parent) 0030 : QWidget(parent) 0031 { 0032 m_startValue = 0; 0033 m_endValue = 0; 0034 0035 m_LMBPointCurrent.setX(-1); 0036 m_LMBPointPress.setX(-1); 0037 0038 m_realHeight = 200; 0039 0040 m_gamma = 0.8; 0041 m_intensityMax = 255; 0042 0043 setType(Prefs::spectrumType()); 0044 0045 setMinimumSize(400, 230); 0046 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0047 setAttribute(Qt::WA_OpaquePaintEvent, true); 0048 setContextMenuPolicy(Qt::PreventContextMenu); 0049 } 0050 0051 void SpectrumWidget::paintEvent(QPaintEvent * /*e*/) 0052 { 0053 if (!m_spectrum) { 0054 return; 0055 } 0056 0057 m_pixmap = QPixmap(width(), height()); 0058 m_pixmap.fill(); 0059 0060 QPainter p; 0061 p.begin(&m_pixmap); 0062 p.fillRect(0, 0, width(), m_realHeight, Qt::black); 0063 0064 paintBands(&p); 0065 0066 drawTickmarks(&p); 0067 0068 if (m_LMBPointPress.x() != -1 && m_LMBPointCurrent.x() != -1) { 0069 drawZoomLine(&p); 0070 } 0071 0072 p.end(); 0073 0074 QPainter p2(this); 0075 p2.drawPixmap(0, 0, m_pixmap); 0076 } 0077 0078 void SpectrumWidget::drawZoomLine(QPainter *p) 0079 { 0080 p->setPen(Qt::white); 0081 p->drawLine(m_LMBPointPress.x(), m_LMBPointPress.y(), m_LMBPointCurrent.x(), m_LMBPointPress.y()); 0082 p->drawLine(m_LMBPointCurrent.x(), m_LMBPointPress.y() + 10, m_LMBPointCurrent.x(), m_LMBPointPress.y() - 10); 0083 p->drawLine(m_LMBPointPress.x(), m_LMBPointPress.y() + 10, m_LMBPointPress.x(), m_LMBPointPress.y() - 10); 0084 } 0085 0086 void SpectrumWidget::paintBands(QPainter *p) 0087 { 0088 if (m_type == AbsorptionSpectrum) { 0089 for (double va = m_startValue; va <= m_endValue; va += 0.1) { 0090 int x = xPos(va); 0091 p->setPen(wavelengthToRGB(va)); 0092 p->drawLine(x, 0, x, m_realHeight); 0093 } 0094 0095 p->setPen(Qt::black); 0096 } 0097 0098 int i = 0; 0099 int x = 0; 0100 double currentWavelength; 0101 0102 foreach (Spectrum::peak *peak, m_spectrum->peaklist()) { 0103 currentWavelength = peak->wavelengthToUnit(Prefs::spectrumWavelengthUnit()); 0104 0105 if (currentWavelength < m_startValue || currentWavelength > m_endValue) { 0106 continue; 0107 } 0108 0109 x = xPos(currentWavelength); 0110 0111 switch (m_type) { 0112 case EmissionSpectrum: 0113 p->setPen(wavelengthToRGB(currentWavelength)); 0114 p->drawLine(x, 0, x, m_realHeight - 1); 0115 0116 p->setPen(Qt::black); 0117 p->drawLine(x, m_realHeight, x, m_realHeight); 0118 break; 0119 0120 case AbsorptionSpectrum: 0121 p->setPen(Qt::black); 0122 p->drawLine(x, 0, x, m_realHeight - 1); 0123 break; 0124 } 0125 0126 ++i; 0127 } 0128 } 0129 0130 QColor SpectrumWidget::wavelengthToRGB(double wavelength) 0131 { 0132 double blue = 0.0, green = 0.0, red = 0.0, factor = 0.0; 0133 0134 // wavelengthTo RGB function works with nanometers. 0135 wavelength = KUnitConversion::Value(wavelength, KUnitConversion::UnitId(Prefs::spectrumWavelengthUnit())).convertTo(KUnitConversion::Nanometer).number(); 0136 0137 int wavelength_ = (int)floor(wavelength); 0138 0139 if (wavelength_ < 380 || wavelength_ > 780) { 0140 return {Qt::white}; 0141 } else if (wavelength_ >= 380 && wavelength_ < 440) { 0142 red = -(wavelength - 440) / (440 - 380); 0143 green = 0.0; 0144 blue = 1.0; 0145 } else if (wavelength_ >= 440 && wavelength_ < 490) { 0146 red = 0.0; 0147 green = (wavelength - 440) / (490 - 440); 0148 blue = 1.0; 0149 } else if (wavelength_ >= 490 && wavelength_ < 510) { 0150 red = 0.0; 0151 green = 1.0; 0152 blue = -(wavelength - 510) / (510 - 490); 0153 } else if (wavelength_ >= 510 && wavelength_ < 580) { 0154 red = (wavelength - 510) / (580 - 510); 0155 green = 1.0; 0156 blue = 0.0; 0157 } else if (wavelength_ >= 580 && wavelength_ < 645) { 0158 red = 1.0; 0159 green = -(wavelength - 645) / (645 - 580); 0160 blue = 0.0; 0161 } else if (wavelength_ >= 645 && wavelength_ < 780) { 0162 red = 1.0; 0163 green = 0.0; 0164 blue = 0.0; 0165 } 0166 0167 if (wavelength_ > 380 && wavelength_ <= 420) { 0168 factor = 0.3 + 0.7 * (wavelength - 380) / (420 - 380); 0169 } else if (wavelength_ > 420 && wavelength_ <= 701) { 0170 factor = 1.0; 0171 } else if (wavelength_ > 701 && wavelength_ <= 780) { 0172 factor = 0.3 + 0.7 * (780 - wavelength) / (780 - 700); 0173 } else { 0174 factor = 0.0; 0175 } 0176 0177 return {Adjust(red, factor), Adjust(green, factor), Adjust(blue, factor)}; 0178 } 0179 0180 int SpectrumWidget::Adjust(double color, double /*factor*/) 0181 { 0182 if (color == 0.0) { 0183 return 0; 0184 } else { 0185 // return qRound(m_intensityMax * pow(color*factor, m_gamma)); FIXME 0186 return m_intensityMax * color; 0187 } 0188 } 0189 0190 void SpectrumWidget::drawTickmarks(QPainter *p) 0191 { 0192 // the size of the text on the tickmarks 0193 const int space = 20; 0194 0195 // the distance between the tickmarks in pixel 0196 const int d = 10; 0197 0198 // the total number of tickmarks to draw (small and long) 0199 const int numberOfTickmarks = (int)floor((double)(width() / d)); 0200 0201 double pos = 0.0; 0202 0203 for (int i = 0; i < numberOfTickmarks; ++i) { 0204 if (i % 5 == 0) { 0205 // long tickmarks plus text 0206 p->drawLine(i * d, m_realHeight, i * d, m_realHeight + 10); 0207 if (i % 10 == 0 && i * d > space && i * d < width() - space) { 0208 pos = (double)(i * d) / width(); 0209 p->fillRect(i * d - space, m_realHeight + 12, 2 * space, 15, Qt::white); 0210 p->drawText(i * d - space, m_realHeight + 12, 2 * space, 15, Qt::AlignCenter, QString::number(KalziumUtils::strippedValue(Wavelength(pos)))); 0211 } 0212 } else { // small tickmarks 0213 p->drawLine(i * d, m_realHeight, i * d, m_realHeight + 5); 0214 } 0215 } 0216 } 0217 0218 void SpectrumWidget::keyPressEvent(QKeyEvent *e) 0219 { 0220 switch (e->key()) { 0221 case Qt::Key_Plus: 0222 slotZoomIn(); 0223 break; 0224 case Qt::Key_Minus: 0225 slotZoomOut(); 0226 break; 0227 } 0228 } 0229 0230 void SpectrumWidget::slotZoomOut() 0231 { 0232 qCDebug(KALZIUM_LOG) << "SpectrumWidget::slotZoomOut() " << m_startValue << ":: " << m_endValue; 0233 0234 double diff = m_endValue - m_startValue; 0235 0236 double offset = diff * 0.05; 0237 0238 m_endValue = m_endValue + offset; 0239 m_startValue = m_startValue - offset; 0240 0241 // check for invalid values 0242 if (m_startValue < 0.0) { 0243 m_startValue = 0.0; 0244 } 0245 0246 if (m_endValue > 10000.0) { // FIXME: Magic numbers... 0247 m_endValue = 40000.0; 0248 } 0249 0250 setBorders(m_startValue, m_endValue); 0251 } 0252 0253 void SpectrumWidget::setBorders(double left, double right) 0254 { 0255 qCDebug(KALZIUM_LOG) << "setBorders " << left << ".." << right; 0256 0257 m_startValue = left; 0258 m_endValue = right; 0259 0260 // round the startValue down and the endValue up 0261 Q_EMIT bordersChanged(int(m_startValue + 0.5), int(m_endValue + 0.5)); 0262 0263 update(); 0264 } 0265 0266 void SpectrumWidget::slotZoomIn() 0267 { 0268 qCDebug(KALZIUM_LOG) << "SpectrumWidget::slotZoomIn() " << m_startValue << ":: " << m_endValue; 0269 0270 double diff = m_endValue - m_startValue; 0271 0272 double offset = diff * 0.05; 0273 0274 m_endValue = m_endValue - offset; 0275 m_startValue = m_startValue + offset; 0276 0277 setBorders(m_startValue, m_endValue); 0278 } 0279 0280 void SpectrumWidget::mouseMoveEvent(QMouseEvent *e) 0281 { 0282 m_LMBPointCurrent = e->pos(); 0283 update(); 0284 } 0285 0286 void SpectrumWidget::mousePressEvent(QMouseEvent *e) 0287 { 0288 if (e->button() == Qt::LeftButton) { 0289 m_LMBPointPress = e->pos(); 0290 } 0291 if (e->button() == Qt::RightButton) { 0292 resetSpectrum(); 0293 } 0294 0295 findPeakFromMouseposition(Wavelength((double)e->pos().x() / width())); 0296 } 0297 0298 void SpectrumWidget::findPeakFromMouseposition(double wavelength) 0299 { 0300 qCDebug(KALZIUM_LOG) << "SpectrumWidget::findPeakFromMouseposition()"; 0301 Spectrum::peak *peak = nullptr; 0302 0303 // find the difference in percent (1.0 is 100%, 0.1 is 10%) 0304 double dif = 0.0; 0305 0306 bool foundWavelength = false; 0307 0308 // find the highest intensity 0309 foreach (Spectrum::peak *currentPeak, m_spectrum->peaklist()) { 0310 double currentWavelength = currentPeak->wavelengthToUnit(Prefs::spectrumWavelengthUnit()); 0311 0312 double thisdif = currentWavelength / wavelength; 0313 0314 if (thisdif < 0.9 || thisdif > 1.1) { 0315 continue; 0316 } 0317 0318 if (thisdif > 1.0) { // convert for example 1.3 to 0.7 0319 thisdif = thisdif - 1; 0320 thisdif = 1 - thisdif; 0321 } 0322 0323 if (thisdif > dif) { 0324 dif = thisdif; 0325 peak = currentPeak; 0326 foundWavelength = true; 0327 } 0328 } 0329 0330 if (foundWavelength) { 0331 Q_EMIT peakSelected(peak); 0332 } 0333 } 0334 0335 void SpectrumWidget::mouseReleaseEvent(QMouseEvent *e) 0336 { 0337 if (e->button() == Qt::LeftButton) { 0338 int left = (int)Wavelength((double)m_LMBPointPress.x() / width()); 0339 int right = (int)Wavelength((double)e->pos().x() / width()); 0340 0341 if (left == right) { 0342 return; 0343 } 0344 0345 if (left > right) { 0346 setBorders(right, left); 0347 } else { 0348 setBorders(left, right); 0349 } 0350 } 0351 0352 m_LMBPointPress.setX(-1); 0353 m_LMBPointCurrent.setX(-1); 0354 } 0355 0356 void SpectrumWidget::setSpectrum(Spectrum *spec) 0357 { 0358 m_spectrum = spec; 0359 resetSpectrum(); 0360 } 0361 0362 void SpectrumWidget::resetSpectrum() 0363 { 0364 // set the minimum and maximum peak to the min/max wavelength 0365 // plus/minus ten. This makes then always visible 0366 double minimumPeak = m_spectrum->minPeak(Prefs::spectrumWavelengthUnit()) - 20.0; 0367 double maximumPeak = m_spectrum->maxPeak(Prefs::spectrumWavelengthUnit()) + 20.0; 0368 0369 setBorders(minimumPeak, maximumPeak); 0370 }