libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
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 "../../core/types.h"
38
41
42
44 qRegisterMetaType<pappso::MassSpecTracePlotContext>(
45 "pappso::MassSpecTracePlotContext");
46
48 qRegisterMetaType<pappso::MassSpecTracePlotContext *>(
49 "pappso::MassSpecTracePlotContext *");
50
51
52namespace pappso
53{
54
56 : BaseTracePlotWidget(parent)
57{
59 m_context.m_dataKind = Enums::DataKind::mz;
60
61 // qDebug() << "Data kind:" << static_cast<int>(m_context.m_dataKind);
62}
63
65 const QString &x_axis_label,
66 const QString &y_axis_label)
67 : BaseTracePlotWidget(parent, x_axis_label, y_axis_label)
68{
69 // Set the base context to be of kind Enums::DataKind::mz;
70
72 m_context.m_dataKind = Enums::DataKind::mz;
73
74 // qDebug() << "Data kind:" << static_cast<int>(m_context.m_dataKind);
75}
76
77
81
82
83//! Set the \c m_pressedKeyCode to the key code in \p event.
84void
86{
87 // qDebug() << "ENTER";
89
90 // Before working on the various data belonging to the base context, we need
91 // to get it from the base class and refresh our local context with it.
93
94 // qDebug() << "Going to emit keyPressEventSignal(m_context);";
95
97}
98
99
100//! Handle specific key codes and trigger respective actions.
101void
103{
104 // Before working on the various data belonging to the base context, we need
105 // to get it from the base class and refresh our local context with it.
107
109}
110
111
112//! Handle mouse movements, in particular record all the last visited points.
113/*!
114
115 This function is reponsible for storing at each time the last visited point
116 in the graph. Here, point is intended as any x/y coordinate in the plot
117 widget viewport, not a graph point.
118
119 The stored values are then the basis for a large set of calculations
120 throughout all the plot widget.
121
122 \param pointer to QMouseEvent from which to retrieve the coordinates of the
123 visited viewport points.
124 */
125void
127{
128 // Before working on the various data belonging to the base context, we need
129 // to get it from the base class and refresh our local context with it.
131
133}
134
135
136void
138{
139 // Before working on the various data belonging to the base context, we need
140 // to get it from the base class and refresh our local context with it.
142
144}
145
146
147void
149{
151
152 // Before working on the various data belonging to the base context, we need
153 // to get it from the base class and refresh our local context with it.
155
156 if(m_context.m_mouseButtonsAtMousePress & Qt::LeftButton)
157 {
158 if(!m_context.m_isMeasuringDistance)
159 return;
160
161 // qDebug() << "lastMovingMouseButtons:"
162 //<< m_context.m_baseContext.m_lastMovingMouseButtons;
163
164 deconvolute();
166 }
167}
168
169
170//! Record the clicks of the mouse.
171void
172MassSpecTracePlotWidget::mousePressHandler([[maybe_unused]] QMouseEvent *event)
173{
174 // qDebug() << "Entering";
175
176 // Before working on the various data belonging to the base context, we need
177 // to get it from the base class and refresh our local context with it.
179
181
183
184 // qDebug() << "Exiting after having emitted mousePressEventSignal with context:"
185 // << m_context.toString();
186}
187
188
189//! React to the release of the mouse buttons.
190void
192{
193 // qDebug() << "Entering";
194
195 // Before working on the various data belonging to the base context, we need
196 // to get it from the base class and refresh our local context with it.
198
200
202
203 // qDebug() << "Exiting after having emitted mouseReleaseEventSignal with context:"
204 // << m_context.toString();
205}
206
207
210{
211 // BasePlotWidget has a member m_context of type BasePlotContext.
212 // Here we also have a m_context *distinct* member that is of type
213 // MassSpecTracePlotContext.
214
215 // While MassSpecTracePlotContext is derived from BasePlotContext, the two
216 // m_context members are distinct and there are lots of housekeeping data that
217 // are managed by the parent BasePlotWidget class in its m_context member
218 // *independently* of what we have in the ::BasePlotContext part of our
219 // m_context member that is of type MassSpecTracePlotContext. We thus need to
220 // resynchronize the data from BasePlotWidget::m_context to our m_context.
221
222 // qDebug().noquote() << "The base plot context:" <<
223 // BasePlotWidget::m_context.toString();
224
226
227 // qDebug().noquote() << "After refreshing the base context, base context:"
228 // << m_context.toString();
229
230 return m_context;
231}
232
233
234void
236 double charge_fractional_part)
237{
238 m_chargeMinimalFractionalPart = charge_fractional_part;
239}
240
241
242double
247
248
249void
254
255
256int
261
262
263//! Deconvolute the mass peaks into charge and molecular mass.
264bool
266{
267
268 // There are two situations: when the user is deconvoluting on the
269 // basis of the distance between two consecutive peaks of a same
270 // isotopic cluster or when the user deconvolutes on the basis of two
271 // different charged-stated peaks that belong to the same envelope.
272
273 // We can tell the difference because in the first case the xDelta
274 // should be less than 1. In the other case, of course the difference
275 // is much greater than 1.
276
277 // In order to do the deconvolutions, we need to know what is the tolerance
278 // on the fractional part of the deconvoluted charge value. This value is set
279 // in the parent window's double spin box.
280
281 if(fabs(m_context.m_xDelta) >= 0 && fabs(m_context.m_xDelta) <= 1.1)
282 {
283 // qDebug() << "m_xDelta:" << m_context.m_xDelta
284 //<< "trying isotope-based deconvolution.";
285
287 }
288
289 // If not deconvoluting on the basis of the isotopic cluster, then:
290
292}
293
294
295//! Deconvolute the mass peaks into charge and molecular mass.
296/*!
297
298 This is one of two methods to deconvolute mass data into a charge value and
299 a Mr value. The method implemented in this function is based on the charge
300 state envelope offered by the mass spectrum (most often for polymers of a
301 reasonable size).
302
303 \param span value representing the number of peaks of the charge state
304 envelope that are spanned by the user selection. Defaults to 1, that is, the
305 span encompasses two \e consecutive mass peaks of a given charge state
306 envelope.
307
308 Set m_lastMz, m_lastZ and m_lastMass.
309
310 \return true if the deconvolution could be performed, false otherwise.
311 */
312bool
314{
315 // We assume that we are dealing with two successive (if span is 1) mass
316 // peaks belonging to a given charge state family.
317
318 // We call span the number of intervals in a given charge state envelope
319 // that separate the initial peak (lowerMz) from the last peak (upperMz).
320 // That parameter defaults to 1, that is the two peaks are immediately
321 // consecutive, that is, there is only one interval.
322
323 // We use the m_contex.basecontext.m_xRegionRange structure that is unsorted.
324 // That is, lower is the start drag point.x and upper is the current drag
325 // point.x. If dragging occurs from left to right, start.x < cur.x.
326 // We use the unsorted values, because we need to know in which direction
327 // the user has drug the mouse, because we want to provide the Mr value
328 // for the peak currently under the mouse cursor, that is under
329 // currentDragPoint, that is the value in
330 // m_context.m_baseContext.m_xRegionRange.upper.
331
332 double startMz = m_context.m_xRegionRangeStart;
333 double curMz = m_context.m_xRegionRangeEnd;
334
335 // qDebug() << "startMz:" << startMz << "curMz:" << curMz;
336
337 if(startMz == curMz)
338 {
339 m_context.m_lastZ = -1;
340 m_context.m_lastMz = std::numeric_limits<double>::min();
341 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
342 m_context.m_lastMr = std::numeric_limits<double>::min();
343
344 return false;
345 }
346
347 // We need to be aware that the status bar of the window that contains
348 // this plot widget shows the cursor position realtime, and that cursor
349 // position is the m_currentDragPoint.x value, that is, curMz. Thus, we need
350 // to make the calculations with the charge being the one of the polymer under
351 // the cursor position. This is tricky because it changes when the user
352 // switches drag senses: from left to right and right to left.
353 // The way z is calculated always makes it the charge of the highest mz
354 // value. So knowing this, depending on the drag direction we'll have to take
355 // curMz and apply to it either z charge (left to right drag) or (z+span)
356 // charge (right to left).
357
358 // Make sure lower is actually lower, even if drag is from right to left.
359 // This is only to have a single charge calculation.
360 double lowerMz;
361 double upperMz;
362
363 if(startMz < curMz)
364 {
365 lowerMz = startMz;
366 upperMz = curMz;
367 }
368 else
369 {
370 lowerMz = curMz;
371 upperMz = startMz;
372 }
373
374 double chargeTemp = ((lowerMz * span) - span) / (upperMz - lowerMz);
375
376 // Make a judicious roundup.
377
378 double chargeIntPart;
379 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
380
381 // When calculating the charge of the ion, very rarely does it provide a
382 // perfect integer value. Most often (if deconvolution is for bona fide
383 // peaks belonging to the same charge state envelope) that value is with
384 // either a large fractional part or a very small fractional part. What we
385 // test here, it that fractional part. If it is greater than
386 // m_chargeMinimalFractionalPart, then we simply round up to the next integer
387 // value (that is, chargeIntPart = 27 and chargeFracPart 0.995, then we
388 // set charge to 28). If it is lesser or equal to (1 -
389 // m_chargeMinimalFractionalPart /* that is >= 0.01 */, then we let
390 // chargeIntPart unmodified (that is, chargeIntPart = 29 and
391 // chargeFracPart 0.01, then we set charge to 29). If chargeFracPart is in
392 // between (1 - m_chargeMinimalFractionalPart) and
393 // m_chargeMinimalFractionalPart, then we consider that the peaks do not
394 // belong to the same charge state envelope.
395
396 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
397 //<< "Charge:" << chargeIntPart
398 //<< "Charge fractional part: " << chargeFracPart;
399
400
401 if(chargeFracPart >=
402 (1 - m_chargeMinimalFractionalPart /* that is >= 0.01 */) &&
403 chargeFracPart <= m_chargeMinimalFractionalPart /* that is <= 0.99 */)
404 {
405 m_context.m_lastZ = -1;
406 m_context.m_lastMz = std::numeric_limits<double>::min();
407 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
408 m_context.m_lastMr = std::numeric_limits<double>::min();
409
410 // qDebug() << "Not a charge state family peak,"
411 //<< "returning from deconvoluteChargeState";
412
413 return false;
414 }
415
416 if(chargeFracPart > m_chargeMinimalFractionalPart)
417 m_context.m_lastZ = chargeIntPart + 1;
418 else
419 m_context.m_lastZ = chargeIntPart;
420
421 // Now, to actually compute the molecular mass based on the charge and on
422 // the currently displayed m/z value, we need to have some thinking:
423
424 if(startMz < curMz)
425 {
426 // The drag was from left to right, that is curMz is greater than
427 // startMz. Fine, the z value is effectively the charge of the ion at
428 // curMz. Easy, no charge value modification here.
429 }
430 else
431 {
432 // The drag was from right to left, that is curMz is less than startMz.
433 // So we want to show the charge of the curMz, that is, z + span.
434 m_context.m_lastZ = m_context.m_lastZ + span;
435 }
436
437 m_context.m_lastMz = curMz;
438 m_context.m_lastMr =
439 (m_context.m_lastMz * m_context.m_lastZ) - (m_context.m_lastZ * MPROTON);
440
441 // qDebug() << "startMz:" << QString("%1").arg(startMz, 0, 'f', 6)
442 //<< "m_lastMz (curMz):"
443 //<< QString("%1").arg(m_context.m_lastMz, 0, 'f', 6)
444 //<< "m_lastMass:" << QString("%1").arg(m_context.m_lastMr, 0, 'f', 6)
445 //<< "m_lastZ:" << QString("%1").arg(m_context.m_lastZ);
446
447 // qDebug() << "returning true";
448
449 // The m_context was refreshed with the base class context in the calling
450 // chain.
452
453 return true;
454}
455
456
457//! Deconvolute the mass peaks into charge and molecular mass.
458/*!
459
460 This is one of two methods to deconvolute mass data into a charge value and
461 a Mr value. The method implemented in this function is based on the distance
462 that separates two immediately consecutive peaks of an isotopic cluster.
463 This method can be used as long as the instrument produced data with a
464 resolution sufficient to separate reasonably well the different peaks of an
465 isotopic cluster.
466
467 Set m_lastMz, m_lastZ and m_lastMass.
468
469 \return true if the deconvolution could be performed, false otherwise.
470 */
471bool
473{
474
475 if(m_context.m_xRegionRangeStart == m_context.m_xRegionRangeEnd)
476 {
477 // qDebug() << __FILE__ << __LINE__
478 //<< "Same xRegionRange.upper and xRegionRange.lower:"
479 //<< "returning from deconvoluteIsotopicCluster";
480
481 return false;
482 }
483
484 double chargeTemp = 1 / fabs(m_context.m_xDelta);
485
486 // Make a judicious roundup.
487 double chargeIntPart;
488 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
489
490 // qDebug() << "m_xDelta:" << m_context.m_baseContext.m_xDelta
491 //<< "chargeTemp:" << QString("%1").arg(chargeTemp, 0, 'f', 6)
492 //<< "chargeIntPart:" << chargeIntPart
493 //<< "chargeFracPart:" << QString("%1").arg(chargeFracPart, 0, 'f', 6)
494 //<< "m_chargeMinimalFractionalPart:" << m_chargeMinimalFractionalPart;
495
496 if(chargeFracPart >= (1 - m_chargeMinimalFractionalPart) &&
497 chargeFracPart <= m_chargeMinimalFractionalPart)
498 {
499 m_context.m_lastZ = -1;
500 m_context.m_lastMz = std::numeric_limits<double>::min();
501 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
502 m_context.m_lastMr = std::numeric_limits<double>::min();
503
504 // qDebug() << "Not in a isotopic cluster peak:"
505 //<< "returning from deconvoluteIsotopicCluster";
506
507 return false;
508 }
509
510 if(chargeFracPart > m_chargeMinimalFractionalPart)
511 {
512 m_context.m_lastZ = chargeIntPart + 1;
513
514 // qDebug() << "chargeFracPart > m_chargeMinimalFractionalPart -> m_lastZ
515 // = "
516 //<< m_context.m_lastZ;
517 }
518 else
519 {
520 m_context.m_lastZ = chargeIntPart;
521
522 // qDebug()
523 //<< "chargeFracPart <= m_chargeMinimalFractionalPart -> m_lastZ = "
524 //<< m_context.m_lastZ;
525 }
526
527 // Now that we have the charge in the form of an int, we can compute the
528 // Mr of the lightest isotopic cluster peak (the one that has the lowest x
529 // value). That value is stored in m_xRangeLower.
530
531 // We need to sort the xRegionRange before being certain that lower is indeed
532 // the left value of the drag span.
533
534 m_context.m_lastMz = std::min<double>(m_context.m_xRegionRangeStart,
535 m_context.m_xRegionRangeEnd);
536
537 m_context.m_lastMr =
538 (m_context.m_lastMz * m_context.m_lastZ) - (m_context.m_lastZ * MPROTON);
539
540 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
541 //<< "returning true";
542
543 // The m_context was refreshed with the base class context in the calling
544 // chain.
546
547 return true;
548}
549
550
551bool
553{
554
555 // m_xRangeLower and m_xRangeUpper and m_xDelta (in fabs() form) have been set
556 // during mouve movement handling. Note that the range values *are
557 // sorted*.
558
559 if(!m_context.m_xDelta || m_context.m_xDelta > 1)
560 {
561 m_context.m_lastResolvingPower = std::numeric_limits<double>::min();
562
563 return false;
564 }
565
566 // Resolving power is m/z / Delta(m/z), for singly-charged species.
567
568 // qDebug() << "Calculating the resolving power with these data:"
569 //<< "m_context.m_xRegionRangeStart: " << m_context.m_xRegionRangeStart
570 //<< "m_context.m_xRegionRangeEnd: " << m_context.m_xRegionRangeEnd
571 //<< "m_context.m_xDelta / 2: " << m_context.m_xDelta / 2;
572
573 m_context.m_lastResolvingPower =
574 (std::min<double>(m_context.m_xRegionRangeStart,
575 m_context.m_xRegionRangeEnd) +
576 (m_context.m_xDelta / 2)) /
577 m_context.m_xDelta;
578
579 // The m_context was refreshed with the base class context in the calling
580 // chain.
582
583 return true;
584}
585
586
587} // namespace pappso
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
bool deconvoluteChargedState(int span=1)
Deconvolute the mass peaks into charge and molecular mass.
void mousePressEventSignal(const MassSpecTracePlotContext &context)
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.
void mouseReleaseEventSignal(const MassSpecTracePlotContext &context)
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)