libpappsomspp
Library for mass spectrometry
massspectraceplotwidget.cpp
Go to the documentation of this file.
1/* This code comes right from the msXpertSuite software project.
2 *
3 * msXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright(C) 2009,...,2018 Filippo Rusconi
6 *
7 * http://www.msxpertsuite.org
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * END software license
23 */
24
25
26/////////////////////// StdLib includes
27#include <vector>
28
29
30/////////////////////// Qt includes
31#include <QVector>
32
33
34/////////////////////// Local includes
35
36// For the proton mass
37#include "../../types.h"
38
40#include "../../pappsoexception.h"
42
43
45 qRegisterMetaType<pappso::MassSpecTracePlotContext>(
46 "pappso::MassSpecTracePlotContext");
47
49 qRegisterMetaType<pappso::MassSpecTracePlotContext *>(
50 "pappso::MassSpecTracePlotContext *");
51
52
53namespace pappso
54{
55
57 : BaseTracePlotWidget(parent)
58{
59 // Set the base context to be of kind DataKind::mz;
60
62
63 // And then initialize ours with the base class' one.
65
66 // qDebug() << "Data kind:" <<
67 // static_cast<int>(m_context.m_baseContext.m_dataKind);
68}
69
71 const QString &x_axis_label,
72 const QString &y_axis_label)
73 : BaseTracePlotWidget(parent, x_axis_label, y_axis_label)
74{
75 // Set the base context to be of kind DataKind::mz;
76
78
79 // And then initialize ours with the base class' one.
81
82 // qDebug() << "Data kind:" <<
83 // static_cast<int>(m_context.m_baseContext.m_dataKind);
84}
85
86
88{
89}
90
91
92//! Set the \c m_pressedKeyCode to the key code in \p event.
93void
95{
96 // qDebug() << "ENTER";
98
99 // Before working on the various data belonging to the base context, we need
100 // to get it from the base class and refresh our local context with it.
102
103 // qDebug() << "Going to emit keyPressEventSignal(m_context);";
104
110}
111
112
113//! Handle specific key codes and trigger respective actions.
114void
116{
118
119 // Before working on the various data belonging to the base context, we need
120 // to get it from the base class and refresh our local context with it.
122}
123
124
125//! Handle mouse movements, in particular record all the last visited points.
126/*!
127
128 This function is reponsible for storing at each time the last visited point
129 in the graph. Here, point is intended as any x/y coordinate in the plot
130 widget viewport, not a graph point.
131
132 The stored values are then the basis for a large set of calculations
133 throughout all the plot widget.
134
135 \param pointer to QMouseEvent from which to retrieve the coordinates of the
136 visited viewport points.
137 */
138void
140{
142
143 // Before working on the various data belonging to the base context, we need
144 // to get it from the base class and refresh our local context with it.
146}
147
148
149void
151{
153
154 // Before working on the various data belonging to the base context, we need
155 // to get it from the base class and refresh our local context with it.
157}
158
159
160void
162{
164
165 // Before working on the various data belonging to the base context, we need
166 // to get it from the base class and refresh our local context with it.
168
170 {
172 return;
173
174 // qDebug() << "lastMovingMouseButtons:"
175 //<< m_context.m_baseContext.m_lastMovingMouseButtons;
176
177 deconvolute();
179 }
180}
181
182
183//! Record the clicks of the mouse.
184void
186{
188
189 // Before working on the various data belonging to the base context, we need
190 // to get it from the base class and refresh our local context with it.
192}
193
194
195//! React to the release of the mouse buttons.
196void
198{
200
201 // Before working on the various data belonging to the base context, we need
202 // to get it from the base class and refresh our local context with it.
204}
205
206
209{
211
212 return m_context;
213}
214
215
216void
218 double charge_fractional_part)
219{
220 m_chargeMinimalFractionalPart = charge_fractional_part;
221}
222
223
224double
226{
228}
229
230
231void
233{
235}
236
237
238int
240{
242}
243
244
245//! Deconvolute the mass peaks into charge and molecular mass.
246bool
248{
249
250 // There are two situations: when the user is deconvoluting on the
251 // basis of the distance between two consecutive peaks of a same
252 // isotopic cluster or when the user deconvolutes on the basis of two
253 // different charged-stated peaks that belong to the same envelope.
254
255 // We can tell the difference because in the first case the xDelta
256 // should be less than 1. In the other case, of course the difference
257 // is much greater than 1.
258
259 // In order to do the deconvolutions, we need to know what is the tolerance
260 // on the fractional part of the deconvoluted charge value. This value is set
261 // in the parent window's double spin box.
262
263 if(fabs(m_context.m_baseContext.m_xDelta) >= 0 &&
264 fabs(m_context.m_baseContext.m_xDelta) <= 1.1)
265 {
266 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
267 //<< "m_xDelta:" << m_context.m_baseContext.m_xDelta
268 //<< "trying isotope-based deconvolution.";
269
271 }
272
273 // If not deconvoluting on the basis of the isotopic cluster, then:
274
276}
277
278
279//! Deconvolute the mass peaks into charge and molecular mass.
280/*!
281
282 This is one of two methods to deconvolute mass data into a charge value and
283 a Mr value. The method implemented in this function is based on the charge
284 state envelope offered by the mass spectrum (most often for polymers of a
285 reasonable size).
286
287 \param span value representing the number of peaks of the charge state
288 envelope that are spanned by the user selection. Defaults to 1, that is, the
289 span encompasses two \e consecutive mass peaks of a given charge state
290 envelope.
291
292 Set m_lastMz, m_lastZ and m_lastMass.
293
294 \return true if the deconvolution could be performed, false otherwise.
295 */
296bool
298{
299 // We assume that we are dealing with two successive (if span is 1) mass
300 // peaks belonging to a given charge state family.
301
302 // We call span the number of intervals in a given charge state envelope
303 // that separate the initial peak (lowerMz) from the last peak (upperMz).
304 // That parameter defaults to 1, that is the two peaks are immediately
305 // consecutive, that is, there is only one interval.
306
307 // We use the m_contex.basecontext.m_xRegionRange structure that is unsorted.
308 // That is, lower is the start drag point.x and upper is the current drag
309 // point.x. If dragging occurs from left to right, start.x < cur.x.
310 // We use the unsorted values, because we need to know in which direction
311 // the user has drug the mouse, because we want to provide the Mr value
312 // for the peak currently under the mouse cursor, that is under
313 // currentDragPoint, that is the value in
314 // m_context.m_baseContext.m_xRegionRange.upper.
315
318
319 // qDebug() << "startMz:" << startMz << "curMz:" << curMz;
320
321 if(startMz == curMz)
322 {
323 m_context.m_lastZ = -1;
324 m_context.m_lastMz = std::numeric_limits<double>::min();
325 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
326 m_context.m_lastMr = std::numeric_limits<double>::min();
327
328 return false;
329 }
330
331 // We need to be aware that the status bar of the window that contains
332 // this plot widget shows the cursor position realtime, and that cursor
333 // position is the m_currentDragPoint.x value, that is, curMz. Thus, we need
334 // to make the calculations with the charge being the one of the polymer under
335 // the cursor position. This is tricky because it changes when the user
336 // switches drag senses: from left to right and right to left.
337 // The way z is calculated always makes it the charge of the highest mz
338 // value. So knowing this, depending on the drag direction we'll have to take
339 // curMz and apply to it either z charge (left to right drag) or (z+span)
340 // charge (right to left).
341
342 // Make sure lower is actually lower, even if drag is from right to left.
343 // This is only to have a single charge calculation.
344 double lowerMz;
345 double upperMz;
346
347 if(startMz < curMz)
348 {
349 lowerMz = startMz;
350 upperMz = curMz;
351 }
352 else
353 {
354 lowerMz = curMz;
355 upperMz = startMz;
356 }
357
358 double chargeTemp = ((lowerMz * span) - span) / (upperMz - lowerMz);
359
360 // Make a judicious roundup.
361
362 double chargeIntPart;
363 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
364
365 // When calculating the charge of the ion, very rarely does it provide a
366 // perfect integer value. Most often (if deconvolution is for bona fide
367 // peaks belonging to the same charge state envelope) that value is with
368 // either a large fractional part or a very small fractional part. What we
369 // test here, it that fractional part. If it is greater than
370 // m_chargeMinimalFractionalPart, then we simply round up to the next integer
371 // value (that is, chargeIntPart = 27 and chargeFracPart 0.995, then we
372 // set charge to 28). If it is lesser or equal to (1 -
373 // m_chargeMinimalFractionalPart /* that is >= 0.01 */, then we let
374 // chargeIntPart unmodified (that is, chargeIntPart = 29 and
375 // chargeFracPart 0.01, then we set charge to 29). If chargeFracPart is in
376 // between (1 - m_chargeMinimalFractionalPart) and
377 // m_chargeMinimalFractionalPart, then we consider that the peaks do not
378 // belong to the same charge state envelope.
379
380 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
381 //<< "Charge:" << chargeIntPart
382 //<< "Charge fractional part: " << chargeFracPart;
383
384
385 if(chargeFracPart >=
386 (1 - m_chargeMinimalFractionalPart /* that is >= 0.01 */) &&
387 chargeFracPart <= m_chargeMinimalFractionalPart /* that is <= 0.99 */)
388 {
389 m_context.m_lastZ = -1;
390 m_context.m_lastMz = std::numeric_limits<double>::min();
391 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
392 m_context.m_lastMr = std::numeric_limits<double>::min();
393
394 // qDebug() << __FILE__ << __LINE__
395 //<< "Not a charge state family peak,"
396 //<< "returning from deconvoluteChargeState";
397
398 return false;
399 }
400
401 if(chargeFracPart > m_chargeMinimalFractionalPart)
402 m_context.m_lastZ = chargeIntPart + 1;
403 else
404 m_context.m_lastZ = chargeIntPart;
405
406 // Now, to actually compute the molecular mass based on the charge and on
407 // the currently displayed m/z value, we need to have some thinking:
408
409 if(startMz < curMz)
410 {
411 // The drag was from left to right, that is curMz is greater than
412 // startMz. Fine, the z value is effectively the charge of the ion at
413 // curMz. Easy, no charge value modification here.
414 }
415 else
416 {
417 // The drag was from right to left, that is curMz is less than startMz.
418 // So we want to show the charge of the curMz, that is, z + span.
420 }
421
422 m_context.m_lastMz = curMz;
425
426 // qDebug() << __FILE__ << __LINE__
427 //<< "startMz:" << QString("%1").arg(startMz, 0, 'f', 6)
428 //<< "m_lastMz (curMz):"
429 //<< QString("%1").arg(m_context.m_lastMz, 0, 'f', 6)
430 //<< "m_lastMass:" << QString("%1").arg(m_context.m_lastMr, 0, 'f', 6)
431 //<< "m_lastZ:" << QString("%1").arg(m_context.m_lastZ);
432
433 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
434 //<< "returning true";
435
436 // The m_context was refreshed with the base class context in the calling
437 // chain.
439
440 return true;
441}
442
443
444//! Deconvolute the mass peaks into charge and molecular mass.
445/*!
446
447 This is one of two methods to deconvolute mass data into a charge value and
448 a Mr value. The method implemented in this function is based on the distance
449 that separates two immediately consecutive peaks of an isotopic cluster.
450 This method can be used as long as the instrument produced data with a
451 resolution sufficient to separate reasonably well the different peaks of an
452 isotopic cluster.
453
454 Set m_lastMz, m_lastZ and m_lastMass.
455
456 \return true if the deconvolution could be performed, false otherwise.
457 */
458bool
460{
461
464 {
465 // qDebug() << __FILE__ << __LINE__
466 //<< "Same xRegionRange.upper and xRegionRange.lower:"
467 //<< "returning from deconvoluteIsotopicCluster";
468
469 return false;
470 }
471
472 double chargeTemp = 1 / fabs(m_context.m_baseContext.m_xDelta);
473
474 // Make a judicious roundup.
475 double chargeIntPart;
476 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
477
478 // qDebug() << "m_xDelta:" << m_context.m_baseContext.m_xDelta
479 //<< "chargeTemp:" << QString("%1").arg(chargeTemp, 0, 'f', 6)
480 //<< "chargeIntPart:" << chargeIntPart
481 //<< "chargeFracPart:" << QString("%1").arg(chargeFracPart, 0, 'f', 6)
482 //<< "m_chargeMinimalFractionalPart:" << m_chargeMinimalFractionalPart;
483
484 if(chargeFracPart >= (1 - m_chargeMinimalFractionalPart) &&
485 chargeFracPart <= m_chargeMinimalFractionalPart)
486 {
487 m_context.m_lastZ = -1;
488 m_context.m_lastMz = std::numeric_limits<double>::min();
489 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
490 m_context.m_lastMr = std::numeric_limits<double>::min();
491
492 // qDebug() << "Not in a isotopic cluster peak:"
493 //<< "returning from deconvoluteIsotopicCluster";
494
495 return false;
496 }
497
498 if(chargeFracPart > m_chargeMinimalFractionalPart)
499 {
500 m_context.m_lastZ = chargeIntPart + 1;
501
502 // qDebug() << "chargeFracPart > m_chargeMinimalFractionalPart -> m_lastZ
503 // = "
504 //<< m_context.m_lastZ;
505 }
506 else
507 {
508 m_context.m_lastZ = chargeIntPart;
509
510 // qDebug()
511 //<< "chargeFracPart <= m_chargeMinimalFractionalPart -> m_lastZ = "
512 //<< m_context.m_lastZ;
513 }
514
515 // Now that we have the charge in the form of an int, we can compute the
516 // Mr of the lightest isotopic cluster peak (the one that has the lowest x
517 // value). That value is stored in m_xRangeLower.
518
519 // We need to sort the xRegionRange before being certain that lower is indeed
520 // the left value of the drag span.
521
524
527
528 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
529 //<< "returning true";
530
531 // The m_context was refreshed with the base class context in the calling
532 // chain.
534
535 return true;
536}
537
538
539bool
541{
542
543 // m_xRangeLower and m_xRangeUpper and m_xDelta (in fabs() form) have been set
544 // during mouve movement handling. Note that the range values *are
545 // sorted*.
546
548 {
549 m_context.m_lastResolvingPower = std::numeric_limits<double>::min();
550
551 return false;
552 }
553
554 // Resolving power is m/z / Delta(m/z), for singly-charged species.
555
561
562 // The m_context was refreshed with the base class context in the calling
563 // chain.
565
566 return true;
567}
568
569
570} // namespace pappso
Qt::MouseButtons m_mouseButtonsAtMousePress
virtual void mouseMoveHandlerDraggingCursor()
virtual void keyPressEvent(QKeyEvent *event)
KEYBOARD-related EVENTS.
virtual void mouseMoveHandlerNotDraggingCursor()
virtual void mousePressHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
virtual void mouseReleaseHandler(QMouseEvent *event)
virtual void mouseMoveHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
virtual void keyReleaseEvent(QKeyEvent *event)
Handle specific key codes and trigger respective actions.
BasePlotContext m_context
virtual void mouseMoveHandler(QMouseEvent *event) override
Handle mouse movements, in particular record all the last visited points.
void resolvingPowerComputationSignal(const MassSpecTracePlotContext &context)
virtual void keyReleaseEvent(QKeyEvent *event) override
Handle specific key codes and trigger respective actions.
const MassSpecTracePlotContext & refreshBaseContext() const
void testKeyPressEventSignal(pappso::MassSpecTracePlotContext context)
void newKeyPressEventSignal(pappso::DataPoint context)
bool deconvoluteChargedState(int span=1)
Deconvolute the mass peaks into charge and molecular mass.
bool deconvoluteIsotopicCluster()
Deconvolute the mass peaks into charge and molecular mass.
bool deconvolute()
Deconvolute the mass peaks into charge and molecular mass.
virtual void mouseMoveHandlerDraggingCursor() override
void keyPressEventSignal(const MassSpecTracePlotContext &context)
void massDeconvolutionSignal(const MassSpecTracePlotContext &context)
virtual void mousePressHandler(QMouseEvent *event) override
Record the clicks of the mouse.
void setChargeMinimalFractionalPart(double charge_fractional_part)
virtual void keyPressEvent(QKeyEvent *event) override
Set the m_pressedKeyCode to the key code in event.
virtual void mouseMoveHandlerNotDraggingCursor() override
virtual void mouseReleaseHandler(QMouseEvent *event) override
React to the release of the mouse buttons.
int massSpecTracePlotContextMetaTypeId
int massSpecTracePlotContextPtrMetaTypeId
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition: aa.cpp:39
const pappso_double MPROTON(1.007276466879)