libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
selectionpolygon.cpp
Go to the documentation of this file.
1// Copyright 2021 Filippo Rusconi
2// GPLv3+
3
4
5/////////////////////// StdLib includes
6#include <limits>
7#include <cmath>
8
9
10/////////////////////// Qt includes
11#include <QDebug>
12
13
14/////////////////////// Local includes
15#include "selectionpolygon.h"
16
17
18namespace pappso
19{
20
22{
23 // When we create a polygon, we create it as immense as possible, so that
24 // if this polygon is not modified, any other polygon created on the basis
25 // of experimental data will fit inside it.
26
27 // See the definition of the points in the header file.
28}
29
30
31SelectionPolygon::SelectionPolygon(QPointF top_left_point, QPointF top_right_point)
32{
33 // First clear the default values points because we want to push_back
34 // new points and we want to only ever have 4 points.
35 m_points.clear();
36
37 // We get only two points that provide the horizontal range of the polygon.
38 // These two points show the x range of the polygon. We need to craft a
39 // polygon that has:
40 //
41 // that specified x range and
42 //
43 // the widest y range possible.
44
45 // In other words, we are crafting a 1D selection polygon.
46
47 // top left point
48 m_points.push_back(QPointF(top_left_point.x(), std::numeric_limits<double>::max()));
49
50 // top right point
51 m_points.push_back(QPointF(top_right_point.x(), std::numeric_limits<double>::max()));
52
53 // bottom right point
54 m_points.push_back(QPointF(top_right_point.x(), std::numeric_limits<double>::min()));
55
56 // bottom left point
57 m_points.push_back(QPointF(top_left_point.x(), std::numeric_limits<double>::min()));
58
59 // Compute the min|max x|y coordinates of the polygon that will be used to
60 // quickly check if a point is outside.
62}
63
64
66 QPointF top_right_point,
67 QPointF bottom_right_point,
68 QPointF bottom_left_point)
69{
70 // First clear the default values points.
71 m_points.clear();
72
73 // Attention, we need to push back the points starting top left and clockwise.
74
75 m_points.push_back(top_left_point);
76 m_points.push_back(top_right_point);
77 m_points.push_back(bottom_right_point);
78 m_points.push_back(bottom_left_point);
79
80 // Compute the min|max x|y coordinates of the polygon that will be used to
81 // quickly check if a point is outside.
83}
84
85
87{
88 if(other.m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
89 qFatal("The template selection polygon must have four points, no less, no more");
90
91 // First clear the default values points.
92 m_points.clear();
93
94 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
95 {
96 m_points.push_back(other.m_points[iter]);
97 }
98
99 m_minX = other.m_minX;
100 m_minY = other.m_minY;
101
102 m_maxX = other.m_maxX;
103 m_maxY = other.m_maxY;
104}
105
106
110
111
112void
113SelectionPolygon::setPoint(PointSpec point_spec, double x, double y)
114{
115 m_points[static_cast<int>(point_spec)].setX(x);
116 m_points[static_cast<int>(point_spec)].setY(y);
117
119}
120
121
122void
123SelectionPolygon::setPoint(PointSpec point_spec, QPointF point)
124{
125 setPoint(point_spec, point.x(), point.y());
126
128}
129
130
131void
132SelectionPolygon::copyPoint(PointSpec point_spec_src, PointSpec point_spec_dest)
133{
134 QPointF src_point = getPoint(point_spec_src);
135 setPoint(point_spec_dest, src_point);
136
138}
139
140
141void
142SelectionPolygon::set1D(double x_range_start, double x_range_end)
143{
144 // We get only two points that provide the horizontal range of the polygon.
145 // These two points show the x range of the polygon. We need to craft a
146 // polygon that has:
147 //
148 // that specified x range and
149 //
150 // the widest y range possible.
151
152 resetPoints();
153
154 // top left point
155 setPoint(PointSpec::TOP_LEFT_POINT, QPointF(x_range_start, std::numeric_limits<double>::max()));
156
157 // top right point
158 setPoint(PointSpec::TOP_RIGHT_POINT, QPointF(x_range_end, std::numeric_limits<double>::max()));
159
160 // bottom right point
161 setPoint(PointSpec::BOTTOM_RIGHT_POINT, QPointF(x_range_end, std::numeric_limits<double>::min()));
162
163 // bottom left point
165 QPointF(x_range_start, std::numeric_limits<double>::min()));
166
167 // Compute the min|max x|y coordinates of the polygon that will be used to
168 // quickly check if a point is outside.
170}
171
172
173void
174SelectionPolygon::set2D(QPointF top_left,
175 QPointF top_right,
176 QPointF bottom_right,
177 QPointF bottom_left)
178{
179 resetPoints();
180
181 // top left point
183 // qDebug() << "PointSpecs::TOP_LEFT_POINT:" << top_left;
184
185 // top right point
187 // qDebug() << "PointSpecs::TOP_RIGHT_POINT:" << top_right;
188
189 // bottom right point
191 // qDebug() << "PointSpecs::BOTTOM_RIGHT_POINT:" << bottom_right;
192
193 // bottom left point
195 // qDebug() << "PointSpecs::BOTTOM_LEFT_POINT:" << bottom_left;
196
197 // Compute the min|max x|y coordinates of the polygon that will be used to
198 // quickly check if a point is outside.
200
201 // qDebug() << toString();
202}
203
204
205void
207{
208 // When a 2D polygon is converted to a 1D polygon, the x axis range is
209 // unchanged, but the height is set to its maximum possible with the bottom
210 // line at y = min and the top line at y = max.
211
213
215}
216
217
218QPointF
220{
221 // When we say leftmost, that means that we are implicitely interesed in
222 // x-axis coordinate of the points.
223
224 QPointF temp_point(std::numeric_limits<double>::max(), 0);
225
226 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
227 {
228 if(m_points[iter].x() < temp_point.x())
229 {
230 temp_point = m_points[iter];
231 }
232 }
233
234 return temp_point;
235}
236
237
238QPointF
240{
241 // When we say rightmost, that means that we are implicitely interesed in
242 // x-axis coordinate of the points.
243
244 QPointF temp_point(std::numeric_limits<double>::min(), 0);
245
246 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
247 {
248 if(m_points[iter].x() > temp_point.x())
249 {
250 temp_point = m_points[iter];
251 }
252 }
253
254 return temp_point;
255}
256
257
258QPointF
260{
261 // When we say topmost or bottommost , that means that we are implicitely
262 // interesed in y-axis coordinate of the points.
263
264 QPointF temp_point(0, std::numeric_limits<double>::min());
265
266 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
267 {
268 if(m_points[iter].y() > temp_point.y())
269 {
270 temp_point = m_points[iter];
271 }
272 }
273
274 return temp_point;
275}
276
277
278QPointF
280{
281 // When we say topmost or bottommost , that means that we are implicitely
282 // interesed in y-axis coordinate of the points.
283
284 QPointF temp_point(0, std::numeric_limits<double>::max());
285
286 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
287 {
288 if(m_points[iter].y() < temp_point.y())
289 {
290 temp_point = m_points[iter];
291 }
292 }
293
294 return temp_point;
295}
296
297
298const std::vector<QPointF> &
300{
301 return m_points;
302}
303
304
305QPointF
307{
308 return m_points[static_cast<int>(point_spec)];
309}
310
311
312bool
314{
315 // Set the variable to starting values that allow easy value comparisons with
316 // std::min() and std::max() for checking the x|y values below.
317
318 m_minX = std::numeric_limits<double>::max();
319 m_minY = std::numeric_limits<double>::max();
320 m_maxX = std::numeric_limits<double>::min();
321 m_maxY = std::numeric_limits<double>::min();
322
323 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
324 {
325 m_minX = std::min(m_points.at(iter).x(), m_minX);
326 m_maxX = std::max(m_points.at(iter).x(), m_maxX);
327
328 m_minY = std::min(m_points.at(iter).y(), m_minY);
329 m_maxY = std::max(m_points.at(iter).y(), m_maxY);
330 }
331
332 return true;
333}
334
335
336bool
338 double &max_x,
339 double &min_y,
340 double &max_y) const
341{
342 // Set the variable to starting values that allow easy value comparisons with
343 // std::min() and std::max() for checking the x|y values below.
344
345 min_x = std::numeric_limits<double>::max();
346 min_y = std::numeric_limits<double>::max();
347 max_x = std::numeric_limits<double>::min();
348 max_y = std::numeric_limits<double>::min();
349
350 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
351 {
352 min_x = std::min(m_points.at(iter).x(), min_x);
353 max_x = std::max(m_points.at(iter).x(), max_x);
354
355 min_y = std::min(m_points.at(iter).y(), min_y);
356 max_y = std::max(m_points.at(iter).y(), max_y);
357 }
358
359 // qDebug() << "min_x:" << min_x << "max_x:" << max_x << "min_y:" << min_y
360 //<< "max_y:" << max_y;
361
362 return true;
363}
364
365
366double
368{
369 double min_x;
370 double min_y;
371 double max_x;
372 double max_y;
373
374 computeMinMaxCoordinates(min_x, max_x, min_y, max_y);
375
376 ok = true;
377 return max_x - min_x;
378}
379
380
381double
383{
384 double min_x;
385 double min_y;
386 double max_x;
387 double max_y;
388
389 computeMinMaxCoordinates(min_x, max_x, min_y, max_y);
390
391 ok = true;
392 return max_y - min_y;
393}
394
395
396bool
397SelectionPolygon::rangeX(double &range_start, double &range_end) const
398{
399 double min_y = std::numeric_limits<double>::max();
400 double max_y = std::numeric_limits<double>::min();
401
402 return computeMinMaxCoordinates(range_start, range_end, min_y, max_y);
403}
404
405
406bool
407SelectionPolygon::rangeY(double &range_start, double &range_end) const
408{
409 double min_x = std::numeric_limits<double>::max();
410 double max_x = std::numeric_limits<double>::min();
411
412 return computeMinMaxCoordinates(min_x, max_x, range_start, range_end);
413}
414
415
416bool
417SelectionPolygon::range(Enums::Axis axis, double &range_start, double &range_end) const
418{
419 if(axis == Enums::Axis::x)
420 return rangeX(range_start, range_end);
421 else if(axis == Enums::Axis::y)
422 return rangeY(range_start, range_end);
423
424 return false;
425}
426
427
428// All this is valid only if the polygon has four sides.
429//
430// Transposing means that we take each point and permute x with y.
431// During transposition, the TOP_RIGHT_POINT BOTTOM_LEFT_POINT are invariant.
432//
433// Transposition is like rotating the polygon arount the
434// BOTTOM_LEFT_POINT--TOP_RIGHT_POINT axis, which make them invariant and
435// permutates TOP_LEFT_POINT with BOTTOM_RIGHT_POINT.
436//
437// If iteration in the points is clockwise before, iteration below
438// counter-clockwise after transposition, that is:
439// TOP_LEFT_POINT becomes BOTTOM_RIGHT_POINT
440//
443{
444 SelectionPolygon selection_polygon;
445
446 // Make sure we do this for a polygon with four sides.
447
448 if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
449 qFatal("The polygon must have four points, no less, no more");
450
451 // The two invariant points, that is, the two points that do no change
452 // position in the polygon corners. Of course, x becomes y.
453 selection_polygon.setPoint(
456
457 selection_polygon.setPoint(PointSpec::BOTTOM_LEFT_POINT,
460
461 // The two other points.
462
463 selection_polygon.setPoint(
466
467 selection_polygon.setPoint(PointSpec::TOP_LEFT_POINT,
470
471 return selection_polygon;
472}
473
474
475bool
476SelectionPolygon::contains(const QPointF &tested_point) const
477{
478 // Easy check: if the point lies outside of most external limits of the
479 // polygon, return false.
480
481 if(tested_point.x() < m_minX || tested_point.x() > m_maxX || tested_point.y() < m_minY ||
482 tested_point.y() > m_maxY)
483 {
484 // qDebug() << "Testing point:" << tested_point
485 //<< "aginst polygon:" << toString()
486 //<< "is out of x and y ranges.";
487 return false;
488 }
489
490 // There are two situations:
491 //
492 // 1. The selection polygon is a rectangle, we can check the tested_point very
493 // easily.
494 //
495 // 2. The selection polygon is a skewed rectangle, that is, it is a
496 // parallelogram, we need to really use the point-in-polygon algorithm.
497
498 if(isRectangle())
499 {
500 // qDebug() << "Selection polygon *is* rectangle.";
501
502 double x = tested_point.x();
503 double y = tested_point.y();
504
505 // return (x >= getPoint(PointSpecs::TOP_LEFT_POINT).x() &&
506 // x <= getPoint(PointSpecs::TOP_RIGHT_POINT).x() &&
507 // y >= getPoint(PointSpecs::BOTTOM_LEFT_POINT).y() &&
508 // y <= getPoint(PointSpecs::TOP_LEFT_POINT).y());
509
510 bool res = x >= m_minX && x <= m_maxX && y >= m_minY && y <= m_maxY;
511
512 // qDebug() << qSetRealNumberPrecision(10) << "Returning: " << res
513 //<< "for point:" << tested_point
514 //<< "and selection polygon:" << toString();
515
516 return res;
517 }
518
519 // qDebug() << "Testing point:" << tested_point
520 //<< "aginst polygon:" << toString()
521 //<< "is tested against a skewed selection polygon rectangle.";
522
523 // At this point, we know the selection polygon is not rectangle, we have to
524 // make the real check using the point-in-polygon algorithm.
525
526 // This code is inspired by the work described here:
527 // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
528
529 // int pnpoly(int vertex_count, float *vertx, float *verty, float testx,
530 // float testy)
531
532 int i = 0;
533 int j = 0;
534 bool is_inside = false;
535
536 int vertex_count = m_points.size();
537
538 for(i = 0, j = vertex_count - 1; i < vertex_count; j = i++)
539 {
540 if(((m_points.at(i).y() > tested_point.y()) != (m_points.at(j).y() > tested_point.y())) &&
541 (tested_point.x() < (m_points.at(j).x() - m_points.at(i).x()) *
542 (tested_point.y() - m_points.at(i).y()) /
543 (m_points.at(j).y() - m_points.at(i).y()) +
544 m_points.at(i).x()))
545 is_inside = !is_inside;
546 }
547
548 // if(is_inside)
549 // qDebug() << "Testing point:" << tested_point
550 //<< "aginst polygon:" << toString() << "turns out be in.";
551 // else
552 // qDebug() << "Testing point:" << tested_point
553 //<< "aginst polygon:" << toString() << "turns out be out.";
554
555 return is_inside;
556}
557
558
559bool
560SelectionPolygon::contains(const SelectionPolygon &selection_polygon) const
561{
562 // A polygon is inside another polygon if all its points are inside the
563 // polygon.
564
565 bool is_inside = true;
566
567 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
568 {
569 if(!contains(selection_polygon.getPoint(static_cast<PointSpec>(iter))))
570 is_inside = false;
571 }
572
573 return is_inside;
574}
575
576
579{
580 if(this == &other)
581 return *this;
582
583 if(other.m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
584 qFatal("Programming error.");
585
586 if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
587 qFatal("Programming error.");
588
589 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
590 m_points[iter] = other.m_points[iter];
591
592 m_minX = other.m_minX;
593 m_minY = other.m_minY;
594
595 m_maxX = other.m_maxX;
596 m_maxY = other.m_maxY;
597
598 return *this;
599}
600
601
602void
604{
605 // Reset the points exactly as they were set upon construction of an empty
606 // polygon.
607
608 m_points[0] = QPointF(std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
609 m_points[0] = QPointF(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
610 m_points[0] = QPointF(std::numeric_limits<double>::max(), std::numeric_limits<double>::min());
611 m_points[0] = QPointF(std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
612}
613
614
615bool
617{
618 // qDebug() << "Selection polygon:" << toString();
619
620 bool ok = false;
621
622 double width_value = width(ok);
623 if(!ok)
624 return false;
625
626 double height_value = height(ok);
627 if(!ok)
628 return false;
629
630 // qDebug() << "Width and height computations succeeded:"
631 //<< "width:" << width_value << "height:" << height_value;
632
633 // A polygon is mono-dimensional if it has both non-0 width and no (max-min)
634 // width AND if the height is 0 or (max-min).
635 return ((width_value > 0 &&
636 width_value < std::numeric_limits<double>::max() - std::numeric_limits<double>::min()) &&
637 (height_value == 0 || height_value == std::numeric_limits<double>::max() -
638 std::numeric_limits<double>::min()));
639}
640
641
642bool
644{
645 // A selection polygon can behave like a line segment if the bottom side
646 // confounds with the top side.
647
648 bool ok = false;
649
650 double width_value = width(ok);
651 if(!ok)
652 return false;
653
654 double height_value = height(ok);
655 if(!ok)
656 return false;
657
658 // A polygon is two-dimensional if it has both non-0 width and no (max-min)
659 // width AND same for height.
660 return ((width_value > 0 &&
661 width_value < std::numeric_limits<double>::max() - std::numeric_limits<double>::min()) &&
662 (height_value > 0 &&
663 height_value < std::numeric_limits<double>::max() - std::numeric_limits<double>::min()));
664}
665
666
667bool
669{
670 // A skewed rectangle polygon has the following conditions verified:
671 //
672 // 1. If its left|right sides are vertical, then its top|bottom lines are
673 // *not* horizontal.
674 //
675 // 2 If its top|bottom lines are horizontal, then its left|right sides are
676 // *not* vertical.
677 //
678 // 3. Then, if a selection polygon is rectangle, its top|bottom lines are
679 // horizontal and its left|right lines are vertical.
680
681 // A line is vertical if its two defining points have the same X.
682 // A line is horizontal if its two defining points have the same Y.
683
684 // Try the horiontal top|bottom lines.
685
688 {
689 // We have horizontal top|bottom lines
690
691 // Try the vertical lines
692
695 {
696 // The top|bottom lines are vertical
697
698 return true;
699 }
700 }
701
702 return false;
703}
704
705
706QString
708{
709 // By essence, a selection polygon is designed to always have 4 points.
710
711 if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
712 qFatal("Programming error.");
713
714 // qDebug() << "size:" << m_points.size();
715
716 QString text = "Selection polygon points, from top left, clockwise\n";
717
718 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
719 {
720 QPointF iter_point = m_points[iter];
721
722 QString x_string = "NOT_SET";
723
724 if(iter_point.x() != std::numeric_limits<double>::min() &&
725 iter_point.x() != std::numeric_limits<double>::max())
726 x_string = QString("%1").arg(iter_point.x(), 0, 'f', 10);
727
728 QString y_string = "NOT_SET";
729
730 if(iter_point.y() != std::numeric_limits<double>::min() &&
731 iter_point.y() != std::numeric_limits<double>::max())
732 y_string = QString("%1").arg(iter_point.y(), 0, 'f', 10);
733
734 text += QString("(%1,%2)\n").arg(x_string).arg(y_string);
735 }
736
737 if(m_minX != std::numeric_limits<double>::min() && m_minX != std::numeric_limits<double>::max())
738 text += QString("minX: %1 - ").arg(m_minX, 0, 'f', 10);
739 else
740 text += QString("minX: NOT_SET - ");
741
742 if(m_maxX != std::numeric_limits<double>::min() && m_maxX != std::numeric_limits<double>::max())
743 text += QString("maxX: %1 - ").arg(m_maxX, 0, 'f', 10);
744 else
745 text += QString("maxX: NOT_SET - ");
746
747 if(m_minY != std::numeric_limits<double>::min() && m_minY != std::numeric_limits<double>::max())
748 text += QString("minY: %1 - ").arg(m_minY, 0, 'f', 10);
749 else
750 text += QString("minY: NOT_SET - ");
751
752 if(m_maxY != std::numeric_limits<double>::min() && m_maxY != std::numeric_limits<double>::max())
753 text += QString("maxY: %1 - ").arg(m_maxY, 0, 'f', 10);
754 else
755 text += QString("maxY: NOT_SET - ");
756
757 return text;
758}
759
760
761QString
763{
764 // By essence, a selection polygon is designed to always have 4 points.
765
766 if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
767 qFatal("Programming error.");
768
769 // qDebug() << "size:" << m_points.size();
770
771 QString text = "[";
772
773 QString x_string = "NOT_SET";
774 QString y_string = "NOT_SET";
775
776 // There are two situations:
777 //
778 // 1. The selection polygon is 1D, we only need to provide two points
779 //
780 // 2. The selection polygon is 2D, we need to provide four points.
781
782 if(is1D())
783 {
784 text += QString("(%1,%2)").arg(getLeftMostPoint().x()).arg("NOT_SET");
785 text += QString("(%1,%2)").arg(getRightMostPoint().x()).arg("NOT_SET");
786 }
787 else
788 {
789 for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
790 {
791 QPointF iter_point = m_points[iter];
792
793
794 if(iter_point.x() != std::numeric_limits<double>::min() &&
795 iter_point.x() != std::numeric_limits<double>::max())
796 x_string = QString("%1").arg(iter_point.x(), 0, 'f', 3);
797
798 if(iter_point.y() != std::numeric_limits<double>::min() &&
799 iter_point.y() != std::numeric_limits<double>::max())
800 y_string = QString("%1").arg(iter_point.y(), 0, 'f', 3);
801
802 text += QString("(%1,%2)").arg(x_string).arg(y_string);
803 }
804 }
805
806 text += "]";
807
808 return text;
809}
810
811
812void
814 const QPointF &tested_point)
815{
816 bool is_point_inside = false;
817
818 QString debug_string;
819
820 is_point_inside = selection_polygon.contains(tested_point);
821 debug_string = QString("(%1,%2) is inside: %3")
822 .arg(tested_point.x(), 0, 'f', 10)
823 .arg(tested_point.y(), 0, 'f', 10)
824 .arg(is_point_inside ? "true" : "false");
825 qDebug().noquote() << debug_string;
826}
827} // namespace pappso
828
829#if 0
830
831// This is to test the algo that check if a point is left of a line or right of
832// it or onto it. In this implementation, two lines define the vertical sides of
833// a polygon (square to ease analysis) and if the point is on the left line, then it
834// is considered ok, that is, that it is on the right side of the line and if it
835// is on the right line, then it is considered ok, that is that it is on the
836// left side of the line. If both conditions are ok, then the point is inside
837// the vertical lines of the polygon.
838
839 qDebug();
840
841 pappso::SelectionPolygon selection_polygon(QPointF(22.4, 473),
842 QPointF(28.9, 473),
843 QPointF(28.9, 250),
844 QPointF(22.4, 250));
845 qDebug() << "The test selection polygon:" << selection_polygon.toString();
846
847 std::vector<QPointF> test_points;
848
849 test_points.push_back(QPointF(25, 250));
850 test_points.push_back(QPointF(22.3, 362));
851 test_points.push_back(QPointF(22.4, 473));
852 test_points.push_back(QPointF(22.4, 473.5));
853 test_points.push_back(QPointF(25, 250));
854 test_points.push_back(QPointF(25, 250.5));
855 test_points.push_back(QPointF(25, 360));
856 test_points.push_back(QPointF(28.9, 250));
857 test_points.push_back(QPointF(29, 250));
858 test_points.push_back(QPointF(29, 360));
859 test_points.push_back(QPointF(28.9, 473));
860 test_points.push_back(QPointF(28.9, 473.5));
861 test_points.push_back(QPointF(20, 200));
862 test_points.push_back(QPointF(20, 600));
863 test_points.push_back(QPointF(35, 200));
864 test_points.push_back(QPointF(35, 600));
865
866 // double res = sideofline(XX;YY;xA;yA;xB;yB) =
867 // (xB-xA) * (YY-yA) - (yB-yA) * (XX-xA)
868
869 // If res == 0, the point is on the line
870 // If rest < 0, the point is on the right of the line
871 // If rest > 0, the point is on the left of the line
872
873 for(auto &&data_point : test_points)
874 {
875 // Left vertical line of the polygon
876
877 // Bottom point
878 double xA_left =
879 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_LEFT_POINT).x();
880 double yA_left =
881 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_LEFT_POINT).y();
882
883 // Top point
884 double xB_left =
885 selection_polygon.getPoint(pappso::PointSpecs::TOP_LEFT_POINT).x();
886 double yB_left =
887 selection_polygon.getPoint(pappso::PointSpecs::TOP_LEFT_POINT).y();
888
889 if((xB_left - xA_left) * (data_point.y() - yA_left) -
890 (yB_left - yA_left) * (data_point.x() - xA_left) >
891 0)
892 {
893 // The point is left of the left line. We can remove the point
894 // from the mass spectrum.
895
896 qDebug() << qSetRealNumberPrecision(10)
897 << "Filtered out point (left of left line):"
898 << data_point.x() << "-" << data_point.y();
899 continue;
900 }
901 else
902 {
903 qDebug() << qSetRealNumberPrecision(10)
904 << "Kept point (right of left line):" << data_point.x()
905 << "-" << data_point.y();
906 }
907
908 // Right vertical line of the polygon
909
910 // Bottom point
911 double xA_right =
912 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_RIGHT_POINT).x();
913 double yA_right =
914 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_RIGHT_POINT).y();
915
916 // Top point
917 double xB_right =
918 selection_polygon.getPoint(pappso::PointSpecs::TOP_RIGHT_POINT).x();
919 double yB_right =
920 selection_polygon.getPoint(pappso::PointSpecs::TOP_RIGHT_POINT).y();
921
922 if((xB_right - xA_right) * (data_point.y() - yA_right) -
923 (yB_right - yA_right) * (data_point.x() - xA_right) <
924 0)
925 {
926 qDebug() << qSetRealNumberPrecision(10)
927 << "Filtered out point (right of right line):"
928 << data_point.x() << "-" << data_point.y();
929 }
930 else
931 {
932 qDebug() << qSetRealNumberPrecision(10)
933 << "Definitively kept point (left of right line):"
934 << data_point.x() << "-" << data_point.y();
935 }
936 }
937#endif
938
939
940#if 0
941 // This is code to test the algorithm we have to establish if a given
942 // point is inside a selection polygon.
943
944 // First polygon that is square.
945 SelectionPolygon first_polygon(
946 QPointF(3, 8), QPointF(12, 8), QPointF(12, 3), QPointF(3, 3));
947
948 qDebug() << "square rectangle polygon: " << first_polygon.toString();
949
950 qDebug() << "outside";
951
952 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,1));
953 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2.999999,5));
954 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2.999999,8.000001));
955 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,5));
956 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,9));
957 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,1));
958 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,8.0000001));
959 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,1));
960 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12.0000001,3));
961 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,3));
962 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,5));
963 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,8));
964 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,9));
965 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,9));
966 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,2.9999999));
967
968 qDebug() << "on the lines";
969
970 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(3,4));
971 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,3));
972 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,3));
973 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,7));
974 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,8));
975 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,8));
976
977 qDebug() << "inside";
978
979 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(4,4));
980 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(3.00001, 3.00001));
981 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,4));
982 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,3.1));
983 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(11,5));
984 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(11.99999,5));
985 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,7.9));
986 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
987
988 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
989 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
990 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
991 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
992
993 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
994 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
995 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
996 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
997
998 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
999 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1000 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1001 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1002
1003 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1004 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1005 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1006 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1007
1008
1009 // Second polygon that is skewed.
1010 SelectionPolygon second_polygon(
1011 QPointF(9, 8), QPointF(12, 8), QPointF(6, 2), QPointF(3, 2));
1012
1013 qDebug() << "skewed rectangle polygon: " << second_polygon.toString();
1014
1015 qDebug() << "outside";
1016
1017 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,1));
1018 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,1.999999));
1019 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,3));
1020 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,8.000001));
1021 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,5));
1022 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,9));
1023 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6,5.0000001));
1024 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,1));
1025 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,2.999999));
1026 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,4.999999));
1027 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,8.0000001));
1028 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12.00000001,8));
1029 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12,7.999999));
1030 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(14,3));
1031 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(14,5));
1032
1033 qDebug() << "on the lines";
1034
1035 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(3,2));
1036 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6,5));
1037 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12,8));
1038 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,8));
1039 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,5));
1040 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(11,7));
1041 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,6));
1042 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(4,3));
1043
1044 qDebug() << "inside";
1045
1046 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(3.00001,2.000001));
1047 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(4,2.000001));
1048 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6.000001,2.00003));
1049 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,4));
1050 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(8.99999,5));
1051 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(10,7.99999));
1052 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(5,3));
1053 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(5,3.99999));
1054#endif
1055
1056
1057#if 0
1058
1059void
1060SelectionPolygon::reorderPoints()
1061{
1062 // We want to make sure that we actually have the right QPointF instances at
1063 // the right corners.
1064
1065 computeMinMaxCoordinates();
1066
1067 // Start by finding a point almost centered in the polygon. Not exactly
1068 // centered because our polygon can be squared and we'll look at angles
1069 // between the center-point->polygon-point_n and
1070 // center-point->polygon-point_n'.
1071
1072 double PI = 3.14159265358979323846;
1073 QPointF center_point(0, 0);
1074
1075 for(auto &point : m_points)
1076 {
1077 center_point.setX(center_point.x() + point.x());
1078 center_point.setY(center_point.y() + point.y());
1079 }
1080
1081 center_point.setX(center_point.x() / m_points.size());
1082 center_point.setY(center_point.y() / m_points.size());
1083
1084 // For a rectangle polygon, that would be the exact center and the angles
1085 // between the line segments would be similar two-by-two. So we need to move
1086 // the center_point a bit.
1087
1088 double distance_x = center_point.x() - m_minX;
1089 double distance_y = center_point.y() - m_minY;
1090
1091 center_point.setX(center_point.x() - (distance_x / 20));
1092 center_point.setY(center_point.y() - (distance_y / 20));
1093
1094 std::sort(m_points.begin(),
1095 m_points.end(),
1096 [center_point, PI](const QPointF &a, const QPointF &b) -> bool {
1097 double a1 = std::fmod(
1098 std::atan2(a.x() - center_point.x(), a.y() - center_point.y()) *
1099 180.0 / PI +
1100 360,
1101 (double)360);
1102
1103 double a2 = std::fmod(
1104 std::atan2(b.x() - center_point.x(), b.y() - center_point.y()) *
1105 180.0 / PI +
1106 360,
1107 (double)360);
1108
1109 // Original version that had problems because arguments to '%'
1110 // were double and int. fmod() is % for double.
1111 //(std::atan2(b.x() - center_point.x(), b.y() - center_point.y())
1112 //* 180.0 / PI + 360) % 360;
1113
1114 return (int)(a1 - a2);
1115 });
1116}
1117
1118#endif
bool range(Enums::Axis axis, double &range_start, double &range_end) const
bool rangeX(double &range_start, double &range_end) const
bool rangeY(double &range_start, double &range_end) const
static void debugAlgorithm(const SelectionPolygon &selection_polygon, const QPointF &tested_point)
SelectionPolygon transpose() const
QString toShort4PointsString() const
void setPoint(PointSpec point_spec, double x, double y)
void set2D(QPointF top_left, QPointF top_right, QPointF bottom_right, QPointF bottom_left)
const std::vector< QPointF > & getPoints() const
void copyPoint(PointSpec point_spec_src, PointSpec point_spec_dest)
double width(bool &ok) const
SelectionPolygon & operator=(const SelectionPolygon &other)
void set1D(double x_range_start, double x_range_end)
QPointF getPoint(PointSpec point_spec) const
double height(bool &ok) const
bool contains(const QPointF &tested_point) const
std::vector< QPointF > m_points
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39