Fixed integer overflow in LinearAlg2D::isInsideCorner().

This commit is contained in:
Lukáš Hejl 2022-03-28 12:57:33 +02:00
parent b97c05176a
commit e99b579f93

View File

@ -16,9 +16,8 @@ namespace Slic3r::Arachne::LinearAlg2D
* *
* Test whether the \p query_point is inside of a polygon w.r.t a single corner. * Test whether the \p query_point is inside of a polygon w.r.t a single corner.
*/ */
inline static bool isInsideCorner(const Point a, const Point b, const Point c, const Vec2i64 query_point) { inline static bool isInsideCorner(const Point &a, const Point &b, const Point &c, const Vec2i64 &query_point)
{
// Visualisation for the algorithm below: // Visualisation for the algorithm below:
// //
// query // query
@ -32,47 +31,39 @@ inline static bool isInsideCorner(const Point a, const Point b, const Point c, c
// a c // a c
// //
auto normal = [](const Point& p0, coord_t len) -> Point auto normal = [](const Point &p0, coord_t len) -> Point {
{
int64_t _len = p0.cast<int64_t>().norm(); int64_t _len = p0.cast<int64_t>().norm();
if (_len < 1) if (_len < 1)
return Point(len, 0); return {len, 0};
return (p0.cast<int64_t>() * int64_t(len) / _len).cast<coord_t>(); return (p0.cast<int64_t>() * int64_t(len) / _len).cast<coord_t>();
}; };
auto rotate_90_degree_ccw = [](const Vec2i64 &p) -> Vec2i64 { auto rotate_90_degree_ccw = [](const Vec2d &p) -> Vec2d {
return Vec2i64(-p.y(), p.x()); return {-p.y(), p.x()};
}; };
constexpr coord_t normal_length = 10000; //Create a normal vector of reasonable length in order to reduce rounding error. constexpr coord_t normal_length = 10000; //Create a normal vector of reasonable length in order to reduce rounding error.
const Point ba = normal(a - b, normal_length); const Point ba = normal(a - b, normal_length);
const Point bc = normal(c - b, normal_length); const Point bc = normal(c - b, normal_length);
const Vec2i64 bq = query_point - b.cast<int64_t>(); const Vec2d bq = query_point.cast<double>() - b.cast<double>();
const Vec2i64 perpendicular = rotate_90_degree_ccw(bq); //The query projects to this perpendicular to coordinate 0. const Vec2d perpendicular = rotate_90_degree_ccw(bq); //The query projects to this perpendicular to coordinate 0.
assert(ba.cast<double>().dot(perpendicular.cast<double>()) <= double(std::numeric_limits<int64_t>::max()) && ba.cast<double>().dot(perpendicular.cast<double>()) >= double(std::numeric_limits<int64_t>::lowest())); const double project_a_perpendicular = ba.cast<double>().dot(perpendicular); //Project vertex A on the perpendicular line.
assert(bc.cast<double>().dot(perpendicular.cast<double>()) <= double(std::numeric_limits<int64_t>::max()) && bc.cast<double>().dot(perpendicular.cast<double>()) >= double(std::numeric_limits<int64_t>::lowest())); const double project_c_perpendicular = bc.cast<double>().dot(perpendicular); //Project vertex C on the perpendicular line.
if ((project_a_perpendicular > 0.) != (project_c_perpendicular > 0.)) //Query is between A and C on the projection.
const int64_t project_a_perpendicular = ba.cast<int64_t>().dot(perpendicular); //Project vertex A on the perpendicular line.
const int64_t project_c_perpendicular = bc.cast<int64_t>().dot(perpendicular); //Project vertex C on the perpendicular line.
if ((project_a_perpendicular > 0) != (project_c_perpendicular > 0)) //Query is between A and C on the projection.
{ {
return project_a_perpendicular > 0; //Due to the winding order of corner ABC, this means that the query is inside. return project_a_perpendicular > 0.; //Due to the winding order of corner ABC, this means that the query is inside.
} }
else //Beyond either A or C, but it could still be inside of the polygon. else //Beyond either A or C, but it could still be inside of the polygon.
{ {
assert(ba.cast<double>().dot(bq.cast<double>()) <= double(std::numeric_limits<int64_t>::max()) && ba.cast<double>().dot(bq.cast<double>()) >= double(std::numeric_limits<int64_t>::lowest())); const double project_a_parallel = ba.cast<double>().dot(bq); //Project not on the perpendicular, but on the original.
assert(bc.cast<double>().dot(bq.cast<double>()) <= double(std::numeric_limits<int64_t>::max()) && bc.cast<double>().dot(bq.cast<double>()) >= double(std::numeric_limits<int64_t>::lowest())); const double project_c_parallel = bc.cast<double>().dot(bq);
const int64_t project_a_parallel = ba.cast<int64_t>().dot(bq); //Project not on the perpendicular, but on the original.
const int64_t project_c_parallel = bc.cast<int64_t>().dot(bq);
//Either: //Either:
// * A is to the right of B (project_a_perpendicular > 0) and C is below A (project_c_parallel < project_a_parallel), or // * A is to the right of B (project_a_perpendicular > 0) and C is below A (project_c_parallel < project_a_parallel), or
// * A is to the left of B (project_a_perpendicular < 0) and C is above A (project_c_parallel > project_a_parallel). // * A is to the left of B (project_a_perpendicular < 0) and C is above A (project_c_parallel > project_a_parallel).
return (project_c_parallel < project_a_parallel) == (project_a_perpendicular > 0); return (project_c_parallel < project_a_parallel) == (project_a_perpendicular > 0.);
} }
} }
/*! /*!
@ -86,7 +77,7 @@ inline static bool isInsideCorner(const Point a, const Point b, const Point c, c
* \param b the to point of the line * \param b the to point of the line
* \return a positive value when \p p lies to the left of the line from \p a to \p b * \return a positive value when \p p lies to the left of the line from \p a to \p b
*/ */
static inline int64_t pointIsLeftOfLine(const Point& p, const Point& a, const Point& b) static inline int64_t pointIsLeftOfLine(const Point &p, const Point &a, const Point &b)
{ {
return int64_t(b.x() - a.x()) * int64_t(p.y() - a.y()) - int64_t(b.y() - a.y()) * int64_t(p.x() - a.x()); return int64_t(b.x() - a.x()) * int64_t(p.y() - a.y()) - int64_t(b.y() - a.y()) * int64_t(p.x() - a.x());
} }
@ -108,7 +99,7 @@ static inline int64_t pointIsLeftOfLine(const Point& p, const Point& a, const Po
* \param c end of second line segment * \param c end of second line segment
* \return the angle in radians between 0 and 2 * pi of the corner in \p b * \return the angle in radians between 0 and 2 * pi of the corner in \p b
*/ */
static inline float getAngleLeft(const Point& a, const Point& b, const Point& c) static inline float getAngleLeft(const Point &a, const Point &b, const Point &c)
{ {
const Vec2i64 ba = (a - b).cast<int64_t>(); const Vec2i64 ba = (a - b).cast<int64_t>();
const Vec2i64 bc = (c - b).cast<int64_t>(); const Vec2i64 bc = (c - b).cast<int64_t>();