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