]> rtime.felk.cvut.cz Git - hubacji1/psp.git/blob - src/psp.cc
Add circle line segment intersection function
[hubacji1/psp.git] / src / psp.cc
1 #include <cmath>
2 #include <list>
3 #include <queue>
4 #include "psp.h"
5
6 bool PSPlanner::collide()
7 {
8         std::vector<std::tuple<double, double>> bc;
9         bc.push_back(std::make_tuple(this->cc().lfx(), this->cc().lfy()));
10         bc.push_back(std::make_tuple(this->cc().lrx(), this->cc().lry()));
11         bc.push_back(std::make_tuple(this->cc().rrx(), this->cc().rry()));
12         bc.push_back(std::make_tuple(this->cc().rfx(), this->cc().rfy()));
13         bc.push_back(std::make_tuple(this->cc().lfx(), this->cc().lfy()));
14         std::vector<std::tuple<double, double>> ps;
15         ps.push_back(std::make_tuple(this->ps().x1(), this->ps().y1()));
16         ps.push_back(std::make_tuple(this->ps().x2(), this->ps().y2()));
17         ps.push_back(std::make_tuple(this->ps().x3(), this->ps().y3()));
18         ps.push_back(std::make_tuple(this->ps().x4(), this->ps().y4()));
19         return std::get<0>(::collide(bc, ps));
20 }
21
22 bool PSPlanner::forward()
23 {
24         if (this->ps().parallel())
25                 return false;
26         double heading = atan2(
27                 this->ps().y2() - this->ps().y1(),
28                 this->ps().x2() - this->ps().x1()
29         );
30         while (heading < 0) heading += 2 * M_PI;
31         double h = this->gc().h();
32         while (h < 0) h += 2 * M_PI;
33         if (std::abs(heading - h) < M_PI / 4)
34                 return true;
35         return false;
36 }
37
38 void PSPlanner::gc_to_4()
39 {
40         double angl_slot = atan2(
41                 this->ps().y3() - this->ps().y4(),
42                 this->ps().x3() - this->ps().x4()
43         );
44         double angl_delta = M_PI / 2;
45         if (this->ps().right())
46                 angl_delta = -M_PI / 2;
47         double x = this->ps().x4();
48         double y = this->ps().y4();
49         x += (this->gc().dr() + 0.01) * cos(angl_slot);
50         y += (this->gc().dr() + 0.01) * sin(angl_slot);
51         x += (this->gc().w() / 2 + 0.01) * cos(angl_slot + angl_delta);
52         y += (this->gc().w() / 2 + 0.01) * sin(angl_slot + angl_delta);
53         this->gc().x(x);
54         this->gc().y(y);
55         this->gc().h(angl_slot);
56 }
57
58 std::tuple<double, double, double, double> circle_line_intersection(
59         double cx, double cy, double r,
60         double x1, double y1,
61         double x2, double y2
62 )
63 {
64         double t = (y2 - y1) / (x2 - x1);
65         //double a = 1 + pow(t, 2);
66         //double b = - 2 * cx - 2 * pow(t, 2) * x1 + 2 * t * y1 - 2 * t * cy;
67         //double c = pow(cx, 2) + pow(t, 2) * pow(x1, 2) - 2 * t * y1 * x1
68         //        + pow(y1, 2) + 2 * t * cy * x1 - 2 * y1 * cy + pow(cy, 2)
69         //        - pow(r, 2);
70         double a = 1 + pow(t, 2);
71         double b = - 2 * cx + 2 * t * (-t * x1 + y1) - 2 * cy * t;
72         double c = pow(cx, 2) + pow(cy, 2) - pow(r, 2);
73         c += pow(-t * x1 + y1, 2);
74         c += 2 * cy * t * x1 - 2 * cy * y1;
75         double D = pow(b, 2) - 4 * a * c;
76         if (D < 0)
77                 return std::make_tuple(cx, cy, cx, cy);
78         double res_x1 = (-b + sqrt(D)) / (2 * a);
79         double res_y1 = t * (res_x1 - x1) + y1;
80         double res_x2 = (-b - sqrt(D)) / (2 * a);
81         double res_y2 = t * (res_x2 - x1) + y1;
82         return std::make_tuple(res_x1, res_y1, res_x2, res_y2);
83 }
84
85 double edist(double x1, double y1, double x2, double y2)
86 {
87         return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
88 }
89
90 void PSPlanner::guess_gc()
91 {
92         double x = this->ps().x1();
93         double y = this->ps().y1();
94         double h = this->ps().heading();
95         double dts = + M_PI / 2; // direction to slot
96         if (this->ps().right())
97                 dts = - M_PI / 2;
98         if (this->ps().parallel()) {
99                 dts *= 0.99; // precision workaround
100                 x += (this->gc().w() / 2 + 0.01) * cos(h + dts);
101                 x += (this->gc().dr() + 0.01) * cos(h);
102                 y += (this->gc().w() / 2 + 0.01) * sin(h + dts);
103                 y += (this->gc().dr() + 0.01) * sin(h);
104         } else {
105                 if (std::abs(
106                         atan2(
107                                 this->ps().y2() - this->ps().y1(),
108                                 this->ps().x2() - this->ps().x1()
109                         )
110                         - this->ps().heading()
111                 ) < M_PI / 2) {
112                         // forward parking
113                         x = this->ps().x4();
114                         y = this->ps().y4();
115                         h = dts;
116                         x += (this->gc().dr() + 0.01) * cos(h);
117                         y += (this->gc().dr() + 0.01) * sin(h);
118                         if (this->ps().right())
119                                 dts -= M_PI / 2;
120                         else
121                                 dts += M_PI / 2;
122                         x += (this->gc().w() / 2 + 0.01) * cos(dts);
123                         y += (this->gc().w() / 2 + 0.01) * sin(dts);
124                 } else {
125                         dts = atan2(
126                                 this->ps().y2() - this->ps().y1(),
127                                 this->ps().x2() - this->ps().x1()
128                         );
129                         dts *= 1.01; // precision workaround
130                         // backward parking
131                         h = dts + M_PI;
132                         x += -(this->gc().df() + 0.01) * cos(h);
133                         y += -(this->gc().df() + 0.01) * sin(h);
134                         if (this->ps().right())
135                                 dts += M_PI / 2;
136                         else
137                                 dts -= M_PI / 2;
138                         x += (this->gc().w() / 2 + 0.01) * cos(dts);
139                         y += (this->gc().w() / 2 + 0.01) * sin(dts);
140                 }
141         }
142         while (h > M_PI)
143                 h -= 2 * M_PI;
144         while (h <= -M_PI)
145                 h += 2 * M_PI;
146         this->gc().x(x);
147         this->gc().y(y);
148         this->gc().h(h);
149 }
150
151 bool PSPlanner::left()
152 {
153         double lfx = this->cc().lfx();
154         double lfy = this->cc().lfy();
155         double lrx = this->cc().lrx();
156         double lry = this->cc().lry();
157         double rrx = this->cc().rrx();
158         double rry = this->cc().rry();
159         double rfx = this->cc().rfx();
160         double rfy = this->cc().rfy();
161         double lfs = sgn(
162                 (lfx - this->ps().x1()) * (this->ps().y4() - this->ps().y1())
163                 - (lfy - this->ps().y1()) * (this->ps().x4() - this->ps().x1())
164         );
165         double lrs = sgn(
166                 (lrx - this->ps().x1()) * (this->ps().y4() - this->ps().y1())
167                 - (lry - this->ps().y1()) * (this->ps().x4() - this->ps().x1())
168         );
169         double rrs = sgn(
170                 (rrx - this->ps().x1()) * (this->ps().y4() - this->ps().y1())
171                 - (rry - this->ps().y1()) * (this->ps().x4() - this->ps().x1())
172         );
173         double rfs = sgn(
174                 (rfx - this->ps().x1()) * (this->ps().y4() - this->ps().y1())
175                 - (rfy - this->ps().y1()) * (this->ps().x4() - this->ps().x1())
176         );
177         if (this->ps().parallel())
178                 return lfs == rfs && (lfs != lrs || lfs != rrs);
179         else if (!this->forward())
180                 return lfs == rfs && (lfs != lrs || lfs != rrs);
181         else
182                 return lrs == rrs && (lrs != lfs || lrs != rfs);
183 }
184
185 bool PSPlanner::parked()
186 {
187         std::vector<std::tuple<double, double>> slot;
188         slot.push_back(std::make_tuple(this->ps().x1(), this->ps().y1()));
189         slot.push_back(std::make_tuple(this->ps().x2(), this->ps().y2()));
190         slot.push_back(std::make_tuple(this->ps().x3(), this->ps().y3()));
191         slot.push_back(std::make_tuple(this->ps().x4(), this->ps().y4()));
192         return inside(this->gc().lfx(), this->gc().lfy(), slot)
193                 && inside(this->gc().lrx(), this->gc().lry(), slot)
194                 && inside(this->gc().rrx(), this->gc().rry(), slot)
195                 && inside(this->gc().rfx(), this->gc().rfy(), slot);
196 }
197
198 std::vector<BicycleCar> PSPlanner::possible_inits(
199         unsigned int cnt,
200         double dist
201 )
202 {
203         std::vector<BicycleCar> pi;
204         this->cc().sp(this->cc().sp() * dist);
205         this->cc().st(this->cc().st() * 1);
206         BicycleCar orig_cc(this->cc());
207         for (unsigned int i = 0; i < cnt; i++) {
208                 this->cc().next();
209                 pi.push_back(BicycleCar(this->cc()));
210         }
211         this->cc() = BicycleCar(orig_cc);
212         return pi;
213 }
214
215 // find entry
216 void PSPlanner::fe()
217 {
218         if (this->ps().parallel())
219                 return this->fe_parallel();
220         else
221                 return this->fe_perpendicular();
222 }
223
224 void PSPlanner::fe_parallel()
225 {
226         // angle for distance from "entry" corner
227         double dist_angl = this->ps().heading() + M_PI;
228         dist_angl += (this->ps().right()) ? - M_PI / 4 : + M_PI / 4;
229         // set bicycle car `bci` basic dimensions and heading
230         BicycleCar bci = BicycleCar(this->gc());
231         BicycleCar bco = BicycleCar(this->gc());
232         bci.h(this->ps().heading());
233         // move 0.01 from the "entry" corner
234         bci.x(this->ps().x4() + 0.01 * cos(dist_angl));
235         bci.y(this->ps().y4() + 0.01 * sin(dist_angl));
236         // align with parking "top" of slot (move backward)
237         dist_angl = bci.h() + M_PI;
238         bci.x(bci.x() + bci.df() * cos(dist_angl));
239         bci.y(bci.y() + bci.df() * sin(dist_angl));
240         // align with "entry" to pakring slot (move outside)
241         dist_angl = this->ps().heading();
242         dist_angl += (this->ps().right()) ? + M_PI / 2 : - M_PI / 2;
243         bci.x(bci.x() + bci.w() / 2 * cos(dist_angl));
244         bci.y(bci.y() + bci.w() / 2 * sin(dist_angl));
245         // set default speed, steer
246         bci.st(bci.wb() / bci.mtr());
247         if (!this->ps().right())
248                 bci.st(bci.st() * -1);
249         bci.sp(-0.01);
250         // BFS - init all starts
251         // see https://courses.cs.washington.edu/courses/cse326/03su/homework/hw3/bfs.html
252         double dist_diag = sqrt(pow(bci.w() / 2, 2) + pow(bci.df(), 2));
253         if (this->ps().right())
254                 dist_angl = atan2(bci.y() - bci.rfy(), bci.x() - bci.rfx());
255         else
256                 dist_angl = atan2(bci.y() - bci.lfy(), bci.x() - bci.lfx());
257         double DIST_ANGL = dist_angl;
258         std::queue<BicycleCar, std::list<BicycleCar>> q;
259         while (
260                 (
261                         this->ps().right()
262                         && dist_angl < DIST_ANGL + 3 * M_PI / 4
263                 )
264                 || (
265                         !this->ps().right()
266                         && dist_angl > DIST_ANGL - 3 * M_PI / 4
267                 )
268         ) {
269                 this->cc() = BicycleCar(bci);
270                 if (this->ps().right()) {
271                         this->cc().x(bci.rfx() + dist_diag * cos(dist_angl));
272                         this->cc().y(bci.rfy() + dist_diag * sin(dist_angl));
273                 } else {
274                         this->cc().x(bci.lfx() + dist_diag * cos(dist_angl));
275                         this->cc().y(bci.lfy() + dist_diag * sin(dist_angl));
276                 }
277                 this->cc().h(this->ps().heading() + dist_angl - DIST_ANGL);
278                 if (!this->collide()) {
279                         q.push(BicycleCar(this->cc()));
280                 }
281                 dist_angl += (this->ps().right()) ? + 0.01 : - 0.01;
282         }
283         // BFS - find entry current car `cc` and corresponding goal car `gc`
284         unsigned int iter_cntr = 0;
285         while (!q.empty() && iter_cntr < 9) {
286                 this->cc() = BicycleCar(q.front());
287                 q.pop();
288                 while (
289                         !this->collide()
290                         && (std::abs(
291                                 this->cc().h() - this->ps().heading()
292                         ) > M_PI / 32)
293                         && (std::abs(
294                                 this->cc().h() - this->ps().heading()
295                         ) < M_PI / 2)
296                 )
297                         this->cc().next();
298                 this->cc().sp(this->cc().sp() * -1);
299                 this->cc().next();
300                 this->gc() = BicycleCar(this->cc());
301                 if (this->parked())
302                         goto successfinish;
303                 this->cc().st(this->cc().st() * -1);
304                 q.push(BicycleCar(this->cc()));
305                 if (sgn(this->cc().st()) == sgn(q.front().st()))
306                         iter_cntr++;
307         }
308         // fallback to fer
309         this->gc() = BicycleCar(bco);
310 successfinish:
311         return this->fer_parallel();
312 }
313
314 void PSPlanner::fe_perpendicular()
315 {
316         // TODO Try multiple angles when going from parking slot.
317         //
318         //      Do not use just the maximum steer angle. Test angles
319         //      until the whole current car `cc` is out of the parking
320         //      slot `ps`.
321         //
322         //      Another approach could be testing angles from the
323         //      beginning of the escape parkig slot maneuver.
324         if (this->forward())
325                 this->cc().sp(-0.01);
326         else
327                 this->cc().sp(0.01);
328         while (!this->left())
329                 this->cc().next();
330         return;
331 }
332
333 void PSPlanner::fer()
334 {
335         if (this->ps().parallel())
336                 return this->fer_parallel();
337         else
338                 return this->fer_perpendicular();
339 }
340
341 void PSPlanner::fer_parallel()
342 {
343         this->cc().st(this->cc().wb() / this->cc().mtr());
344         if (!this->ps().right())
345                 this->cc().st(this->cc().st() * -1);
346         this->cc().sp(0.01);
347         while (!this->left()) {
348                 while (!this->collide() && !this->left())
349                         this->cc().next();
350                 if (this->left() && !this->collide()) {
351                         break;
352                 } else {
353                         this->cc().sp(this->cc().sp() * -1);
354                         this->cc().next();
355                         this->cc().st(this->cc().st() * -1);
356                 }
357         }
358 }
359
360 void PSPlanner::fer_perpendicular()
361 {
362         bool delta_use[] = {true, true, true};
363         double cc_h = this->cc().h();
364         double x;
365         double y;
366         // check inner radius
367         if (this->forward()) {
368                 x = this->ps().x1();
369                 y = this->ps().y1();
370         } else {
371                 x = this->ps().x4();
372                 y = this->ps().y4();
373         }
374         double x1;
375         double y1;
376         if (this->ps().right()) {
377                 x1 = this->cc().ccr().x();
378                 y1 = this->cc().ccr().y();
379         } else {
380                 x1 = this->cc().ccl().x();
381                 y1 = this->cc().ccl().y();
382         }
383         double IR = this->cc().iradi();
384         double a = 1;
385         double b;
386         if (this->forward())
387                 b = (x - x1) * 2 * cos(cc_h) + (y - y1) * 2 * sin(cc_h);
388         else
389                 b = (x1 - x) * 2 * cos(cc_h) + (y1 - y) * 2 * sin(cc_h);
390         double c = pow(x - x1, 2) + pow(y - y1, 2) - pow(IR, 2);
391         double D = pow(b, 2) - 4 * a * c;
392         double delta;
393         delta = -b - sqrt(D);
394         delta /= 2 * a;
395         double delta_1 = delta;
396         if (D < 0)
397                 delta_use[0] = false;
398         // check outer radius
399         if (this->forward()) {
400                 x = this->ps().x4();
401                 y = this->ps().y4();
402         } else {
403                 x = this->ps().x1();
404                 y = this->ps().y1();
405         }
406         IR = this->cc().ofradi();
407         a = 1;
408         if (this->forward())
409                 b = (x - x1) * 2 * cos(cc_h) + (y - y1) * 2 * sin(cc_h);
410         else
411                 b = (x1 - x) * 2 * cos(cc_h) + (y1 - y) * 2 * sin(cc_h);
412         c = pow(x - x1, 2) + pow(y - y1, 2) - pow(IR, 2);
413         D = pow(b, 2) - 4 * a * c;
414         if (this->forward()) {
415                 delta = -b + sqrt(D);
416                 delta /= 2 * a;
417         }
418         double delta_2 = delta;
419         if (D < 0)
420                 delta_use[1] = false;
421         delta = -b - sqrt(D);
422         delta /= 2 * a;
423         double delta_3 = delta;
424         if (D < 0)
425                 delta_use[2] = false;
426         if (delta_use[0] && delta_use[1] && delta_use[22])
427                 delta = std::max(delta_1, std::max(delta_2, delta_3));
428         else if (delta_use[0] && delta_use[1])
429                 delta = std::max(delta_1, delta_2);
430         else if (delta_use[0] && delta_use[2])
431                 delta = std::max(delta_1, delta_3);
432         else if (delta_use[1] && delta_use[2])
433                 delta = std::max(delta_2, delta_3);
434         else if (delta_use[0])
435                 delta = delta_1;
436         else if (delta_use[1])
437                 delta = delta_2;
438         else if (delta_use[2])
439                 delta = delta_3;
440         else
441                 return;
442         // current car `cc` can get out of slot with max steer
443         this->cc().x(this->cc().x() + delta * cos(cc_h));
444         this->cc().y(this->cc().y() + delta * sin(cc_h));
445         this->cc().h(cc_h);
446         // get current car `cc` out of slot
447         if (this->forward())
448                 this->cc().sp(-0.01);
449         else
450                 this->cc().sp(0.01);
451         this->cc().st(this->cc().wb() / this->cc().mtr());
452         if (this->ps().right())
453                 this->cc().st(this->cc().st() * -1);
454         while (!this->left()) {
455                 while (!this->collide() && !this->left())
456                         this->cc().next();
457                 if (this->left() && !this->collide()) {
458                         break;
459                 } else {
460                         this->cc().sp(this->cc().sp() * -1);
461                         this->cc().next();
462                         this->cc().st(this->cc().st() * -1);
463                 }
464         }
465 }
466
467 PSPlanner::PSPlanner()
468 {
469 }