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 
52 namespace 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.
84 void
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.
101 void
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  */
125 void
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 
136 void
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 
147 void
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.
171 void
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.
183 void
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 
214 void
216  double charge_fractional_part)
217 {
218  m_chargeMinimalFractionalPart = charge_fractional_part;
219 }
220 
221 
222 double
224 {
226 }
227 
228 
229 void
231 {
233 }
234 
235 
236 int
238 {
240 }
241 
242 
243 //! Deconvolute the mass peaks into charge and molecular mass.
244 bool
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  */
293 bool
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  */
455 bool
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 
518  m_context.m_lastMz = std::min<double>(m_context.m_xRegionRangeStart,
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 
535 bool
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)