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.
*/
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:
//
// query
@ -32,47 +31,39 @@ inline static bool isInsideCorner(const Point a, const Point b, const Point c, 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();
if (_len < 1)
return Point(len, 0);
return {len, 0};
return (p0.cast<int64_t>() * int64_t(len) / _len).cast<coord_t>();
};
auto rotate_90_degree_ccw = [](const Vec2i64 &p) -> Vec2i64 {
return Vec2i64(-p.y(), p.x());
auto rotate_90_degree_ccw = [](const Vec2d &p) -> Vec2d {
return {-p.y(), p.x()};
};
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 bc = normal(c - b, normal_length);
const Vec2i64 bq = query_point - b.cast<int64_t>();
const Vec2i64 perpendicular = rotate_90_degree_ccw(bq); //The query projects to this perpendicular to coordinate 0.
const Vec2d bq = query_point.cast<double>() - b.cast<double>();
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()));
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 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.
const double project_a_perpendicular = ba.cast<double>().dot(perpendicular); //Project vertex A on the perpendicular line.
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.
{
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.
{
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()));
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 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);
const double project_a_parallel = ba.cast<double>().dot(bq); //Project not on the perpendicular, but on the original.
const double project_c_parallel = bc.cast<double>().dot(bq);
//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 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
* \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());
}
@ -108,7 +99,7 @@ static inline int64_t pointIsLeftOfLine(const Point& p, const Point& a, const Po
* \param c end of second line segment
* \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 bc = (c - b).cast<int64_t>();