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