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