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 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
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() &&
760 m_minX != std::numeric_limits<double>::max())
761 text += QString("minX: %1 - ").arg(m_minX, 0, 'f', 10);
762 else
763 text += QString("minX: NOT_SET - ");
764
765 if(m_maxX != std::numeric_limits<double>::min() &&
766 m_maxX != std::numeric_limits<double>::max())
767 text += QString("maxX: %1 - ").arg(m_maxX, 0, 'f', 10);
768 else
769 text += QString("maxX: NOT_SET - ");
770
771 if(m_minY != std::numeric_limits<double>::min() &&
772 m_minY != std::numeric_limits<double>::max())
773 text += QString("minY: %1 - ").arg(m_minY, 0, 'f', 10);
774 else
775 text += QString("minY: NOT_SET - ");
776
777 if(m_maxY != std::numeric_limits<double>::min() &&
778 m_maxY != std::numeric_limits<double>::max())
779 text += QString("maxY: %1 - ").arg(m_maxY, 0, 'f', 10);
780 else
781 text += QString("maxY: NOT_SET - ");
782
783 return text;
784}
785
786
787QString
789{
790 // By essence, a selection polygon is designed to always have 4 points.
791
792 if(m_points.size() != static_cast<int>(PointSpecs::ENUM_LAST))
793 qFatal("Programming error.");
794
795 // qDebug() << "size:" << m_points.size();
796
797 QString text = "[";
798
799 QString x_string = "NOT_SET";
800 QString y_string = "NOT_SET";
801
802 // There are two situations:
803 //
804 // 1. The selection polygon is 1D, we only need to provide two points
805 //
806 // 2. The selection polygon is 2D, we need to provide four points.
807
808 if(is1D())
809 {
810 text += QString("(%1,%2)").arg(getLeftMostPoint().x()).arg("NOT_SET");
811 text += QString("(%1,%2)").arg(getRightMostPoint().x()).arg("NOT_SET");
812 }
813 else
814 {
815 for(int iter = 0; iter < static_cast<int>(PointSpecs::ENUM_LAST); ++iter)
816 {
817 QPointF iter_point = m_points[iter];
818
819
820 if(iter_point.x() != std::numeric_limits<double>::min() &&
821 iter_point.x() != std::numeric_limits<double>::max())
822 x_string = QString("%1").arg(iter_point.x(), 0, 'f', 3);
823
824 if(iter_point.y() != std::numeric_limits<double>::min() &&
825 iter_point.y() != std::numeric_limits<double>::max())
826 y_string = QString("%1").arg(iter_point.y(), 0, 'f', 3);
827
828 text += QString("(%1,%2)").arg(x_string).arg(y_string);
829 }
830 }
831
832 text += "]";
833
834 return text;
835}
836
837
838void
840 const QPointF &tested_point)
841{
842 bool is_point_inside = false;
843
844 QString debug_string;
845
846 is_point_inside = selection_polygon.contains(tested_point);
847 debug_string = QString("(%1,%2) is inside: %3")
848 .arg(tested_point.x(), 0, 'f', 10)
849 .arg(tested_point.y(), 0, 'f', 10)
850 .arg(is_point_inside ? "true" : "false");
851 qDebug().noquote() << debug_string;
852}
853} // namespace pappso
854
855#if 0
856
857// This is to test the algo that check if a point is left of a line or right of
858// it or onto it. In this implementation, two lines define the vertical sides of
859// a polygon (square to ease analysis) and if the point is on the left line, then it
860// is considered ok, that is, that it is on the right side of the line and if it
861// is on the right line, then it is considered ok, that is that it is on the
862// left side of the line. If both conditions are ok, then the point is inside
863// the vertical lines of the polygon.
864
865 qDebug();
866
867 pappso::SelectionPolygon selection_polygon(QPointF(22.4, 473),
868 QPointF(28.9, 473),
869 QPointF(28.9, 250),
870 QPointF(22.4, 250));
871 qDebug() << "The test selection polygon:" << selection_polygon.toString();
872
873 std::vector<QPointF> test_points;
874
875 test_points.push_back(QPointF(25, 250));
876 test_points.push_back(QPointF(22.3, 362));
877 test_points.push_back(QPointF(22.4, 473));
878 test_points.push_back(QPointF(22.4, 473.5));
879 test_points.push_back(QPointF(25, 250));
880 test_points.push_back(QPointF(25, 250.5));
881 test_points.push_back(QPointF(25, 360));
882 test_points.push_back(QPointF(28.9, 250));
883 test_points.push_back(QPointF(29, 250));
884 test_points.push_back(QPointF(29, 360));
885 test_points.push_back(QPointF(28.9, 473));
886 test_points.push_back(QPointF(28.9, 473.5));
887 test_points.push_back(QPointF(20, 200));
888 test_points.push_back(QPointF(20, 600));
889 test_points.push_back(QPointF(35, 200));
890 test_points.push_back(QPointF(35, 600));
891
892 // double res = sideofline(XX;YY;xA;yA;xB;yB) =
893 // (xB-xA) * (YY-yA) - (yB-yA) * (XX-xA)
894
895 // If res == 0, the point is on the line
896 // If rest < 0, the point is on the right of the line
897 // If rest > 0, the point is on the left of the line
898
899 for(auto &&data_point : test_points)
900 {
901 // Left vertical line of the polygon
902
903 // Bottom point
904 double xA_left =
905 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_LEFT_POINT).x();
906 double yA_left =
907 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_LEFT_POINT).y();
908
909 // Top point
910 double xB_left =
911 selection_polygon.getPoint(pappso::PointSpecs::TOP_LEFT_POINT).x();
912 double yB_left =
913 selection_polygon.getPoint(pappso::PointSpecs::TOP_LEFT_POINT).y();
914
915 if((xB_left - xA_left) * (data_point.y() - yA_left) -
916 (yB_left - yA_left) * (data_point.x() - xA_left) >
917 0)
918 {
919 // The point is left of the left line. We can remove the point
920 // from the mass spectrum.
921
922 qDebug() << qSetRealNumberPrecision(10)
923 << "Filtered out point (left of left line):"
924 << data_point.x() << "-" << data_point.y();
925 continue;
926 }
927 else
928 {
929 qDebug() << qSetRealNumberPrecision(10)
930 << "Kept point (right of left line):" << data_point.x()
931 << "-" << data_point.y();
932 }
933
934 // Right vertical line of the polygon
935
936 // Bottom point
937 double xA_right =
938 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_RIGHT_POINT).x();
939 double yA_right =
940 selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_RIGHT_POINT).y();
941
942 // Top point
943 double xB_right =
944 selection_polygon.getPoint(pappso::PointSpecs::TOP_RIGHT_POINT).x();
945 double yB_right =
946 selection_polygon.getPoint(pappso::PointSpecs::TOP_RIGHT_POINT).y();
947
948 if((xB_right - xA_right) * (data_point.y() - yA_right) -
949 (yB_right - yA_right) * (data_point.x() - xA_right) <
950 0)
951 {
952 qDebug() << qSetRealNumberPrecision(10)
953 << "Filtered out point (right of right line):"
954 << data_point.x() << "-" << data_point.y();
955 }
956 else
957 {
958 qDebug() << qSetRealNumberPrecision(10)
959 << "Definitively kept point (left of right line):"
960 << data_point.x() << "-" << data_point.y();
961 }
962 }
963#endif
964
965
966#if 0
967 // This is code to test the algorithm we have to establish if a given
968 // point is inside a selection polygon.
969
970 // First polygon that is square.
971 SelectionPolygon first_polygon(
972 QPointF(3, 8), QPointF(12, 8), QPointF(12, 3), QPointF(3, 3));
973
974 qDebug() << "square rectangle polygon: " << first_polygon.toString();
975
976 qDebug() << "outside";
977
978 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,1));
979 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2.999999,5));
980 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2.999999,8.000001));
981 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,5));
982 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,9));
983 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,1));
984 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,8.0000001));
985 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,1));
986 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12.0000001,3));
987 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,3));
988 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,5));
989 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,8));
990 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,9));
991 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,9));
992 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,2.9999999));
993
994 qDebug() << "on the lines";
995
996 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(3,4));
997 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,3));
998 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,3));
999 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,7));
1000 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,8));
1001 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,8));
1002
1003 qDebug() << "inside";
1004
1005 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(4,4));
1006 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(3.00001, 3.00001));
1007 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,4));
1008 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,3.1));
1009 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(11,5));
1010 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(11.99999,5));
1011 SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,7.9));
1012 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1013
1014 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1015 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1016 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1017 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1018
1019 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1020 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1021 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1022 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1023
1024 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1025 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1026 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1027 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1028
1029 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1030 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1031 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1032 SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
1033
1034
1035 // Second polygon that is skewed.
1036 SelectionPolygon second_polygon(
1037 QPointF(9, 8), QPointF(12, 8), QPointF(6, 2), QPointF(3, 2));
1038
1039 qDebug() << "skewed rectangle polygon: " << second_polygon.toString();
1040
1041 qDebug() << "outside";
1042
1043 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,1));
1044 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,1.999999));
1045 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,3));
1046 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,8.000001));
1047 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,5));
1048 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,9));
1049 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6,5.0000001));
1050 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,1));
1051 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,2.999999));
1052 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,4.999999));
1053 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,8.0000001));
1054 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12.00000001,8));
1055 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12,7.999999));
1056 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(14,3));
1057 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(14,5));
1058
1059 qDebug() << "on the lines";
1060
1061 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(3,2));
1062 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6,5));
1063 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12,8));
1064 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,8));
1065 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,5));
1066 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(11,7));
1067 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,6));
1068 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(4,3));
1069
1070 qDebug() << "inside";
1071
1072 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(3.00001,2.000001));
1073 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(4,2.000001));
1074 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6.000001,2.00003));
1075 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,4));
1076 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(8.99999,5));
1077 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(10,7.99999));
1078 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(5,3));
1079 SelectionPolygon::debugAlgorithm(second_polygon, QPointF(5,3.99999));
1080#endif
1081
1082
1083#if 0
1084
1085void
1086SelectionPolygon::reorderPoints()
1087{
1088 // We want to make sure that we actually have the right QPointF instances at
1089 // the right corners.
1090
1091 computeMinMaxCoordinates();
1092
1093 // Start by finding a point almost centered in the polygon. Not exactly
1094 // centered because our polygon can be squared and we'll look at angles
1095 // between the center-point->polygon-point_n and
1096 // center-point->polygon-point_n'.
1097
1098 double PI = 3.14159265358979323846;
1099 QPointF center_point(0, 0);
1100
1101 for(auto &point : m_points)
1102 {
1103 center_point.setX(center_point.x() + point.x());
1104 center_point.setY(center_point.y() + point.y());
1105 }
1106
1107 center_point.setX(center_point.x() / m_points.size());
1108 center_point.setY(center_point.y() / m_points.size());
1109
1110 // For a rectangle polygon, that would be the exact center and the angles
1111 // between the line segments would be similar two-by-two. So we need to move
1112 // the center_point a bit.
1113
1114 double distance_x = center_point.x() - m_minX;
1115 double distance_y = center_point.y() - m_minY;
1116
1117 center_point.setX(center_point.x() - (distance_x / 20));
1118 center_point.setY(center_point.y() - (distance_y / 20));
1119
1120 std::sort(m_points.begin(),
1121 m_points.end(),
1122 [center_point, PI](const QPointF &a, const QPointF &b) -> bool {
1123 double a1 = std::fmod(
1124 std::atan2(a.x() - center_point.x(), a.y() - center_point.y()) *
1125 180.0 / PI +
1126 360,
1127 (double)360);
1128
1129 double a2 = std::fmod(
1130 std::atan2(b.x() - center_point.x(), b.y() - center_point.y()) *
1131 180.0 / PI +
1132 360,
1133 (double)360);
1134
1135 // Original version that had problems because arguments to '%'
1136 // were double and int. fmod() is % for double.
1137 //(std::atan2(b.x() - center_point.x(), b.y() - center_point.y())
1138 //* 180.0 / PI + 360) % 360;
1139
1140 return (int)(a1 - a2);
1141 });
1142}
1143
1144#endif
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
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
SelectionPolygon & operator=(const SelectionPolygon &other)
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