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