]> rtime.felk.cvut.cz Git - hubacji1/iamcar.git/blob - decision_control/slotplanner.cc
Add list of goals
[hubacji1/iamcar.git] / decision_control / slotplanner.cc
1 /*
2 This file is part of I am car.
3
4 I am car is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 I am car is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with I am car. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <algorithm>
19 #include <cmath>
20 #include <list>
21 #include <queue>
22 #include "slotplanner.h"
23
24 ParallelSlot::ParallelSlot()
25 {}
26
27 // getter
28 std::vector<RRTNode *> &ParallelSlot::goals()
29 {
30         return this->goals_;
31 }
32
33 RRTNode *ParallelSlot::getMidd()
34 {
35         if (this->cusp().size() > 0)
36                 return this->cusp().front().front();
37         else
38                 return nullptr;
39 }
40
41 std::vector<std::vector<RRTNode *>> &ParallelSlot::cusp()
42 {
43         return this->cusp_;
44 }
45
46 float ParallelSlot::DH() const
47 {
48         return this->DH_;
49 }
50
51 PolygonObstacle &ParallelSlot::slot()
52 {
53         return this->slot_;
54 }
55
56 float ParallelSlot::slotHeading()
57 {
58         return this->slotHeading_;
59 }
60
61 SlotSide ParallelSlot::slotSide()
62 {
63         return this->slotSide_;
64 }
65
66 SlotType ParallelSlot::slotType()
67 {
68         return this->slotType_;
69 }
70
71 // setter
72 void ParallelSlot::DH(float dh)
73 {
74         this->DH_ = dh;
75 }
76
77 void ParallelSlot::setAll()
78 {
79         // slot heading
80         float y0 = this->slot().bnodes()[0]->y();
81         float x0 = this->slot().bnodes()[0]->x();
82         float y3 = this->slot().bnodes()[3]->y();
83         float x3 = this->slot().bnodes()[3]->x();
84         float dy = y3 - y0;
85         float dx = x3 - x0;
86         this->slotHeading_ = atan2(dy, dx);
87         // slot side
88         float y1 = this->slot().bnodes()[1]->y();
89         float x1 = this->slot().bnodes()[1]->x();
90         if (sgn((x1 - x0) * (y3 - y0) - (y1 - y0) * (x3 - x0)) < 0)
91                 this->slotSide_ = LEFT;
92         else
93                 this->slotSide_ = RIGHT;
94         // slot type
95         float d1 = EDIST(
96                 this->slot().bnodes()[0],
97                 this->slot().bnodes()[1]
98         );
99         float d2 = EDIST(
100                 this->slot().bnodes()[1],
101                 this->slot().bnodes()[2]
102         );
103         if (d1 > d2)
104                 this->slotType_ = PERPENDICULAR;
105         else
106                 this->slotType_ = PARALLEL;
107 }
108
109 // other
110 void ParallelSlot::fip(
111         std::vector<CircleObstacle>& co,
112         std::vector<SegmentObstacle>& so
113 )
114 {
115         this->setAll();
116         if (this->slotType() == PERPENDICULAR) {
117                 // TODO different slot headings
118                 // this is jus for slot heading = pi / 2
119                 this->DH(0.01 / BCAR_TURNING_RADIUS);
120                 BicycleCar *perc = nullptr;
121                 RRTNode *cc = nullptr;
122                 BicycleCar *p = nullptr;
123                 int i = 0;
124                 float x;
125                 float h;
126                 float y;
127                 // parking backward
128                 x = this->slot().bnodes()[3]->x();
129                 y = this->slot().bnodes()[3]->y();
130                 h = 0;
131                 // -> top
132                 x -= BCAR_DIST_FRONT;
133                 y = this->slot().bnodes()[3]->y();
134                 y += BCAR_OUT_RRADI - BCAR_TURNING_RADIUS;
135                 if (perc)
136                         delete perc;
137                 perc = new BicycleCar(x, y, h);
138                 if (cc)
139                         delete cc;
140                 cc = perc->ccl();
141                 if (p)
142                         delete p;
143                 p = perc->move(cc, i * this->DH());
144                 while (p->x() < 0) {
145                         delete p;
146                         p = perc->move(cc, i * this->DH());
147                         i++;
148                 }
149                 // -> bottom
150                 // (reset for parking backward)
151                 x = this->slot().bnodes()[0]->x();
152                 y = this->slot().bnodes()[0]->y();
153                 h = 0;
154                 // -> bottom
155                 x -= BCAR_DIST_FRONT;
156                 // get y from quadratic equation
157                 float tmpD = pow(-2 * this->slot().bnodes()[0]->y(), 2);
158                 tmpD -= 4 * (
159                         pow(x - this->slot().bnodes()[0]->x(), 2) +
160                         pow(this->slot().bnodes()[0]->y(), 2) -
161                         pow(BCAR_IN_RADI, 2)
162                 );
163                 y = 2 * this->slot().bnodes()[0]->y();
164                 y += sqrt(tmpD);
165                 y /= 2;
166                 y -= BCAR_TURNING_RADIUS;
167                 // -- end of quadratic equation
168                 if (perc)
169                         delete perc;
170                 perc = new BicycleCar(x, y, h);
171                 if (cc)
172                         delete cc;
173                 cc = perc->ccl();
174                 if (p)
175                         delete p;
176                 p = perc->move(cc, i * this->DH());
177                 while (p->x() < 0) {
178                         delete p;
179                         p = perc->move(cc, i * this->DH());
180                         i++;
181                 }
182                 // store nodes
183                 std::vector<RRTNode *> cusp;
184                 cusp.push_back(new RRTNode(p->x(), p->y(), p->h()));
185                 cusp.push_back(new RRTNode(x, y, h));
186                 this->cusp().push_back(cusp);
187                 return;
188         }
189         // see https://courses.cs.washington.edu/courses/cse326/03su/homework/hw3/bfs.html
190         // RRTNode.s() works as iteration level
191         std::queue<BicycleCar *, std::list<BicycleCar *>> q;
192         std::queue<BicycleCar *, std::list<BicycleCar *>> empty;
193         int di = -1;
194         if (this->slotSide() == LEFT)
195                 di = 1;
196         BicycleCar *CC = this->getEPC();
197         BicycleCar *B = this->getEP();
198         this->DH(di * 0.01 / CC->out_radi());
199         BicycleCar *c;
200         int i = 0;
201         c = B->move(CC, -i * di * 0.01 / CC->diag_radi());
202         while (!this->slot().collide(c->frame())) {
203                 bool end = false;
204                 std::vector<RRTEdge *> eds = c->frame();
205                 for (auto o: co)
206                         if (o.collide(eds))
207                                 end = true;
208                 for (auto o: so)
209                         if (o.collide(eds))
210                                 end = true;
211                 for (auto e: eds)
212                         delete e;
213                 if (!end)
214                         q.push(c);
215                 c = B->move(CC, -i * di * 0.01 / CC->diag_radi());
216                 i += 1;
217         }
218         delete c; // not in q and collide
219         while (!q.empty()) {
220                 c = q.front();
221                 q.pop();
222                 if (this->isInside(c)) {
223                         goto createcuspandfinish;
224                 } else if (c->s() < 9) {
225                         BicycleCar *cc = this->flnc(c, co, so);
226                         cc->s(c->s() + 1);
227                         cc->bcparent(c);
228                         q.push(cc);
229                 } else {
230                         delete c; // not in q and collide
231                 }
232         }
233         std::swap(q, empty);
234         return;
235 createcuspandfinish:
236         std::vector<RRTNode *> cusp;
237         while (c) {
238                 cusp.push_back(new RRTNode(c->x(), c->y(), c->h()));
239                 c = c->bcparent();
240         }
241         std::reverse(cusp.begin(), cusp.end());
242         this->cusp().push_back(cusp);
243         std::swap(q, empty);
244 }
245
246 BicycleCar *ParallelSlot::flnc(
247         BicycleCar *B,
248         std::vector<CircleObstacle>& co,
249         std::vector<SegmentObstacle>& so
250 )
251 {
252         RRTNode *cc;
253         if (this->slotSide() == LEFT) {
254                 if (int(B->s()) % 2 == 0)
255                         cc = BicycleCar(B->x(), B->y(), B->h()).ccr();
256                 else
257                         cc = BicycleCar(B->x(), B->y(), B->h()).ccl();
258         } else {
259                 if (int(B->s()) % 2 == 0)
260                         cc = BicycleCar(B->x(), B->y(), B->h()).ccl();
261                 else
262                         cc = BicycleCar(B->x(), B->y(), B->h()).ccr();
263         }
264         BicycleCar *p;
265         int i = 1;
266         p = B->move(cc, i * this->DH());
267         while (
268                 !this->slot().collide(p->frame())
269                 && std::abs(this->slotHeading() - p->h()) < M_PI / 2
270         ) {
271                 delete p;
272                 i += 10;
273                 p = B->move(cc, i * this->DH());
274                 bool end = false;
275                 std::vector<RRTEdge *> eds = p->frame();
276                 for (auto o: co)
277                         if (o.collide(eds))
278                                 end = true;
279                 for (auto o: so)
280                         if (o.collide(eds))
281                                 end = true;
282                 for (auto e: eds)
283                         delete e;
284                 if (end)
285                         break;
286         }
287         i -= 10;
288         p = B->move(cc, i * this->DH());
289         while (
290                 !this->slot().collide(p->frame())
291                 && std::abs(this->slotHeading() - p->h()) < M_PI / 2
292         ) {
293                 if (this->isInside(p)) {
294                         i += 1;
295                         break;
296                 }
297                 delete p;
298                 i += 1;
299                 p = B->move(cc, i * this->DH());
300                 bool end = false;
301                 std::vector<RRTEdge *> eds = p->frame();
302                 for (auto o: co)
303                         if (o.collide(eds))
304                                 end = true;
305                 for (auto o: so)
306                         if (o.collide(eds))
307                                 end = true;
308                 for (auto e: eds)
309                         delete e;
310                 if (end)
311                         break;
312         }
313         delete p;
314         return B->move(cc, (i - 1) * this->DH());
315 }
316
317 void ParallelSlot::fipr(RRTNode *n)
318 {
319         return this->fipr(new BicycleCar(n->x(), n->y(), n->h()));
320 }
321
322 void ParallelSlot::fipr(BicycleCar *B)
323 {
324         std::vector<RRTNode *> cusp;
325         cusp.push_back(new RRTNode(B->x(), B->y(), B->h()));
326         int di = 1;
327         if (this->slotSide() == LEFT)
328                 di = -1;
329         if (this->slotType() == PERPENDICULAR) {
330                 this->DH(di * 0.01 / B->out_radi()); // TODO car in slot h()
331                 RRTNode *cc;
332                 if (this->slotSide() == LEFT)
333                         cc = BicycleCar(B->x(), B->y(), B->h()).ccl();
334                 else
335                         cc = BicycleCar(B->x(), B->y(), B->h()).ccr();
336                 BicycleCar *p;
337                 int i = 1;
338                 p = B->move(cc, i * this->DH());
339                 while (
340                         !this->slot().collide(p->frame())
341                         && this->slot().collide(p)
342                 ) {
343                         delete p;
344                         i += 10;
345                         p = B->move(cc, i * this->DH());
346                 }
347                 i -= 10;
348                 p = B->move(cc, i * this->DH());
349                 while (
350                         !this->slot().collide(p->frame())
351                         && this->slot().collide(p)
352                 ) {
353                         delete p;
354                         i += 1;
355                         p = B->move(cc, i * this->DH());
356                 }
357                 i -= 1;
358                 p = B->move(cc, i * this->DH());
359                 cusp.push_back(new RRTNode(p->x(), p->y(), p->h()));
360                 std::reverse(cusp.begin(), cusp.end());
361                 this->cusp().push_back(cusp);
362                 return;
363         }
364         this->DH(di * 0.01 / B->out_radi());
365         BicycleCar *c;
366         c = this->flncr(B);
367         c->s(B->s() + 1);
368         while ((
369                 this->slotSide() == LEFT
370                 && this->slot().collide(new RRTNode(c->lfx(), c->lfy(), 0))
371         ) || (
372                 this->slotSide() == RIGHT
373                 && this->slot().collide(new RRTNode(c->rfx(), c->rfy(), 0))
374         )) {
375                 cusp.push_back(new RRTNode(c->x(), c->y(), c->h()));
376                 BicycleCar *cc = this->flncr(c);
377                 cc->s(c->s() + 1);
378                 delete c;
379                 c = cc;
380         }
381         cusp.push_back(new RRTNode(c->x(), c->y(), c->h()));
382         std::reverse(cusp.begin(), cusp.end());
383         this->cusp().push_back(cusp);
384 }
385
386 BicycleCar *ParallelSlot::flncr(BicycleCar *B)
387 {
388         RRTNode *cc;
389         if (this->slotSide() == LEFT) {
390                 if (int(B->s()) % 2 == 0)
391                         cc = BicycleCar(B->x(), B->y(), B->h()).ccr();
392                 else
393                         cc = BicycleCar(B->x(), B->y(), B->h()).ccl();
394         } else {
395                 if (int(B->s()) % 2 == 0)
396                         cc = BicycleCar(B->x(), B->y(), B->h()).ccl();
397                 else
398                         cc = BicycleCar(B->x(), B->y(), B->h()).ccr();
399         }
400         BicycleCar *p;
401         int i = 1;
402         p = B->move(cc, i * this->DH());
403         while (
404                 !this->slot().collide(p->frame())
405                 && ((
406                         this->slotSide() == LEFT
407                         && this->slot().collide(new RRTNode(
408                                 p->lfx(),
409                                 p->lfy(),
410                                 0
411                         ))
412                 ) || (
413                         this->slotSide() == RIGHT
414                         && this->slot().collide(new RRTNode(
415                                 p->rfx(),
416                                 p->rfy(),
417                                 0
418                         ))
419                 ))
420         ) {
421                 delete p;
422                 i += 10;
423                 p = B->move(cc, i * this->DH());
424         }
425         i -= 10;
426         p = B->move(cc, i * this->DH());
427         while (!this->slot().collide(p->frame())) {
428                 if(
429                         this->slotSide() == LEFT
430                         && !this->slot().collide(new RRTNode(
431                                 p->lfx(),
432                                 p->lfy(),
433                                 0
434                         ))
435                 ) {
436                         i += 1;
437                         break;
438                 }
439                 if(
440                         this->slotSide() == RIGHT
441                         && !this->slot().collide(new RRTNode(
442                                 p->rfx(),
443                                 p->rfy(),
444                                 0
445                         ))
446                 ) {
447                         i += 1;
448                         break;
449                 }
450                 i += 1;
451                 p = B->move(cc, i * this->DH());
452         }
453         delete p;
454         return B->move(cc, (i - 1) * this->DH());
455 }
456
457 RRTNode *ParallelSlot::fposecenter()
458 {
459         return this->slot().bnodes().back();
460 }
461
462 bool ParallelSlot::flast(
463         RRTNode *P,
464         bool right,
465         int il,
466         std::vector<RRTNode *> &cusp
467 )
468 {
469         BicycleCar *B = new BicycleCar(P->x(), P->y(), P->h());
470         RRTNode *cc;
471         if (right)
472                 cc = BicycleCar(B->x(), B->y(), B->h()).ccr();
473         else
474                 cc = BicycleCar(B->x(), B->y(), B->h()).ccl();
475         BicycleCar *p;
476         int i = 1;
477         p = B->move(cc, i * this->DH());
478         while (!this->slot().collide(p->frame())
479                         && (
480                                 (this->DH() > 0 && p->x() <= 0)
481                                 || (this->DH() < 0 && p->x() >= 0)
482                         )) {
483                 delete p;
484                 i += 10;
485                 p = B->move(cc, i * this->DH());
486         }
487         i -= 10;
488         p = B->move(cc, i * this->DH());
489         while (!this->slot().collide(p->frame())
490                         && (
491                                 (this->DH() > 0 && p->x() <= 0)
492                                 || (this->DH() < 0 && p->x() >= 0)
493                         )) {
494                 if (this->DH() > 0 && p->rfx() <= 0 && p->rrx() <= 0) {
495                         i += 1;
496                         break;
497                 }
498                 if (this->DH() < 0 && p->lfx() >= 0 && p->lrx() >= 0) {
499                         i += 1;
500                         break;
501                 }
502                 delete p;
503                 i += 1;
504                 p = B->move(cc, i * this->DH());
505         }
506         delete p;
507         p = B->move(cc, (i - 1) * this->DH());
508         if (this->DH() > 0 && p->rfx() <= 0 && p->rrx() <= 0) {
509                 cusp.push_back(p);
510                 return true;
511         } else if (this->DH() < 0 && p->lfx() >= 0 && p->lrx() >= 0) {
512                 cusp.push_back(p);
513                 return true;
514         } else if (il < 8) {
515                 cusp.push_back(p);
516                 return this->flast(p, !right, il + 1, cusp);
517         }
518         return false;
519 }
520
521 void ParallelSlot::fpose()
522 {
523         bool left = false; // right parking slot
524         float di = -1;
525         BicycleCar *CC = new BicycleCar(
526                 this->fposecenter()->x(),
527                 this->fposecenter()->y() - 0.01,
528                 this->slotHeading()
529         );
530         BicycleCar *B = new BicycleCar(
531                 CC->x() - CC->width() / 2,
532                 CC->y() - (CC->length() + CC->wheelbase()) / 2,
533                 this->slotHeading()
534         );
535         if (this->slot().bnodes()[0]->x() > this->slot().bnodes()[1]->x()) {
536                 left = true;
537                 di = 1;
538                 delete B;
539                 B = new BicycleCar(
540                         CC->x() + CC->width() / 2,
541                         CC->y() - (CC->length() + CC->wheelbase()) / 2,
542                         this->slotHeading()
543                 );
544         }
545         this->DH(di * 0.01 / CC->out_radi());
546         BicycleCar *p;
547         int i = 0;
548         p = B->move(CC, -i * di * 0.01 / CC->diag_radi());
549         while (!this->slot().collide(p->frame())) {
550                 std::vector<RRTNode *> tmpcusp;
551                 tmpcusp.push_back(new BicycleCar(p->x(), p->y(), p->h()));
552                 if (this->flast(p, left, 0, tmpcusp)) {
553                         this->cusp().push_back(tmpcusp);
554                         return;
555                 }
556                 i += 1;
557                 delete p;
558                 p = B->move(CC, -i * di * 0.01 / CC->diag_radi());
559         }
560 }
561
562 BicycleCar *ParallelSlot::getEP()
563 {
564         // new pose for parallel parking to right slot
565         float tnx;
566         float tny;
567         float nx;
568         float ny;
569         BicycleCar *CC = this->getEPC();
570         // move left by car width / 2
571         tnx = CC->x() + CC->width() / 2 * cos(CC->h() + M_PI / 2);
572         tny = CC->y() + CC->width() / 2 * sin(CC->h() + M_PI / 2);
573         if (this->slotSide() == LEFT) {
574                 // move right by car width / 2
575                 tnx = CC->x() + CC->width() / 2 * cos(CC->h() - M_PI / 2);
576                 tny = CC->y() + CC->width() / 2 * sin(CC->h() - M_PI / 2);
577         }
578         if (this->slotType() == PARALLEL) {
579                 // move down
580                 nx = tnx - (CC->length() + CC->wheelbase()) / 2 * cos(CC->h());
581                 ny = tny - (CC->length() + CC->wheelbase()) / 2 * sin(CC->h());
582         } else {
583                 // move down
584                 nx = tnx + (CC->length() - CC->wheelbase()) / 2 * cos(CC->h());
585                 ny = tny + (CC->length() - CC->wheelbase()) / 2 * sin(CC->h());
586         }
587         return new BicycleCar(nx, ny, CC->h());
588 }
589
590 BicycleCar *ParallelSlot::getEPC()
591 {
592         // new pose for parallel parking to right slot
593         float ta;
594         float nx;
595         float ny;
596         ta = this->slotHeading() + M_PI;
597         if (this->slotSide() == RIGHT)
598                 ta -= M_PI / 4;
599         else
600                 ta += M_PI / 4;
601         nx = this->fposecenter()->x() + 0.01 * cos(ta);
602         ny = this->fposecenter()->y() + 0.01 * sin(ta);
603         return new BicycleCar(nx, ny, this->slotHeading());
604 }
605
606 BicycleCar *ParallelSlot::getFP()
607 {
608         this->setAll();
609         float x = this->slot().bnodes()[0]->x();
610         float y = this->slot().bnodes()[0]->y();
611         float h = this->slotHeading();
612         float nx;
613         float ny;
614         if (this->slotType() == PARALLEL) {
615                 if (this->slotSide() == LEFT) {
616                         nx = x + BCAR_WIDTH / 2 * cos(h + M_PI / 2);
617                         ny = y + BCAR_WIDTH / 2 * sin(h + M_PI / 2);
618                 } else {
619                         nx = x + BCAR_WIDTH / 2 * cos(h - M_PI / 2);
620                         ny = y + BCAR_WIDTH / 2 * sin(h - M_PI / 2);
621                 }
622                 x = nx + ((BCAR_LENGTH - BCAR_WHEEL_BASE) / 2 + 0.01) * cos(h);
623                 y = ny + ((BCAR_LENGTH - BCAR_WHEEL_BASE) / 2 + 0.01) * sin(h);
624         } else {
625                 if (this->slotSide() == LEFT) {
626                         h -= M_PI / 2;
627                         nx = x + (BCAR_LENGTH + BCAR_WHEEL_BASE) / 2
628                                 * cos(h + M_PI);
629                         ny = y + (BCAR_LENGTH + BCAR_WHEEL_BASE) / 2
630                                 * sin(h + M_PI);
631                         x = nx + (BCAR_DIAG_RRADI) * cos(h + M_PI / 2);
632                         y = ny + (BCAR_DIAG_RRADI) * sin(h + M_PI / 2);
633                 } else {
634                         h += M_PI / 2;
635                         nx = x + (BCAR_LENGTH + BCAR_WHEEL_BASE) / 2
636                                 * cos(h - M_PI);
637                         ny = y + (BCAR_LENGTH + BCAR_WHEEL_BASE) / 2
638                                 * sin(h - M_PI);
639                         x = nx + (BCAR_DIAG_RRADI) * cos(h - M_PI / 2);
640                         y = ny + (BCAR_DIAG_RRADI) * sin(h - M_PI / 2);
641                 }
642         }
643         return new BicycleCar(x, y, h);
644 }
645
646 BicycleCar *ParallelSlot::getISPP(BicycleCar *B)
647 {
648         float x = this->slot().bnodes().back()->x();
649         float y = this->slot().bnodes().back()->y();
650         float y0;
651         if (this->slotSide() == LEFT) // TODO only for backward parking now
652                 y0 = B->ccl()->y();
653         else
654                 y0 = B->ccr()->y();
655         float IR = BCAR_IN_RADI;
656         float a = 1;
657         float b = -2 * x;
658         float c = pow(x, 2) + pow(y - y0, 2) - pow(IR, 2);
659         float D = pow(b, 2) - 4 * a * c;
660         float x0 = -b + sqrt(D);
661         x0 /= 2 * a;
662         return new BicycleCar(x0, B->y(), B->h());
663 }
664
665 bool ParallelSlot::isInside(BicycleCar *c)
666 {
667         bool inside = true;
668         RRTNode *tmpn;
669         tmpn = new RRTNode(c->lfx(), c->lfy(), 0);
670         if (!this->slot().collide(tmpn))
671                 inside = false;
672         delete tmpn;
673         tmpn = new RRTNode(c->lrx(), c->lry(), 0);
674         if (!this->slot().collide(tmpn))
675                 inside = false;
676         delete tmpn;
677         tmpn = new RRTNode(c->rrx(), c->rry(), 0);
678         if (!this->slot().collide(tmpn))
679                 inside = false;
680         delete tmpn;
681         tmpn = new RRTNode(c->rfx(), c->rfy(), 0);
682         if (!this->slot().collide(tmpn))
683                 inside = false;
684         delete tmpn;
685         return inside;
686 }
687
688 struct SamplingInfo ParallelSlot::getSamplingInfo()
689 {
690         struct SamplingInfo si;
691         RRTNode *n = this->getMidd();
692         if (this->slotType() == PARALLEL) {
693                 if (n != nullptr) {
694                         si.x0 = n->x() + 1.5 * BCAR_LENGTH * cos(n->h());
695                         si.y0 = n->y() + 1.5 * BCAR_LENGTH * sin(n->h());
696                         si.h0 = n->h();
697                 } else {
698                         si.x0 = this->slot().bnodes().front()->x();
699                         si.y0 = this->slot().bnodes().front()->y();
700                         si.h0 = this->slotHeading();
701                 }
702                 si.x = BCAR_WIDTH;
703                 si.y = BCAR_WIDTH;
704                 si.h = M_PI / 8;
705         } else {
706                 // TODO
707         }
708         return si;
709 }