Loading...
Searching...
No Matches
selectionpolygon.cpp
Go to the documentation of this file.
// Copyright 2021 Filippo Rusconi
// GPLv3+
/////////////////////// StdLib includes
#include <limits>
#include <cmath>
/////////////////////// Qt includes
#include <QDebug>
/////////////////////// Local includes
#include "selectionpolygon.h"
namespace pappso
{
SelectionPolygon::SelectionPolygon()
{
// When we create a polygon, we create it as immense as possible, so that
// if this polygon is not modified, any other polygon created on the basis
// of experimental data will fit inside it.
// See the definition of the points in the header file.
}
SelectionPolygon::SelectionPolygon(QPointF top_left_point, QPointF top_right_point)
{
// First clear the default values points because we want to push_back
// new points and we want to only ever have 4 points.
m_points.clear();
// We get only two points that provide the horizontal range of the polygon.
// These two points show the x range of the polygon. We need to craft a
// polygon that has:
//
// that specified x range and
//
// the widest y range possible.
// In other words, we are crafting a 1D selection polygon.
// top left point
m_points.push_back(QPointF(top_left_point.x(), std::numeric_limits<double>::max()));
// top right point
m_points.push_back(QPointF(top_right_point.x(), std::numeric_limits<double>::max()));
// bottom right point
m_points.push_back(QPointF(top_right_point.x(), std::numeric_limits<double>::min()));
// bottom left point
m_points.push_back(QPointF(top_left_point.x(), std::numeric_limits<double>::min()));
// Compute the min|max x|y coordinates of the polygon that will be used to
// quickly check if a point is outside.
computeMinMaxCoordinates();
}
SelectionPolygon::SelectionPolygon(QPointF top_left_point,
QPointF top_right_point,
QPointF bottom_right_point,
QPointF bottom_left_point)
{
// First clear the default values points.
m_points.clear();
// Attention, we need to push back the points starting top left and clockwise.
m_points.push_back(top_left_point);
m_points.push_back(top_right_point);
m_points.push_back(bottom_right_point);
m_points.push_back(bottom_left_point);
// Compute the min|max x|y coordinates of the polygon that will be used to
// quickly check if a point is outside.
computeMinMaxCoordinates();
}
SelectionPolygon::SelectionPolygon(const SelectionPolygon &other)
{
if(other.m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
qFatal("The template selection polygon must have four points, no less, no more");
// First clear the default values points.
m_points.clear();
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
m_points.push_back(other.m_points[iter]);
}
m_minX = other.m_minX;
m_minY = other.m_minY;
m_maxX = other.m_maxX;
m_maxY = other.m_maxY;
}
SelectionPolygon::~SelectionPolygon()
{
}
void
SelectionPolygon::setPoint(PointSpec point_spec, double x, double y)
{
m_points[static_cast<int>(point_spec)].setX(x);
m_points[static_cast<int>(point_spec)].setY(y);
computeMinMaxCoordinates();
}
void
SelectionPolygon::setPoint(PointSpec point_spec, QPointF point)
{
setPoint(point_spec, point.x(), point.y());
computeMinMaxCoordinates();
}
void
SelectionPolygon::copyPoint(PointSpec point_spec_src, PointSpec point_spec_dest)
{
QPointF src_point = getPoint(point_spec_src);
setPoint(point_spec_dest, src_point);
computeMinMaxCoordinates();
}
void
SelectionPolygon::set1D(double x_range_start, double x_range_end)
{
// We get only two points that provide the horizontal range of the polygon.
// These two points show the x range of the polygon. We need to craft a
// polygon that has:
//
// that specified x range and
//
// the widest y range possible.
resetPoints();
// top left point
setPoint(PointSpec::TOP_LEFT_POINT, QPointF(x_range_start, std::numeric_limits<double>::max()));
// top right point
setPoint(PointSpec::TOP_RIGHT_POINT, QPointF(x_range_end, std::numeric_limits<double>::max()));
// bottom right point
setPoint(PointSpec::BOTTOM_RIGHT_POINT, QPointF(x_range_end, std::numeric_limits<double>::min()));
// bottom left point
setPoint(PointSpec::BOTTOM_LEFT_POINT,
QPointF(x_range_start, std::numeric_limits<double>::min()));
// Compute the min|max x|y coordinates of the polygon that will be used to
// quickly check if a point is outside.
computeMinMaxCoordinates();
}
void
SelectionPolygon::set2D(QPointF top_left,
QPointF top_right,
QPointF bottom_right,
QPointF bottom_left)
{
resetPoints();
// top left point
setPoint(PointSpec::TOP_LEFT_POINT, top_left);
// qDebug() << "PointSpecs::TOP_LEFT_POINT:" << top_left;
// top right point
setPoint(PointSpec::TOP_RIGHT_POINT, top_right);
// qDebug() << "PointSpecs::TOP_RIGHT_POINT:" << top_right;
// bottom right point
setPoint(PointSpec::BOTTOM_RIGHT_POINT, bottom_right);
// qDebug() << "PointSpecs::BOTTOM_RIGHT_POINT:" << bottom_right;
// bottom left point
setPoint(PointSpec::BOTTOM_LEFT_POINT, bottom_left);
// qDebug() << "PointSpecs::BOTTOM_LEFT_POINT:" << bottom_left;
// Compute the min|max x|y coordinates of the polygon that will be used to
// quickly check if a point is outside.
computeMinMaxCoordinates();
// qDebug() << toString();
}
void
SelectionPolygon::convertTo1D()
{
// When a 2D polygon is converted to a 1D polygon, the x axis range is
// unchanged, but the height is set to its maximum possible with the bottom
// line at y = min and the top line at y = max.
computeMinMaxCoordinates();
set1D(m_minX, m_maxX);
}
QPointF
SelectionPolygon::getLeftMostPoint() const
{
// When we say leftmost, that means that we are implicitely interesed in
// x-axis coordinate of the points.
QPointF temp_point(std::numeric_limits<double>::max(), 0);
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
if(m_points[iter].x() < temp_point.x())
{
temp_point = m_points[iter];
}
}
return temp_point;
}
QPointF
SelectionPolygon::getRightMostPoint() const
{
// When we say rightmost, that means that we are implicitely interesed in
// x-axis coordinate of the points.
QPointF temp_point(std::numeric_limits<double>::min(), 0);
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
if(m_points[iter].x() > temp_point.x())
{
temp_point = m_points[iter];
}
}
return temp_point;
}
QPointF
SelectionPolygon::getTopMostPoint() const
{
// When we say topmost or bottommost , that means that we are implicitely
// interesed in y-axis coordinate of the points.
QPointF temp_point(0, std::numeric_limits<double>::min());
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
if(m_points[iter].y() > temp_point.y())
{
temp_point = m_points[iter];
}
}
return temp_point;
}
QPointF
SelectionPolygon::getBottomMostPoint() const
{
// When we say topmost or bottommost , that means that we are implicitely
// interesed in y-axis coordinate of the points.
QPointF temp_point(0, std::numeric_limits<double>::max());
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
if(m_points[iter].y() < temp_point.y())
{
temp_point = m_points[iter];
}
}
return temp_point;
}
const std::vector<QPointF> &
SelectionPolygon::getPoints() const
{
return m_points;
}
QPointF
SelectionPolygon::getPoint(PointSpec point_spec) const
{
return m_points[static_cast<int>(point_spec)];
}
bool
SelectionPolygon::computeMinMaxCoordinates()
{
// Set the variable to starting values that allow easy value comparisons with
// std::min() and std::max() for checking the x|y values below.
m_minX = std::numeric_limits<double>::max();
m_minY = std::numeric_limits<double>::max();
m_maxX = std::numeric_limits<double>::min();
m_maxY = std::numeric_limits<double>::min();
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
m_minX = std::min(m_points.at(iter).x(), m_minX);
m_maxX = std::max(m_points.at(iter).x(), m_maxX);
m_minY = std::min(m_points.at(iter).y(), m_minY);
m_maxY = std::max(m_points.at(iter).y(), m_maxY);
}
return true;
}
bool
SelectionPolygon::computeMinMaxCoordinates(double &min_x,
double &max_x,
double &min_y,
double &max_y) const
{
// Set the variable to starting values that allow easy value comparisons with
// std::min() and std::max() for checking the x|y values below.
min_x = std::numeric_limits<double>::max();
min_y = std::numeric_limits<double>::max();
max_x = std::numeric_limits<double>::min();
max_y = std::numeric_limits<double>::min();
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
min_x = std::min(m_points.at(iter).x(), min_x);
max_x = std::max(m_points.at(iter).x(), max_x);
min_y = std::min(m_points.at(iter).y(), min_y);
max_y = std::max(m_points.at(iter).y(), max_y);
}
// qDebug() << "min_x:" << min_x << "max_x:" << max_x << "min_y:" << min_y
//<< "max_y:" << max_y;
return true;
}
double
SelectionPolygon::width(bool &ok) const
{
double min_x;
double min_y;
double max_x;
double max_y;
computeMinMaxCoordinates(min_x, max_x, min_y, max_y);
ok = true;
return max_x - min_x;
}
double
SelectionPolygon::height(bool &ok) const
{
double min_x;
double min_y;
double max_x;
double max_y;
computeMinMaxCoordinates(min_x, max_x, min_y, max_y);
ok = true;
return max_y - min_y;
}
bool
SelectionPolygon::rangeX(double &range_start, double &range_end) const
{
double min_y = std::numeric_limits<double>::max();
double max_y = std::numeric_limits<double>::min();
return computeMinMaxCoordinates(range_start, range_end, min_y, max_y);
}
bool
SelectionPolygon::rangeY(double &range_start, double &range_end) const
{
double min_x = std::numeric_limits<double>::max();
double max_x = std::numeric_limits<double>::min();
return computeMinMaxCoordinates(min_x, max_x, range_start, range_end);
}
bool
SelectionPolygon::range(Enums::Axis axis, double &range_start, double &range_end) const
{
if(axis == Enums::Axis::x)
return rangeX(range_start, range_end);
else if(axis == Enums::Axis::y)
return rangeY(range_start, range_end);
return false;
}
// All this is valid only if the polygon has four sides.
//
// Transposing means that we take each point and permute x with y.
// During transposition, the TOP_RIGHT_POINT BOTTOM_LEFT_POINT are invariant.
//
// Transposition is like rotating the polygon arount the
// BOTTOM_LEFT_POINT--TOP_RIGHT_POINT axis, which make them invariant and
// permutates TOP_LEFT_POINT with BOTTOM_RIGHT_POINT.
//
// If iteration in the points is clockwise before, iteration below
// counter-clockwise after transposition, that is:
// TOP_LEFT_POINT becomes BOTTOM_RIGHT_POINT
//
SelectionPolygon
SelectionPolygon::transpose() const
{
SelectionPolygon selection_polygon;
// Make sure we do this for a polygon with four sides.
if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
qFatal("The polygon must have four points, no less, no more");
// The two invariant points, that is, the two points that do no change
// position in the polygon corners. Of course, x becomes y.
selection_polygon.setPoint(
PointSpec::TOP_RIGHT_POINT,
QPointF(getPoint(PointSpec::TOP_RIGHT_POINT).y(), getPoint(PointSpec::TOP_RIGHT_POINT).x()));
selection_polygon.setPoint(PointSpec::BOTTOM_LEFT_POINT,
QPointF(getPoint(PointSpec::BOTTOM_LEFT_POINT).y(),
getPoint(PointSpec::BOTTOM_LEFT_POINT).x()));
// The two other points.
selection_polygon.setPoint(
PointSpec::BOTTOM_RIGHT_POINT,
QPointF(getPoint(PointSpec::TOP_LEFT_POINT).y(), getPoint(PointSpec::TOP_LEFT_POINT).x()));
selection_polygon.setPoint(PointSpec::TOP_LEFT_POINT,
QPointF(getPoint(PointSpec::BOTTOM_RIGHT_POINT).y(),
getPoint(PointSpec::BOTTOM_RIGHT_POINT).x()));
return selection_polygon;
}
bool
SelectionPolygon::contains(const QPointF &tested_point) const
{
// Easy check: if the point lies outside of most external limits of the
// polygon, return false.
if(tested_point.x() < m_minX || tested_point.x() > m_maxX || tested_point.y() < m_minY ||
tested_point.y() > m_maxY)
{
// qDebug() << "Testing point:" << tested_point
//<< "aginst polygon:" << toString()
//<< "is out of x and y ranges.";
return false;
}
// There are two situations:
//
// 1. The selection polygon is a rectangle, we can check the tested_point very
// easily.
//
// 2. The selection polygon is a skewed rectangle, that is, it is a
// parallelogram, we need to really use the point-in-polygon algorithm.
if(isRectangle())
{
// qDebug() << "Selection polygon *is* rectangle.";
double x = tested_point.x();
double y = tested_point.y();
// return (x >= getPoint(PointSpecs::TOP_LEFT_POINT).x() &&
// x <= getPoint(PointSpecs::TOP_RIGHT_POINT).x() &&
// y >= getPoint(PointSpecs::BOTTOM_LEFT_POINT).y() &&
// y <= getPoint(PointSpecs::TOP_LEFT_POINT).y());
bool res = x >= m_minX && x <= m_maxX && y >= m_minY && y <= m_maxY;
// qDebug() << qSetRealNumberPrecision(10) << "Returning: " << res
//<< "for point:" << tested_point
//<< "and selection polygon:" << toString();
return res;
}
// qDebug() << "Testing point:" << tested_point
//<< "aginst polygon:" << toString()
//<< "is tested against a skewed selection polygon rectangle.";
// At this point, we know the selection polygon is not rectangle, we have to
// make the real check using the point-in-polygon algorithm.
// This code is inspired by the work described here:
// https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
// int pnpoly(int vertex_count, float *vertx, float *verty, float testx,
// float testy)
int i = 0;
int j = 0;
bool is_inside = false;
int vertex_count = m_points.size();
for(i = 0, j = vertex_count - 1; i < vertex_count; j = i++)
{
if(((m_points.at(i).y() > tested_point.y()) != (m_points.at(j).y() > tested_point.y())) &&
(tested_point.x() < (m_points.at(j).x() - m_points.at(i).x()) *
(tested_point.y() - m_points.at(i).y()) /
(m_points.at(j).y() - m_points.at(i).y()) +
m_points.at(i).x()))
is_inside = !is_inside;
}
// if(is_inside)
// qDebug() << "Testing point:" << tested_point
//<< "aginst polygon:" << toString() << "turns out be in.";
// else
// qDebug() << "Testing point:" << tested_point
//<< "aginst polygon:" << toString() << "turns out be out.";
return is_inside;
}
bool
SelectionPolygon::contains(const SelectionPolygon &selection_polygon) const
{
// A polygon is inside another polygon if all its points are inside the
// polygon.
bool is_inside = true;
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
if(!contains(selection_polygon.getPoint(static_cast<PointSpec>(iter))))
is_inside = false;
}
return is_inside;
}
SelectionPolygon &
SelectionPolygon::operator=(const SelectionPolygon &other)
{
if(this == &other)
return *this;
if(other.m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
qFatal("Programming error.");
if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
qFatal("Programming error.");
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
m_points[iter] = other.m_points[iter];
m_minX = other.m_minX;
m_minY = other.m_minY;
m_maxX = other.m_maxX;
m_maxY = other.m_maxY;
return *this;
}
void
SelectionPolygon::resetPoints()
{
// Reset the points exactly as they were set upon construction of an empty
// polygon.
m_points[0] = QPointF(std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
m_points[0] = QPointF(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
m_points[0] = QPointF(std::numeric_limits<double>::max(), std::numeric_limits<double>::min());
m_points[0] = QPointF(std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
}
bool
SelectionPolygon::is1D() const
{
// qDebug() << "Selection polygon:" << toString();
bool ok = false;
double width_value = width(ok);
if(!ok)
return false;
double height_value = height(ok);
if(!ok)
return false;
// qDebug() << "Width and height computations succeeded:"
//<< "width:" << width_value << "height:" << height_value;
// A polygon is mono-dimensional if it has both non-0 width and no (max-min)
// width AND if the height is 0 or (max-min).
return ((width_value > 0 &&
width_value < std::numeric_limits<double>::max() - std::numeric_limits<double>::min()) &&
(height_value == 0 || height_value == std::numeric_limits<double>::max() -
std::numeric_limits<double>::min()));
}
bool
SelectionPolygon::is2D() const
{
// A selection polygon can behave like a line segment if the bottom side
// confounds with the top side.
bool ok = false;
double width_value = width(ok);
if(!ok)
return false;
double height_value = height(ok);
if(!ok)
return false;
// A polygon is two-dimensional if it has both non-0 width and no (max-min)
// width AND same for height.
return ((width_value > 0 &&
width_value < std::numeric_limits<double>::max() - std::numeric_limits<double>::min()) &&
(height_value > 0 &&
height_value < std::numeric_limits<double>::max() - std::numeric_limits<double>::min()));
}
bool
SelectionPolygon::isRectangle() const
{
// A skewed rectangle polygon has the following conditions verified:
//
// 1. If its left|right sides are vertical, then its top|bottom lines are
// *not* horizontal.
//
// 2 If its top|bottom lines are horizontal, then its left|right sides are
// *not* vertical.
//
// 3. Then, if a selection polygon is rectangle, its top|bottom lines are
// horizontal and its left|right lines are vertical.
// A line is vertical if its two defining points have the same X.
// A line is horizontal if its two defining points have the same Y.
// Try the horiontal top|bottom lines.
if(getPoint(PointSpec::TOP_LEFT_POINT).y() == getPoint(PointSpec::TOP_RIGHT_POINT).y() &&
getPoint(PointSpec::BOTTOM_LEFT_POINT).y() == getPoint(PointSpec::BOTTOM_RIGHT_POINT).y())
{
// We have horizontal top|bottom lines
// Try the vertical lines
if(getPoint(PointSpec::TOP_LEFT_POINT).x() == getPoint(PointSpec::BOTTOM_LEFT_POINT).x() &&
getPoint(PointSpec::TOP_RIGHT_POINT).x() == getPoint(PointSpec::BOTTOM_RIGHT_POINT).x())
{
// The top|bottom lines are vertical
return true;
}
}
return false;
}
QString
SelectionPolygon::toString() const
{
// By essence, a selection polygon is designed to always have 4 points.
if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
qFatal("Programming error.");
// qDebug() << "size:" << m_points.size();
QString text = "Selection polygon points, from top left, clockwise\n";
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
QPointF iter_point = m_points[iter];
QString x_string = "NOT_SET";
if(iter_point.x() != std::numeric_limits<double>::min() &&
iter_point.x() != std::numeric_limits<double>::max())
x_string = QString("%1").arg(iter_point.x(), 0, 'f', 10);
QString y_string = "NOT_SET";
if(iter_point.y() != std::numeric_limits<double>::min() &&
iter_point.y() != std::numeric_limits<double>::max())
y_string = QString("%1").arg(iter_point.y(), 0, 'f', 10);
text += QString("(%1,%2)\n").arg(x_string).arg(y_string);
}
if(m_minX != std::numeric_limits<double>::min() && m_minX != std::numeric_limits<double>::max())
text += QString("minX: %1 - ").arg(m_minX, 0, 'f', 10);
else
text += QString("minX: NOT_SET - ");
if(m_maxX != std::numeric_limits<double>::min() && m_maxX != std::numeric_limits<double>::max())
text += QString("maxX: %1 - ").arg(m_maxX, 0, 'f', 10);
else
text += QString("maxX: NOT_SET - ");
if(m_minY != std::numeric_limits<double>::min() && m_minY != std::numeric_limits<double>::max())
text += QString("minY: %1 - ").arg(m_minY, 0, 'f', 10);
else
text += QString("minY: NOT_SET - ");
if(m_maxY != std::numeric_limits<double>::min() && m_maxY != std::numeric_limits<double>::max())
text += QString("maxY: %1 - ").arg(m_maxY, 0, 'f', 10);
else
text += QString("maxY: NOT_SET - ");
return text;
}
QString
SelectionPolygon::toShort4PointsString() const
{
// By essence, a selection polygon is designed to always have 4 points.
if(m_points.size() != static_cast<int>(PointSpec::ENUM_LAST))
qFatal("Programming error.");
// qDebug() << "size:" << m_points.size();
QString text = "[";
QString x_string = "NOT_SET";
QString y_string = "NOT_SET";
// There are two situations:
//
// 1. The selection polygon is 1D, we only need to provide two points
//
// 2. The selection polygon is 2D, we need to provide four points.
if(is1D())
{
text += QString("(%1,%2)").arg(getLeftMostPoint().x()).arg("NOT_SET");
text += QString("(%1,%2)").arg(getRightMostPoint().x()).arg("NOT_SET");
}
else
{
for(int iter = 0; iter < static_cast<int>(PointSpec::ENUM_LAST); ++iter)
{
QPointF iter_point = m_points[iter];
if(iter_point.x() != std::numeric_limits<double>::min() &&
iter_point.x() != std::numeric_limits<double>::max())
x_string = QString("%1").arg(iter_point.x(), 0, 'f', 3);
if(iter_point.y() != std::numeric_limits<double>::min() &&
iter_point.y() != std::numeric_limits<double>::max())
y_string = QString("%1").arg(iter_point.y(), 0, 'f', 3);
text += QString("(%1,%2)").arg(x_string).arg(y_string);
}
}
text += "]";
return text;
}
void
SelectionPolygon::debugAlgorithm(const SelectionPolygon &selection_polygon,
const QPointF &tested_point)
{
bool is_point_inside = false;
QString debug_string;
is_point_inside = selection_polygon.contains(tested_point);
debug_string = QString("(%1,%2) is inside: %3")
.arg(tested_point.x(), 0, 'f', 10)
.arg(tested_point.y(), 0, 'f', 10)
.arg(is_point_inside ? "true" : "false");
qDebug().noquote() << debug_string;
}
} // namespace pappso
#if 0
// This is to test the algo that check if a point is left of a line or right of
// it or onto it. In this implementation, two lines define the vertical sides of
// a polygon (square to ease analysis) and if the point is on the left line, then it
// is considered ok, that is, that it is on the right side of the line and if it
// is on the right line, then it is considered ok, that is that it is on the
// left side of the line. If both conditions are ok, then the point is inside
// the vertical lines of the polygon.
qDebug();
pappso::SelectionPolygon selection_polygon(QPointF(22.4, 473),
QPointF(28.9, 473),
QPointF(28.9, 250),
QPointF(22.4, 250));
qDebug() << "The test selection polygon:" << selection_polygon.toString();
std::vector<QPointF> test_points;
test_points.push_back(QPointF(25, 250));
test_points.push_back(QPointF(22.3, 362));
test_points.push_back(QPointF(22.4, 473));
test_points.push_back(QPointF(22.4, 473.5));
test_points.push_back(QPointF(25, 250));
test_points.push_back(QPointF(25, 250.5));
test_points.push_back(QPointF(25, 360));
test_points.push_back(QPointF(28.9, 250));
test_points.push_back(QPointF(29, 250));
test_points.push_back(QPointF(29, 360));
test_points.push_back(QPointF(28.9, 473));
test_points.push_back(QPointF(28.9, 473.5));
test_points.push_back(QPointF(20, 200));
test_points.push_back(QPointF(20, 600));
test_points.push_back(QPointF(35, 200));
test_points.push_back(QPointF(35, 600));
// double res = sideofline(XX;YY;xA;yA;xB;yB) =
// (xB-xA) * (YY-yA) - (yB-yA) * (XX-xA)
// If res == 0, the point is on the line
// If rest < 0, the point is on the right of the line
// If rest > 0, the point is on the left of the line
for(auto &&data_point : test_points)
{
// Left vertical line of the polygon
// Bottom point
double xA_left =
selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_LEFT_POINT).x();
double yA_left =
selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_LEFT_POINT).y();
// Top point
double xB_left =
selection_polygon.getPoint(pappso::PointSpecs::TOP_LEFT_POINT).x();
double yB_left =
selection_polygon.getPoint(pappso::PointSpecs::TOP_LEFT_POINT).y();
if((xB_left - xA_left) * (data_point.y() - yA_left) -
(yB_left - yA_left) * (data_point.x() - xA_left) >
0)
{
// The point is left of the left line. We can remove the point
// from the mass spectrum.
qDebug() << qSetRealNumberPrecision(10)
<< "Filtered out point (left of left line):"
<< data_point.x() << "-" << data_point.y();
continue;
}
else
{
qDebug() << qSetRealNumberPrecision(10)
<< "Kept point (right of left line):" << data_point.x()
<< "-" << data_point.y();
}
// Right vertical line of the polygon
// Bottom point
double xA_right =
selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_RIGHT_POINT).x();
double yA_right =
selection_polygon.getPoint(pappso::PointSpecs::BOTTOM_RIGHT_POINT).y();
// Top point
double xB_right =
selection_polygon.getPoint(pappso::PointSpecs::TOP_RIGHT_POINT).x();
double yB_right =
selection_polygon.getPoint(pappso::PointSpecs::TOP_RIGHT_POINT).y();
if((xB_right - xA_right) * (data_point.y() - yA_right) -
(yB_right - yA_right) * (data_point.x() - xA_right) <
0)
{
qDebug() << qSetRealNumberPrecision(10)
<< "Filtered out point (right of right line):"
<< data_point.x() << "-" << data_point.y();
}
else
{
qDebug() << qSetRealNumberPrecision(10)
<< "Definitively kept point (left of right line):"
<< data_point.x() << "-" << data_point.y();
}
}
#endif
#if 0
// This is code to test the algorithm we have to establish if a given
// point is inside a selection polygon.
// First polygon that is square.
SelectionPolygon first_polygon(
QPointF(3, 8), QPointF(12, 8), QPointF(12, 3), QPointF(3, 3));
qDebug() << "square rectangle polygon: " << first_polygon.toString();
qDebug() << "outside";
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,1));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2.999999,5));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2.999999,8.000001));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,5));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(2,9));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,1));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,8.0000001));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,1));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12.0000001,3));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,3));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,5));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,8));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(14,9));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,9));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,2.9999999));
qDebug() << "on the lines";
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(3,4));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,3));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,3));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,7));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(12,8));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,8));
qDebug() << "inside";
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(4,4));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(3.00001, 3.00001));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,4));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,3.1));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(11,5));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(11.99999,5));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF(7,7.9));
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
SelectionPolygon::debugAlgorithm(first_polygon, QPointF());
// Second polygon that is skewed.
SelectionPolygon second_polygon(
QPointF(9, 8), QPointF(12, 8), QPointF(6, 2), QPointF(3, 2));
qDebug() << "skewed rectangle polygon: " << second_polygon.toString();
qDebug() << "outside";
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,1));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,1.999999));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,3));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2.999999,8.000001));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,5));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(2,9));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6,5.0000001));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,1));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,2.999999));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,4.999999));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,8.0000001));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12.00000001,8));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12,7.999999));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(14,3));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(14,5));
qDebug() << "on the lines";
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(3,2));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6,5));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(12,8));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,8));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(9,5));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(11,7));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,6));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(4,3));
qDebug() << "inside";
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(3.00001,2.000001));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(4,2.000001));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(6.000001,2.00003));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(7,4));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(8.99999,5));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(10,7.99999));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(5,3));
SelectionPolygon::debugAlgorithm(second_polygon, QPointF(5,3.99999));
#endif
#if 0
void
SelectionPolygon::reorderPoints()
{
// We want to make sure that we actually have the right QPointF instances at
// the right corners.
computeMinMaxCoordinates();
// Start by finding a point almost centered in the polygon. Not exactly
// centered because our polygon can be squared and we'll look at angles
// between the center-point->polygon-point_n and
// center-point->polygon-point_n'.
double PI = 3.14159265358979323846;
QPointF center_point(0, 0);
for(auto &point : m_points)
{
center_point.setX(center_point.x() + point.x());
center_point.setY(center_point.y() + point.y());
}
center_point.setX(center_point.x() / m_points.size());
center_point.setY(center_point.y() / m_points.size());
// For a rectangle polygon, that would be the exact center and the angles
// between the line segments would be similar two-by-two. So we need to move
// the center_point a bit.
double distance_x = center_point.x() - m_minX;
double distance_y = center_point.y() - m_minY;
center_point.setX(center_point.x() - (distance_x / 20));
center_point.setY(center_point.y() - (distance_y / 20));
std::sort(m_points.begin(),
m_points.end(),
[center_point, PI](const QPointF &a, const QPointF &b) -> bool {
double a1 = std::fmod(
std::atan2(a.x() - center_point.x(), a.y() - center_point.y()) *
180.0 / PI +
360,
(double)360);
double a2 = std::fmod(
std::atan2(b.x() - center_point.x(), b.y() - center_point.y()) *
180.0 / PI +
360,
(double)360);
// Original version that had problems because arguments to '%'
// were double and int. fmod() is % for double.
//(std::atan2(b.x() - center_point.x(), b.y() - center_point.y())
//* 180.0 / PI + 360) % 360;
return (int)(a1 - a2);
});
}
#endif
pappso::SelectionPolygon
Definition selectionpolygon.h:44
pappso::SelectionPolygon::range
bool range(Enums::Axis axis, double &range_start, double &range_end) const
Definition selectionpolygon.cpp:417
pappso::SelectionPolygon::rangeX
bool rangeX(double &range_start, double &range_end) const
Definition selectionpolygon.cpp:397
pappso::SelectionPolygon::~SelectionPolygon
virtual ~SelectionPolygon()
Definition selectionpolygon.cpp:107
pappso::SelectionPolygon::m_maxY
double m_maxY
Definition selectionpolygon.h:135
pappso::SelectionPolygon::rangeY
bool rangeY(double &range_start, double &range_end) const
Definition selectionpolygon.cpp:407
pappso::SelectionPolygon::debugAlgorithm
static void debugAlgorithm(const SelectionPolygon &selection_polygon, const QPointF &tested_point)
Definition selectionpolygon.cpp:813
pappso::SelectionPolygon::is1D
bool is1D() const
Definition selectionpolygon.cpp:616
pappso::SelectionPolygon::transpose
SelectionPolygon transpose() const
Definition selectionpolygon.cpp:442
pappso::SelectionPolygon::toShort4PointsString
QString toShort4PointsString() const
Definition selectionpolygon.cpp:762
pappso::SelectionPolygon::setPoint
void setPoint(PointSpec point_spec, double x, double y)
Definition selectionpolygon.cpp:113
pappso::SelectionPolygon::m_minY
double m_minY
Definition selectionpolygon.h:132
pappso::SelectionPolygon::getTopMostPoint
QPointF getTopMostPoint() const
Definition selectionpolygon.cpp:259
pappso::SelectionPolygon::getRightMostPoint
QPointF getRightMostPoint() const
Definition selectionpolygon.cpp:239
pappso::SelectionPolygon::set2D
void set2D(QPointF top_left, QPointF top_right, QPointF bottom_right, QPointF bottom_left)
Definition selectionpolygon.cpp:174
pappso::SelectionPolygon::computeMinMaxCoordinates
bool computeMinMaxCoordinates()
Definition selectionpolygon.cpp:313
pappso::SelectionPolygon::m_maxX
double m_maxX
Definition selectionpolygon.h:134
pappso::SelectionPolygon::getPoints
const std::vector< QPointF > & getPoints() const
Definition selectionpolygon.cpp:299
pappso::SelectionPolygon::copyPoint
void copyPoint(PointSpec point_spec_src, PointSpec point_spec_dest)
Definition selectionpolygon.cpp:132
pappso::SelectionPolygon::m_minX
double m_minX
Definition selectionpolygon.h:131
pappso::SelectionPolygon::is2D
bool is2D() const
Definition selectionpolygon.cpp:643
pappso::SelectionPolygon::width
double width(bool &ok) const
Definition selectionpolygon.cpp:367
pappso::SelectionPolygon::getLeftMostPoint
QPointF getLeftMostPoint() const
Definition selectionpolygon.cpp:219
pappso::SelectionPolygon::operator=
SelectionPolygon & operator=(const SelectionPolygon &other)
Definition selectionpolygon.cpp:578
pappso::SelectionPolygon::getBottomMostPoint
QPointF getBottomMostPoint() const
Definition selectionpolygon.cpp:279
pappso::SelectionPolygon::set1D
void set1D(double x_range_start, double x_range_end)
Definition selectionpolygon.cpp:142
pappso::SelectionPolygon::convertTo1D
void convertTo1D()
Definition selectionpolygon.cpp:206
pappso::SelectionPolygon::isRectangle
bool isRectangle() const
Definition selectionpolygon.cpp:668
pappso::SelectionPolygon::resetPoints
void resetPoints()
Definition selectionpolygon.cpp:603
pappso::SelectionPolygon::getPoint
QPointF getPoint(PointSpec point_spec) const
Definition selectionpolygon.cpp:306
pappso::SelectionPolygon::height
double height(bool &ok) const
Definition selectionpolygon.cpp:382
pappso::SelectionPolygon::contains
bool contains(const QPointF &tested_point) const
Definition selectionpolygon.cpp:476
pappso::SelectionPolygon::SelectionPolygon
SelectionPolygon()
Definition selectionpolygon.cpp:21
pappso::SelectionPolygon::toString
QString toString() const
Definition selectionpolygon.cpp:707
pappso::SelectionPolygon::m_points
std::vector< QPointF > m_points
Definition selectionpolygon.h:122
pappso::Enums::Axis
Axis
Definition types.h:258
pappso::Enums::Axis::y
@ y
Definition types.h:261
pappso::Enums::Axis::x
@ x
Definition types.h:260
pappso
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
pappso::PeptideIonCter::y
@ y
Definition peptide.h:87
pappso::PeptideIonCter::x
@ x
Definition peptide.h:92
pappso::PointSpec
PointSpec
Definition selectionpolygon.h:24
pappso::PointSpec::BOTTOM_RIGHT_POINT
@ BOTTOM_RIGHT_POINT
Definition selectionpolygon.h:28
pappso::PointSpec::TOP_LEFT_POINT
@ TOP_LEFT_POINT
Definition selectionpolygon.h:26
pappso::PointSpec::BOTTOM_LEFT_POINT
@ BOTTOM_LEFT_POINT
Definition selectionpolygon.h:29
pappso::PointSpec::ENUM_LAST
@ ENUM_LAST
Definition selectionpolygon.h:30
pappso::PointSpec::TOP_RIGHT_POINT
@ TOP_RIGHT_POINT
Definition selectionpolygon.h:27
selectionpolygon.h