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
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
60 // qDebug() << "Data kind:" <<
61 // static_cast<int>(m_context.m_baseContext.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 DataKind::mz;
70
72
73 // qDebug() << "Data kind:" <<
74 // static_cast<int>(m_context.m_baseContext.m_dataKind);
75}
76
77
79{
80}
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{
105
106 // Before working on the various data belonging to the base context, we need
107 // to get it from the base class and refresh our local context with it.
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{
129
130 // Before working on the various data belonging to the base context, we need
131 // to get it from the base class and refresh our local context with it.
133}
134
135
136void
138{
140
141 // Before working on the various data belonging to the base context, we need
142 // to get it from the base class and refresh our local context with it.
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 {
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
173{
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}
180
181
182//! React to the release of the mouse buttons.
183void
185{
187
188 // Before working on the various data belonging to the base context, we need
189 // to get it from the base class and refresh our local context with it.
191}
192
193
196{
197 // BasePlotWidget has a member m_context of type BasePlotContext.
198 // Here we also have a m_context *distinct* member that is of type
199 // MassSpecTracePlotContext.
200
201 // While MassSpecTracePlotContext is derived from BasePlotContext, the two
202 // m_context members are distinct and there are lots of housekeeping data that
203 // are managed by the parent BasePlotWidget class in its m_context member
204 // *independently* of what we have in the ::BasePlotContext part of our
205 // m_context member that is of type MassSpecTracePlotContext. We thus need to
206 // resynchronize the data from BasePlotWidget::m_context to our m_context.
207
209
210 return m_context;
211}
212
213
214void
216 double charge_fractional_part)
217{
218 m_chargeMinimalFractionalPart = charge_fractional_part;
219}
220
221
222double
224{
226}
227
228
229void
231{
233}
234
235
236int
238{
240}
241
242
243//! Deconvolute the mass peaks into charge and molecular mass.
244bool
246{
247
248 // There are two situations: when the user is deconvoluting on the
249 // basis of the distance between two consecutive peaks of a same
250 // isotopic cluster or when the user deconvolutes on the basis of two
251 // different charged-stated peaks that belong to the same envelope.
252
253 // We can tell the difference because in the first case the xDelta
254 // should be less than 1. In the other case, of course the difference
255 // is much greater than 1.
256
257 // In order to do the deconvolutions, we need to know what is the tolerance
258 // on the fractional part of the deconvoluted charge value. This value is set
259 // in the parent window's double spin box.
260
261 if(fabs(m_context.m_xDelta) >= 0 && fabs(m_context.m_xDelta) <= 1.1)
262 {
263 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
264 //<< "m_xDelta:" << m_context.m_baseContext.m_xDelta
265 //<< "trying isotope-based deconvolution.";
266
268 }
269
270 // If not deconvoluting on the basis of the isotopic cluster, then:
271
273}
274
275
276//! Deconvolute the mass peaks into charge and molecular mass.
277/*!
278
279 This is one of two methods to deconvolute mass data into a charge value and
280 a Mr value. The method implemented in this function is based on the charge
281 state envelope offered by the mass spectrum (most often for polymers of a
282 reasonable size).
283
284 \param span value representing the number of peaks of the charge state
285 envelope that are spanned by the user selection. Defaults to 1, that is, the
286 span encompasses two \e consecutive mass peaks of a given charge state
287 envelope.
288
289 Set m_lastMz, m_lastZ and m_lastMass.
290
291 \return true if the deconvolution could be performed, false otherwise.
292 */
293bool
295{
296 // We assume that we are dealing with two successive (if span is 1) mass
297 // peaks belonging to a given charge state family.
298
299 // We call span the number of intervals in a given charge state envelope
300 // that separate the initial peak (lowerMz) from the last peak (upperMz).
301 // That parameter defaults to 1, that is the two peaks are immediately
302 // consecutive, that is, there is only one interval.
303
304 // We use the m_contex.basecontext.m_xRegionRange structure that is unsorted.
305 // That is, lower is the start drag point.x and upper is the current drag
306 // point.x. If dragging occurs from left to right, start.x < cur.x.
307 // We use the unsorted values, because we need to know in which direction
308 // the user has drug the mouse, because we want to provide the Mr value
309 // for the peak currently under the mouse cursor, that is under
310 // currentDragPoint, that is the value in
311 // m_context.m_baseContext.m_xRegionRange.upper.
312
313 double startMz = m_context.m_xRegionRangeStart;
314 double curMz = m_context.m_xRegionRangeEnd;
315
316 // qDebug() << "startMz:" << startMz << "curMz:" << curMz;
317
318 if(startMz == curMz)
319 {
320 m_context.m_lastZ = -1;
321 m_context.m_lastMz = std::numeric_limits<double>::min();
322 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
323 m_context.m_lastMr = std::numeric_limits<double>::min();
324
325 return false;
326 }
327
328 // We need to be aware that the status bar of the window that contains
329 // this plot widget shows the cursor position realtime, and that cursor
330 // position is the m_currentDragPoint.x value, that is, curMz. Thus, we need
331 // to make the calculations with the charge being the one of the polymer under
332 // the cursor position. This is tricky because it changes when the user
333 // switches drag senses: from left to right and right to left.
334 // The way z is calculated always makes it the charge of the highest mz
335 // value. So knowing this, depending on the drag direction we'll have to take
336 // curMz and apply to it either z charge (left to right drag) or (z+span)
337 // charge (right to left).
338
339 // Make sure lower is actually lower, even if drag is from right to left.
340 // This is only to have a single charge calculation.
341 double lowerMz;
342 double upperMz;
343
344 if(startMz < curMz)
345 {
346 lowerMz = startMz;
347 upperMz = curMz;
348 }
349 else
350 {
351 lowerMz = curMz;
352 upperMz = startMz;
353 }
354
355 double chargeTemp = ((lowerMz * span) - span) / (upperMz - lowerMz);
356
357 // Make a judicious roundup.
358
359 double chargeIntPart;
360 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
361
362 // When calculating the charge of the ion, very rarely does it provide a
363 // perfect integer value. Most often (if deconvolution is for bona fide
364 // peaks belonging to the same charge state envelope) that value is with
365 // either a large fractional part or a very small fractional part. What we
366 // test here, it that fractional part. If it is greater than
367 // m_chargeMinimalFractionalPart, then we simply round up to the next integer
368 // value (that is, chargeIntPart = 27 and chargeFracPart 0.995, then we
369 // set charge to 28). If it is lesser or equal to (1 -
370 // m_chargeMinimalFractionalPart /* that is >= 0.01 */, then we let
371 // chargeIntPart unmodified (that is, chargeIntPart = 29 and
372 // chargeFracPart 0.01, then we set charge to 29). If chargeFracPart is in
373 // between (1 - m_chargeMinimalFractionalPart) and
374 // m_chargeMinimalFractionalPart, then we consider that the peaks do not
375 // belong to the same charge state envelope.
376
377 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
378 //<< "Charge:" << chargeIntPart
379 //<< "Charge fractional part: " << chargeFracPart;
380
381
382 if(chargeFracPart >=
383 (1 - m_chargeMinimalFractionalPart /* that is >= 0.01 */) &&
384 chargeFracPart <= m_chargeMinimalFractionalPart /* that is <= 0.99 */)
385 {
386 m_context.m_lastZ = -1;
387 m_context.m_lastMz = std::numeric_limits<double>::min();
388 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
389 m_context.m_lastMr = std::numeric_limits<double>::min();
390
391 // qDebug() << __FILE__ << __LINE__
392 //<< "Not a charge state family peak,"
393 //<< "returning from deconvoluteChargeState";
394
395 return false;
396 }
397
398 if(chargeFracPart > m_chargeMinimalFractionalPart)
399 m_context.m_lastZ = chargeIntPart + 1;
400 else
401 m_context.m_lastZ = chargeIntPart;
402
403 // Now, to actually compute the molecular mass based on the charge and on
404 // the currently displayed m/z value, we need to have some thinking:
405
406 if(startMz < curMz)
407 {
408 // The drag was from left to right, that is curMz is greater than
409 // startMz. Fine, the z value is effectively the charge of the ion at
410 // curMz. Easy, no charge value modification here.
411 }
412 else
413 {
414 // The drag was from right to left, that is curMz is less than startMz.
415 // So we want to show the charge of the curMz, that is, z + span.
417 }
418
419 m_context.m_lastMz = curMz;
422
423 // qDebug() << __FILE__ << __LINE__
424 //<< "startMz:" << QString("%1").arg(startMz, 0, 'f', 6)
425 //<< "m_lastMz (curMz):"
426 //<< QString("%1").arg(m_context.m_lastMz, 0, 'f', 6)
427 //<< "m_lastMass:" << QString("%1").arg(m_context.m_lastMr, 0, 'f', 6)
428 //<< "m_lastZ:" << QString("%1").arg(m_context.m_lastZ);
429
430 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
431 //<< "returning true";
432
433 // The m_context was refreshed with the base class context in the calling
434 // chain.
436
437 return true;
438}
439
440
441//! Deconvolute the mass peaks into charge and molecular mass.
442/*!
443
444 This is one of two methods to deconvolute mass data into a charge value and
445 a Mr value. The method implemented in this function is based on the distance
446 that separates two immediately consecutive peaks of an isotopic cluster.
447 This method can be used as long as the instrument produced data with a
448 resolution sufficient to separate reasonably well the different peaks of an
449 isotopic cluster.
450
451 Set m_lastMz, m_lastZ and m_lastMass.
452
453 \return true if the deconvolution could be performed, false otherwise.
454 */
455bool
457{
458
460 {
461 // qDebug() << __FILE__ << __LINE__
462 //<< "Same xRegionRange.upper and xRegionRange.lower:"
463 //<< "returning from deconvoluteIsotopicCluster";
464
465 return false;
466 }
467
468 double chargeTemp = 1 / fabs(m_context.m_xDelta);
469
470 // Make a judicious roundup.
471 double chargeIntPart;
472 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
473
474 // qDebug() << "m_xDelta:" << m_context.m_baseContext.m_xDelta
475 //<< "chargeTemp:" << QString("%1").arg(chargeTemp, 0, 'f', 6)
476 //<< "chargeIntPart:" << chargeIntPart
477 //<< "chargeFracPart:" << QString("%1").arg(chargeFracPart, 0, 'f', 6)
478 //<< "m_chargeMinimalFractionalPart:" << m_chargeMinimalFractionalPart;
479
480 if(chargeFracPart >= (1 - m_chargeMinimalFractionalPart) &&
481 chargeFracPart <= m_chargeMinimalFractionalPart)
482 {
483 m_context.m_lastZ = -1;
484 m_context.m_lastMz = std::numeric_limits<double>::min();
485 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
486 m_context.m_lastMr = std::numeric_limits<double>::min();
487
488 // qDebug() << "Not in a isotopic cluster peak:"
489 //<< "returning from deconvoluteIsotopicCluster";
490
491 return false;
492 }
493
494 if(chargeFracPart > m_chargeMinimalFractionalPart)
495 {
496 m_context.m_lastZ = chargeIntPart + 1;
497
498 // qDebug() << "chargeFracPart > m_chargeMinimalFractionalPart -> m_lastZ
499 // = "
500 //<< m_context.m_lastZ;
501 }
502 else
503 {
504 m_context.m_lastZ = chargeIntPart;
505
506 // qDebug()
507 //<< "chargeFracPart <= m_chargeMinimalFractionalPart -> m_lastZ = "
508 //<< m_context.m_lastZ;
509 }
510
511 // Now that we have the charge in the form of an int, we can compute the
512 // Mr of the lightest isotopic cluster peak (the one that has the lowest x
513 // value). That value is stored in m_xRangeLower.
514
515 // We need to sort the xRegionRange before being certain that lower is indeed
516 // the left value of the drag span.
517
520
523
524 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
525 //<< "returning true";
526
527 // The m_context was refreshed with the base class context in the calling
528 // chain.
530
531 return true;
532}
533
534
535bool
537{
538
539 // m_xRangeLower and m_xRangeUpper and m_xDelta (in fabs() form) have been set
540 // during mouve movement handling. Note that the range values *are
541 // sorted*.
542
544 {
545 m_context.m_lastResolvingPower = std::numeric_limits<double>::min();
546
547 return false;
548 }
549
550 // Resolving power is m/z / Delta(m/z), for singly-charged species.
551
553 (std::min<double>(m_context.m_xRegionRangeStart,
555 (m_context.m_xDelta / 2)) /
557
558 // The m_context was refreshed with the base class context in the calling
559 // chain.
561
562 return true;
563}
564
565
566} // 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
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)