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() << "m_xDelta:" << m_context.m_xDelta
264 //<< "trying isotope-based deconvolution.";
265
267 }
268
269 // If not deconvoluting on the basis of the isotopic cluster, then:
270
272}
273
274
275//! Deconvolute the mass peaks into charge and molecular mass.
276/*!
277
278 This is one of two methods to deconvolute mass data into a charge value and
279 a Mr value. The method implemented in this function is based on the charge
280 state envelope offered by the mass spectrum (most often for polymers of a
281 reasonable size).
282
283 \param span value representing the number of peaks of the charge state
284 envelope that are spanned by the user selection. Defaults to 1, that is, the
285 span encompasses two \e consecutive mass peaks of a given charge state
286 envelope.
287
288 Set m_lastMz, m_lastZ and m_lastMass.
289
290 \return true if the deconvolution could be performed, false otherwise.
291 */
292bool
294{
295 // We assume that we are dealing with two successive (if span is 1) mass
296 // peaks belonging to a given charge state family.
297
298 // We call span the number of intervals in a given charge state envelope
299 // that separate the initial peak (lowerMz) from the last peak (upperMz).
300 // That parameter defaults to 1, that is the two peaks are immediately
301 // consecutive, that is, there is only one interval.
302
303 // We use the m_contex.basecontext.m_xRegionRange structure that is unsorted.
304 // That is, lower is the start drag point.x and upper is the current drag
305 // point.x. If dragging occurs from left to right, start.x < cur.x.
306 // We use the unsorted values, because we need to know in which direction
307 // the user has drug the mouse, because we want to provide the Mr value
308 // for the peak currently under the mouse cursor, that is under
309 // currentDragPoint, that is the value in
310 // m_context.m_baseContext.m_xRegionRange.upper.
311
312 double startMz = m_context.m_xRegionRangeStart;
313 double curMz = m_context.m_xRegionRangeEnd;
314
315 // qDebug() << "startMz:" << startMz << "curMz:" << curMz;
316
317 if(startMz == curMz)
318 {
319 m_context.m_lastZ = -1;
320 m_context.m_lastMz = std::numeric_limits<double>::min();
321 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
322 m_context.m_lastMr = std::numeric_limits<double>::min();
323
324 return false;
325 }
326
327 // We need to be aware that the status bar of the window that contains
328 // this plot widget shows the cursor position realtime, and that cursor
329 // position is the m_currentDragPoint.x value, that is, curMz. Thus, we need
330 // to make the calculations with the charge being the one of the polymer under
331 // the cursor position. This is tricky because it changes when the user
332 // switches drag senses: from left to right and right to left.
333 // The way z is calculated always makes it the charge of the highest mz
334 // value. So knowing this, depending on the drag direction we'll have to take
335 // curMz and apply to it either z charge (left to right drag) or (z+span)
336 // charge (right to left).
337
338 // Make sure lower is actually lower, even if drag is from right to left.
339 // This is only to have a single charge calculation.
340 double lowerMz;
341 double upperMz;
342
343 if(startMz < curMz)
344 {
345 lowerMz = startMz;
346 upperMz = curMz;
347 }
348 else
349 {
350 lowerMz = curMz;
351 upperMz = startMz;
352 }
353
354 double chargeTemp = ((lowerMz * span) - span) / (upperMz - lowerMz);
355
356 // Make a judicious roundup.
357
358 double chargeIntPart;
359 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
360
361 // When calculating the charge of the ion, very rarely does it provide a
362 // perfect integer value. Most often (if deconvolution is for bona fide
363 // peaks belonging to the same charge state envelope) that value is with
364 // either a large fractional part or a very small fractional part. What we
365 // test here, it that fractional part. If it is greater than
366 // m_chargeMinimalFractionalPart, then we simply round up to the next integer
367 // value (that is, chargeIntPart = 27 and chargeFracPart 0.995, then we
368 // set charge to 28). If it is lesser or equal to (1 -
369 // m_chargeMinimalFractionalPart /* that is >= 0.01 */, then we let
370 // chargeIntPart unmodified (that is, chargeIntPart = 29 and
371 // chargeFracPart 0.01, then we set charge to 29). If chargeFracPart is in
372 // between (1 - m_chargeMinimalFractionalPart) and
373 // m_chargeMinimalFractionalPart, then we consider that the peaks do not
374 // belong to the same charge state envelope.
375
376 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__
377 //<< "Charge:" << chargeIntPart
378 //<< "Charge fractional part: " << chargeFracPart;
379
380
381 if(chargeFracPart >=
382 (1 - m_chargeMinimalFractionalPart /* that is >= 0.01 */) &&
383 chargeFracPart <= m_chargeMinimalFractionalPart /* that is <= 0.99 */)
384 {
385 m_context.m_lastZ = -1;
386 m_context.m_lastMz = std::numeric_limits<double>::min();
387 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
388 m_context.m_lastMr = std::numeric_limits<double>::min();
389
390 // qDebug() << "Not a charge state family peak,"
391 //<< "returning from deconvoluteChargeState";
392
393 return false;
394 }
395
396 if(chargeFracPart > m_chargeMinimalFractionalPart)
397 m_context.m_lastZ = chargeIntPart + 1;
398 else
399 m_context.m_lastZ = chargeIntPart;
400
401 // Now, to actually compute the molecular mass based on the charge and on
402 // the currently displayed m/z value, we need to have some thinking:
403
404 if(startMz < curMz)
405 {
406 // The drag was from left to right, that is curMz is greater than
407 // startMz. Fine, the z value is effectively the charge of the ion at
408 // curMz. Easy, no charge value modification here.
409 }
410 else
411 {
412 // The drag was from right to left, that is curMz is less than startMz.
413 // So we want to show the charge of the curMz, that is, z + span.
415 }
416
417 m_context.m_lastMz = curMz;
420
421 // qDebug() << "startMz:" << QString("%1").arg(startMz, 0, 'f', 6)
422 //<< "m_lastMz (curMz):"
423 //<< QString("%1").arg(m_context.m_lastMz, 0, 'f', 6)
424 //<< "m_lastMass:" << QString("%1").arg(m_context.m_lastMr, 0, 'f', 6)
425 //<< "m_lastZ:" << QString("%1").arg(m_context.m_lastZ);
426
427 // qDebug() << "returning true";
428
429 // The m_context was refreshed with the base class context in the calling
430 // chain.
432
433 return true;
434}
435
436
437//! Deconvolute the mass peaks into charge and molecular mass.
438/*!
439
440 This is one of two methods to deconvolute mass data into a charge value and
441 a Mr value. The method implemented in this function is based on the distance
442 that separates two immediately consecutive peaks of an isotopic cluster.
443 This method can be used as long as the instrument produced data with a
444 resolution sufficient to separate reasonably well the different peaks of an
445 isotopic cluster.
446
447 Set m_lastMz, m_lastZ and m_lastMass.
448
449 \return true if the deconvolution could be performed, false otherwise.
450 */
451bool
453{
454
456 {
457 // qDebug() << __FILE__ << __LINE__
458 //<< "Same xRegionRange.upper and xRegionRange.lower:"
459 //<< "returning from deconvoluteIsotopicCluster";
460
461 return false;
462 }
463
464 double chargeTemp = 1 / fabs(m_context.m_xDelta);
465
466 // Make a judicious roundup.
467 double chargeIntPart;
468 double chargeFracPart = modf(chargeTemp, &chargeIntPart);
469
470 // qDebug() << "m_xDelta:" << m_context.m_baseContext.m_xDelta
471 //<< "chargeTemp:" << QString("%1").arg(chargeTemp, 0, 'f', 6)
472 //<< "chargeIntPart:" << chargeIntPart
473 //<< "chargeFracPart:" << QString("%1").arg(chargeFracPart, 0, 'f', 6)
474 //<< "m_chargeMinimalFractionalPart:" << m_chargeMinimalFractionalPart;
475
476 if(chargeFracPart >= (1 - m_chargeMinimalFractionalPart) &&
477 chargeFracPart <= m_chargeMinimalFractionalPart)
478 {
479 m_context.m_lastZ = -1;
480 m_context.m_lastMz = std::numeric_limits<double>::min();
481 m_context.m_lastTicIntensity = std::numeric_limits<double>::min();
482 m_context.m_lastMr = std::numeric_limits<double>::min();
483
484 // qDebug() << "Not in a isotopic cluster peak:"
485 //<< "returning from deconvoluteIsotopicCluster";
486
487 return false;
488 }
489
490 if(chargeFracPart > m_chargeMinimalFractionalPart)
491 {
492 m_context.m_lastZ = chargeIntPart + 1;
493
494 // qDebug() << "chargeFracPart > m_chargeMinimalFractionalPart -> m_lastZ
495 // = "
496 //<< m_context.m_lastZ;
497 }
498 else
499 {
500 m_context.m_lastZ = chargeIntPart;
501
502 // qDebug()
503 //<< "chargeFracPart <= m_chargeMinimalFractionalPart -> m_lastZ = "
504 //<< m_context.m_lastZ;
505 }
506
507 // Now that we have the charge in the form of an int, we can compute the
508 // Mr of the lightest isotopic cluster peak (the one that has the lowest x
509 // value). That value is stored in m_xRangeLower.
510
511 // We need to sort the xRegionRange before being certain that lower is indeed
512 // the left value of the drag span.
513
516
519
520 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
521 //<< "returning true";
522
523 // The m_context was refreshed with the base class context in the calling
524 // chain.
526
527 return true;
528}
529
530
531bool
533{
534
535 // m_xRangeLower and m_xRangeUpper and m_xDelta (in fabs() form) have been set
536 // during mouve movement handling. Note that the range values *are
537 // sorted*.
538
540 {
541 m_context.m_lastResolvingPower = std::numeric_limits<double>::min();
542
543 return false;
544 }
545
546 // Resolving power is m/z / Delta(m/z), for singly-charged species.
547
548 // qDebug() << "Calculating the resolving power with these data:"
549 //<< "m_context.m_xRegionRangeStart: " << m_context.m_xRegionRangeStart
550 //<< "m_context.m_xRegionRangeEnd: " << m_context.m_xRegionRangeEnd
551 //<< "m_context.m_xDelta / 2: " << m_context.m_xDelta / 2;
552
554 (std::min<double>(m_context.m_xRegionRangeStart,
556 (m_context.m_xDelta / 2)) /
558
559 // The m_context was refreshed with the base class context in the calling
560 // chain.
562
563 return true;
564}
565
566
567} // 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)