1 //Copyright (c) 2018 Ultimaker B.V. 2 //CuraEngine is released under the terms of the AGPLv3 or higher. 3 4 #ifndef UTILS_LINEAR_ALG_2D_H 5 #define UTILS_LINEAR_ALG_2D_H 6 7 #include "IntPoint.h" 8 9 namespace cura 10 { 11 class LinearAlg2D 12 { 13 public: pointLiesOnTheRightOfLine(const Point & p,const Point & p0,const Point & p1)14 static short pointLiesOnTheRightOfLine(const Point& p, const Point& p0, const Point& p1) 15 { 16 // no tests unless the segment p0-p1 is at least partly at, or to right of, p.X 17 if ( std::max(p0.X, p1.X) >= p.X ) 18 { 19 const coord_t pd_y = p1.Y - p0.Y; 20 if (pd_y < 0) // p0->p1 is 'falling' 21 { 22 if (p1.Y <= p.Y && p0.Y > p.Y) // candidate 23 { 24 // dx > 0 if intersection is to right of p.X 25 const coord_t dx = (p1.X - p0.X) * (p1.Y - p.Y) - (p1.X - p.X) * pd_y; 26 if (dx == 0) // includes p == p1 27 { 28 return 0; 29 } 30 if (dx > 0) 31 { 32 return 1; 33 } 34 } 35 } 36 else if (p.Y >= p0.Y) 37 { 38 if (p.Y < p1.Y) // candidate for p0->p1 'rising' and includes p.Y 39 { 40 // dx > 0 if intersection is to right of p.X 41 const coord_t dx = (p1.X - p0.X) * (p.Y - p0.Y) - (p.X - p0.X) * pd_y; 42 if (dx == 0) // includes p == p0 43 { 44 return 0; 45 } 46 if (dx > 0) 47 { 48 return 1; 49 } 50 } 51 else if (p.Y == p1.Y) 52 { 53 // some special cases here, points on border: 54 // - p1 exactly matches p (might otherwise be missed) 55 // - p0->p1 exactly horizontal, and includes p. 56 // (we already tested std::max(p0.X,p1.X) >= p.X ) 57 if (p.X == p1.X || 58 (pd_y == 0 && std::min(p0.X, p1.X) <= p.X) ) 59 { 60 return 0; 61 } 62 } 63 } 64 } 65 return -1; 66 67 } 68 69 /*! 70 * Find whether a point projected on a line segment would be projected to 71 * - properly on the line : zero returned 72 * - closer to \p a : -1 returned 73 * - closer to \p b : 1 returned 74 * 75 * \param from The point to check in relation to the line segment 76 * \param a The start point of the line segment 77 * \param b The end point of the line segment 78 * \return the sign of the projection wrt the line segment 79 */ pointIsProjectedBeyondLine(const Point & from,const Point & a,const Point & b)80 inline static short pointIsProjectedBeyondLine(const Point& from, const Point& a, const Point& b) 81 { 82 const Point vec = b - a; 83 const Point point_vec = from - a; 84 const coord_t dot_prod = dot(point_vec, vec); 85 if (dot_prod < 0) 86 { // point is projected to before ab 87 return -1; 88 } 89 if (dot_prod > vSize2(vec)) 90 { // point is projected to after ab 91 return 1; 92 } 93 return 0; 94 } 95 96 /*! 97 * Find the point closest to \p from on the line from \p p0 to \p p1 98 */ getClosestOnLineSegment(const Point & from,const Point & p0,const Point & p1)99 static Point getClosestOnLineSegment(const Point& from, const Point& p0, const Point& p1) 100 { 101 const Point direction = p1 - p0; 102 const Point to_from = from - p0; 103 const coord_t projected_x = dot(to_from, direction) ; 104 105 const coord_t x_p0 = 0; 106 const coord_t x_p1 = vSize2(direction); 107 108 if (x_p1 == 0) 109 { 110 //Line segment has length 0. 111 return p0; 112 } 113 if (projected_x <= x_p0) 114 { 115 //Projection is beyond p0. 116 return p0; 117 } 118 if (projected_x >= x_p1) 119 { 120 //Projection is beyond p1. 121 return p1; 122 } 123 else 124 { 125 //Projection is between p0 and p1. 126 //Return direction-normalised projection (projected_x / vSize(direction)) on direction vector. 127 //vSize(direction) * vSize(direction) == vSize2(direction) == x_p1. 128 return p0 + projected_x * direction / x_p1; 129 } 130 } 131 132 /*! 133 * Find the two points on two line segments closest to each other. 134 * 135 * Find the smallest line segment connecting the two line segments a and b. 136 * 137 * \param a1 first point on line a 138 * \param a2 second point on line a 139 * \param b1 first point on line b 140 * \param b2 second point on line b 141 * \return A pair: the first point on line a and the second pouint on line b 142 */ 143 static std::pair<Point, Point> getClosestConnection(Point a1, Point a2, Point b1, Point b2); 144 145 /*! 146 * Get the squared distance from point \p b to a line *segment* from \p a to \p c. 147 * 148 * In case \p b is on \p a or \p c, \p b_is_beyond_ac should become 0. 149 * 150 * \param a the first point of the line segment 151 * \param b the point to measure the distance from 152 * \param c the second point on the line segment 153 * \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1) 154 */ 155 static coord_t getDist2FromLineSegment(const Point& a, const Point& b, const Point& c, int16_t* b_is_beyond_ac = nullptr) 156 { 157 /* 158 * a, 159 * /| 160 * / | 161 * b,/__|, x 162 * \ | 163 * \ | 164 * \| 165 * 'c 166 * 167 * x = b projected on ac 168 * ax = ab dot ac / vSize(ac) 169 * xb = ab - ax 170 * error = vSize(xb) 171 */ 172 const Point ac = c - a; 173 const coord_t ac_size = vSize(ac); 174 175 const Point ab = b - a; 176 if (ac_size == 0) 177 { 178 const coord_t ab_dist2 = vSize2(ab); 179 if (ab_dist2 == 0 && b_is_beyond_ac) 180 { 181 *b_is_beyond_ac = 0; // a is on b is on c 182 } 183 // otherwise variable b_is_beyond_ac remains its value; it doesn't make sense to choose between -1 and 1 184 return ab_dist2; 185 } 186 const coord_t projected_x = dot(ab, ac); 187 const coord_t ax_size = projected_x / ac_size; 188 189 if (ax_size < 0) 190 {// b is 'before' segment ac 191 if (b_is_beyond_ac) 192 { 193 *b_is_beyond_ac = -1; 194 } 195 return vSize2(ab); 196 } 197 if (ax_size > ac_size) 198 {// b is 'after' segment ac 199 if (b_is_beyond_ac) 200 { 201 *b_is_beyond_ac = 1; 202 } 203 return vSize2(b - c); 204 } 205 206 if (b_is_beyond_ac) 207 { 208 *b_is_beyond_ac = 0; 209 } 210 const Point ax = ac * ax_size / ac_size; 211 const Point bx = ab - ax; 212 return vSize2(bx); 213 // return vSize2(ab) - ax_size*ax_size; // less accurate 214 } 215 216 /*! 217 * Checks whether the minimal distance between two line segments is at most \p max_dist 218 * The first line semgent is given by end points \p a and \p b, the second by \p c and \p d. 219 * 220 * \param a One end point of the first line segment 221 * \param b Another end point of the first line segment 222 * \param c One end point of the second line segment 223 * \param d Another end point of the second line segment 224 * \param max_dist The maximal distance between the two line segments for which this function will return true. 225 */ lineSegmentsAreCloserThan(const Point & a,const Point & b,const Point & c,const Point & d,const coord_t max_dist)226 static bool lineSegmentsAreCloserThan(const Point& a, const Point& b, const Point& c, const Point& d, const coord_t max_dist) 227 { 228 const coord_t max_dist2 = max_dist * max_dist; 229 230 return getDist2FromLineSegment(a, c, b) <= max_dist2 231 || getDist2FromLineSegment(a, d, b) <= max_dist2 232 || getDist2FromLineSegment(c, a, d) <= max_dist2 233 || getDist2FromLineSegment(c, b, d) <= max_dist2; 234 } 235 236 /*! 237 * Get the minimal distance between two line segments 238 * The first line semgent is given by end points \p a and \p b, the second by \p c and \p d. 239 * 240 * \param a One end point of the first line segment 241 * \param b Another end point of the first line segment 242 * \param c One end point of the second line segment 243 * \param d Another end point of the second line segment 244 */ getDist2BetweenLineSegments(const Point & a,const Point & b,const Point & c,const Point & d)245 static coord_t getDist2BetweenLineSegments(const Point& a, const Point& b, const Point& c, const Point& d) 246 { 247 return 248 std::min(getDist2FromLineSegment(a, c, b), 249 std::min(getDist2FromLineSegment(a, d, b), 250 std::min(getDist2FromLineSegment(c, a, d), 251 getDist2FromLineSegment(c, b, d)))); 252 } 253 254 /*! 255 * Check whether two line segments collide. 256 * 257 * \warning Edge cases (end points of line segments fall on other line segment) register as a collision. 258 * 259 * \note All points are assumed to be transformed by the transformation matrix of the vector from \p a_from to \p a_to. 260 * I.e. a is a vertical line; the Y of \p a_from_transformed is the same as the Y of \p a_to_transformed. 261 * 262 * \param a_from_transformed The transformed from location of line a 263 * \param a_from_transformed The transformed to location of line a 264 * \param b_from_transformed The transformed from location of line b 265 * \param b_from_transformed The transformed to location of line b 266 * \return Whether the two line segments collide 267 */ 268 static bool lineSegmentsCollide(const Point& a_from_transformed, const Point& a_to_transformed, Point b_from_transformed, Point b_to_transformed); 269 270 /*! 271 * Compute the angle between two consecutive line segments. 272 * 273 * The angle is computed from the left side of b when looking from a. 274 * 275 * c 276 * \ . 277 * \ b 278 * angle| 279 * | 280 * a 281 * 282 * \param a start of first line segment 283 * \param b end of first segment and start of second line segment 284 * \param c end of second line segment 285 * \return the angle in radians between 0 and 2 * pi of the corner in \p b 286 */ 287 static float getAngleLeft(const Point& a, const Point& b, const Point& c); 288 289 /*! 290 * Returns the determinant of the 2D matrix defined by the the vectors ab and ap as rows. 291 * 292 * The returned value is zero for \p p lying (approximately) on the line going through \p a and \p b 293 * The value is positive for values lying to the left and negative for values lying to the right when looking from \p a to \p b. 294 * 295 * \param p the point to check 296 * \param a the from point of the line 297 * \param b the to point of the line 298 * \return a positive value when \p p lies to the left of the line from \p a to \p b 299 */ pointIsLeftOfLine(const Point & p,const Point & a,const Point & b)300 static inline coord_t pointIsLeftOfLine(const Point& p, const Point& a, const Point& b) 301 { 302 return (b.X - a.X) * (p.Y - a.Y) - (b.Y - a.Y) * (p.X - a.X); 303 } 304 305 /*! 306 * Get a point on the line segment (\p a - \p b)with a given distance to point \p p 307 * 308 * In case there are two possible point that meet the criteria, choose the one closest to a. 309 * 310 * \param p The reference point 311 * \param a Start of the line segment 312 * \param b End of the line segment 313 * \param dist The required distance of \p result to \p p 314 * \param[out] result The result (if any was found) 315 * \return Whether any such point has been found 316 */ 317 static bool getPointOnLineWithDist(const Point& p, const Point& a, const Point& b, const coord_t dist, Point& result); 318 319 /*! 320 * Get the squared distance from a point \p p to the line on which \p a and 321 * \p b lie 322 * \param p The point to measure the distance from. 323 * \param a One of the points through which the line goes. 324 * \param b One of the points through which the line goes. 325 * \return The distance between the point and the line, squared. 326 */ 327 static coord_t getDist2FromLine(const Point& p, const Point& a, const Point& b); 328 329 /*! 330 * Check whether a corner is acute or obtuse. 331 * 332 * This function is irrespective of the order between \p a and \p c; 333 * the lowest angle among bot hsides of the corner is always chosen. 334 * 335 * isAcuteCorner(a, b, c) === isAcuteCorner(c, b, a) 336 * 337 * \param a start of first line segment 338 * \param b end of first segment and start of second line segment 339 * \param c end of second line segment 340 * \return positive if acute, negative if obtuse, zero if 90 degree corner 341 */ isAcuteCorner(const Point & a,const Point & b,const Point & c)342 static inline int isAcuteCorner(const Point& a, const Point& b, const Point& c) 343 { 344 const Point ba = a - b; 345 const Point bc = c - b; 346 return dot(ba, bc); 347 } 348 349 /*! 350 * Get the rotation matrix for rotating around a specific point in place. 351 */ rotateAround(const Point & middle,double rotation)352 static Point3Matrix rotateAround(const Point& middle, double rotation) 353 { 354 PointMatrix rotation_matrix(rotation); 355 Point3Matrix rotation_matrix_homogeneous(rotation_matrix); 356 return Point3Matrix::translate(middle).compose(rotation_matrix_homogeneous).compose(Point3Matrix::translate(-middle)); 357 } 358 }; 359 360 361 362 }//namespace cura 363 #endif//UTILS_LINEAR_ALG_2D_H 364