Developer Documentation
wayfind.cc
1/*===========================================================================*\
2* *
3* OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39* *
40\*===========================================================================*/
41
42
43
44//== INCLUDES =================================================================
45#include <QRectF>
46#include <QVector>
47#include <QHash>
48#include <QTime>
49
50#include <iostream>
51
52#include <cmath>
53
54#include "wayfind.hh"
55
56#include "graphicsScene.hh"
57#include "elementArea.hh"
58#include "sceneElement.hh"
59#include "connection.hh"
60#include "elementInput.hh"
61#include "elementOutput.hh"
62
63#define PAIR(p) QPair<int,int>((p).x (),(p).y ())
64
65//== NAMESPACES ===============================================================
66namespace VSI {
67
68//=============================================================================
69//
70// CLASS VSI::WayFind - IMPLEMENTATION
71//
72//=============================================================================
73
76 scene_ (_scene),
77 counter_ (0),
78 ll_(0)
79{
80}
81
82//------------------------------------------------------------------------------
83
86{
87 foreach (Node *n, nodes_)
88 delete n;
89}
90
91//------------------------------------------------------------------------------
92
94QPolygonF WayFind::findWay(Connection *_conn, QPoint _from, QPoint _to)
95{
96 // our maximum working area
97 QRect bb = scene_->elementArea ()->childrenBoundingRect ().toRect ().adjusted (-100, -100, 100, 100);
98
99 QRegion eR;
100
101 // combine all scene element bounding boxes and connections into one region
102 foreach (SceneElement *e, scene_->elements())
103 {
104 eR += e->mapRectToParent (e->boundingRect ()).toRect ().adjusted (-15,-15,15,15);
105 foreach (ElementInput *i, e->inputs ())
106 foreach (Connection *c, i->connections ())
107 {
108 // ignore connection from the same output
109 if (c->output () == _conn->output ())
110 continue;
111 if (c->way ().isEmpty ())
112 continue;
113 QPoint p1 = c->way ().first ().toPoint ();
114 QPoint p2;
115 for (int j = 1; j < c->way ().size (); j++)
116 {
117 p2 = c->way ()[j].toPoint ();
118 eR += QRect (qMin (p1.x (), p2.x ()), qMin (p1.y (), p2.y ()), abs (p1.x () - p2.x ()), abs (p1.y () - p2.y ())).adjusted (-2, -2, 2, 2);
119 p1 = p2;
120 }
121 }
122 if (e->dataIn ())
123 {
124 foreach (Connection *c, e->dataIn ()->connections ())
125 {
126 if (c->way ().isEmpty ())
127 continue;
128 QPoint p1 = c->way ().first ().toPoint ();
129 QPoint p2;
130 for (int i = 1; i < c->way ().size (); i++)
131 {
132 p2 = c->way ()[i].toPoint ();
133 eR += QRect (qMin (p1.x (), p2.x ()), qMin (p1.y (), p2.y ()), abs (p1.x () - p2.x ()), abs (p1.y () - p2.y ())).adjusted (-4, -4, 4, 4);
134 p1 = p2;
135 }
136 }
137 }
138 }
139
140 // 5px wide grid
141 int step = 5;
142
143 // round to grid size
144 int x1 = (_from.x () / step) * step;
145 int y1 = (_from.y () / step) * step;
146 int x2 = (_to.x () / step) * step;
147 int y2 = (_to.y () / step) * step;
148
149 QPoint newTo = QPoint (x2, y2);
150
151 // make sure that start and end get different nodes
152 if (_from.x () != _to.x () && x1 == x2)
153 {
154 if (_from.x () > _to.x ())
155 x1 += step;
156 else
157 x2 += step;
158 }
159
160 if (_from.y () != _to.y () && y1 == y2)
161 {
162 if (_from.y () > _to.y ())
163 y1 += step;
164 else
165 y2 += step;
166 }
167
168 bool found = false;
169 Node *n = NULL, *tmp, *tmp2, *lastIn = 0;
170
171 // reuse old calculation if nothing changed
172 if (_from == oldFrom_ && eR == oldReg_)
173 {
174 if (map_.contains (PAIR(newTo)))
175 {
176 n = map_[PAIR(newTo)];
177 if (n->closed_ && n->counter_ == counter_)
178 found = true;
179 }
180 }
181
182 if (!found)
183 {
184 // increase usage counter
185 counter_++;
186
187 // create start node if not avaiable
188 if (!map_.contains (QPair<int,int>(x1, y1)))
189 {
190 ll_ = new Node (counter_);
191 nodes_.push_back (ll_);
192
193 ll_->pos_ = QPoint (x1, y1);
194 map_.insert (QPair<int,int>(x1, y1), ll_);
195
196 for (unsigned int k = 0; k < 4; k++)
197 {
198 QPoint p2 = validPos (k, step, ll_->pos_);
199
200 if (map_.contains (PAIR(p2)))
201 {
202 ll_->n_[k] = map_[PAIR(p2)];
203 map_[PAIR(p2)]->n_[(k + 2) & 3] = ll_;
204 }
205 }
206 }
207 else
208 {
209 ll_ = map_[QPair<int,int>(x1, y1)];
210 ll_->counter_ = counter_;
211 ll_->prev_ = 0;
212 ll_->next_ = 0;
213 ll_->from_ = 0;
214 ll_->f_ = 100000000;
215 ll_->closed_ = false;
216 ll_->cost_ = 5;
217 }
218
219 ll_->g_ = 0;
220 ll_->h_ = heuristicDistance (_from, _to);
221 ll_->type_ = Node::Horizontal;
222
223 }
224
225 while (ll_ && !found)
226 {
227 // take first node of the list
228 n = ll_;
229 ll_ = n->next_;
230 if (ll_)
231 ll_->prev_ = 0;
232
233 n->closed_ = true;
234
235 // stop if end node is found
236 if (n->pos_.y () == y2 && n->pos_.x () == x2)
237 {
238 found = true;
239 break;
240 }
241
242 // Add neighbor nodes if not yet present or reset old ones if not yet used during this round
243 for (unsigned int i = 0; i < 4; i++)
244 if (!n->n_[i])
245 {
246 QPoint p = validPos (i, step, n->pos_);
247 if (bb.contains (p))
248 {
249 Node *nn = new Node (counter_);
250 nodes_.push_back (nn);
251
252 n->n_[i] = nn;
253
254 nn->n_[(i + 2) & 3] = n;
255 nn->pos_ = p;
256 nn->h_ = heuristicDistance (p, newTo);
257
258 if (eR.contains (p))
259 nn->cost_ = 50;
260
261 map_.insert (PAIR(nn->pos_), nn);
262
263 for (unsigned int j = 0; j < 3; j++)
264 {
265 unsigned int k = (i - 1 + j) & 3;
266 QPoint p2 = validPos (k, step, p);
267
268 if (map_.contains (PAIR(p2)))
269 {
270 nn->n_[k] = map_[PAIR(p2)];
271 map_[PAIR(p2)]->n_[(k + 2) & 3] = nn;
272 }
273 }
274 }
275 }
276 else if (n->n_[i]->counter_ != counter_)
277 {
278 tmp = n->n_[i];
279 tmp->counter_ = counter_;
280 tmp->prev_ = 0;
281 tmp->next_ = 0;
282 tmp->from_ = 0;
283 tmp->g_ = 100000000;
284 tmp->f_ = 100000000;
285 tmp->h_ = 100000000;
286 tmp->closed_ = false;
287 if (eR.contains (tmp->pos_))
288 tmp->cost_ = 50;
289 else
290 tmp->cost_ = 5;
291 tmp->h_ = heuristicDistance (tmp->pos_, newTo);
292 }
293
294 // update nodes in all directions
295 for (unsigned int i = 0; i < 4; i++)
296 {
297 if (!n->n_[i])
298 continue;
299
300 if (n->n_[i]->closed_)
301 continue;
302
303 tmp = n->n_[i];
304
305 unsigned int g = n->g_;
306
307 // direction change?
308 if ((n->type_ == Node::Horizontal && (i & 1)) || (n->type_ == Node::Vertical && (i & 1) == 0))
309 g += n->cost_;
310 else
311 g += n->cost_ * 2;
312
313 if (g < tmp->g_)
314 {
315 tmp->from_ = n;
316 tmp->type_ = (i & 1)? Node::Horizontal : Node::Vertical;
317
318 tmp->g_ = g;
319 tmp->f_ = g + tmp->h_;
320
321 // remove node from list
322 if (tmp->prev_)
323 tmp->prev_->next_ = tmp->next_;
324 if (tmp->next_)
325 tmp->next_->prev_ = tmp->prev_;
326 if (tmp == ll_)
327 ll_ = tmp->next_;
328
329 // insert node at right position in sorted list
330 if (!ll_ || tmp->f_ <= ll_->f_)
331 {
332 tmp->next_ = ll_;
333 if (ll_)
334 ll_->prev_ = tmp;
335 ll_ = tmp;
336 lastIn = tmp;
337 }
338 else
339 {
340 if (lastIn && lastIn->f_ <= tmp->f_ && lastIn != tmp)
341 tmp2 = lastIn;
342 else
343 tmp2 = ll_;
344 while (tmp2->next_ && tmp2->next_->f_ < tmp->f_)
345 tmp2 = tmp2->next_;
346 tmp->next_ = tmp2->next_;
347 if (tmp->next_)
348 tmp->next_->prev_ = tmp;
349 tmp2->next_ = tmp;
350 tmp->prev_ = tmp2;
351 lastIn = tmp2;
352 }
353 }
354 }
355 }
356
357 QPolygonF rv;
358
359 // convert found way into polygon
360 if (found)
361 {
362 bool dir = true;
363 if (n->from_)
364 if (n->pos_.y () != n->from_->pos_.y ())
365 dir = false;
366
367 int lastX = n->pos_.x ();
368 int lastY = n->pos_.y ();
369
370 QPoint last = _to;
371
372 while (n)
373 {
374 if (dir && n->pos_.y () != lastY)
375 {
376 dir = false;
377 lastX = n->pos_.x ();
378 lastY = n->pos_.y ();
379 rv.append (last);
380 last = QPoint (n->pos_.x (), last.y ());
381 }
382 else if (!dir && n->pos_.x () != lastX)
383 {
384 dir = true;
385 lastX = n->pos_.x ();
386 lastY = n->pos_.y ();
387 rv.append (last);
388 last = QPoint (last.x (), n->pos_.y ());
389 }
390
391 n = n->from_;
392 }
393
394 if (dir)
395 last.setY (_from.y ());
396 else
397 last.setX (_from.x ());
398
399 rv.append(QPointF (last));
400 rv.append(QPointF (_from));
401 }
402 else
403 {
404 rv.append(QPointF (_from));
405 rv.append(QPointF (_to));
406 std::cerr << "Not Found" << std::endl;
407 }
408
409 oldFrom_ = _from;
410 oldReg_ = eR;
411
412 // free unused nodes
413 cleanup ();
414
415 return rv;
416}
417
418//------------------------------------------------------------------------------
419
420// Node constructor
421WayFind::Node::Node(unsigned int _counter) :
422 counter_ (_counter),
423 type_ (Horizontal),
424 prev_ (0),
425 next_ (0),
426 from_ (0),
427 g_ (100000000),
428 f_ (100000000),
429 h_ (100000000),
430 cost_ (5),
431 closed_ (false)
432{
433 n_[0] = 0;
434 n_[1] = 0;
435 n_[2] = 0;
436 n_[3] = 0;
437}
438
439//------------------------------------------------------------------------------
440
441// Node destructor
442WayFind::Node::~ Node()
443{
444}
445
446//------------------------------------------------------------------------------
447
448// Next position in distance _step from _pnt in direction _dir
449QPoint WayFind::validPos(unsigned int _dir, int _step, QPoint _pnt)
450{
451
452 QPoint rv = _pnt;
453 if (_dir == 0 || _dir == 3)
454 _step = -_step;
455
456 if (_dir == 1 || _dir == 3)
457 rv += QPoint (_step, 0);
458 else
459 rv += QPoint (0, _step);
460
461 return rv;
462}
463
464//------------------------------------------------------------------------------
465
466// Heuristic distance from _from to _to
467int WayFind::heuristicDistance(const QPoint &_from, const QPoint &_to) const
468{
469 QPoint p = _from - _to;
470 return abs (p.x ()) + abs (p.y ());
471}
472
473//------------------------------------------------------------------------------
474
475// cleanup ununsed nodes
476void VSI::WayFind::cleanup()
477{
478 // only every 128 runs
479 if ((counter_ & 0x7f) != 0)
480 return;
481
482 std::list<Node *>::iterator it = nodes_.begin();
483
484 while (it != nodes_.end ())
485 {
486 Node* n = *it;
487 // nodes that hasn't be used in the last 64 rounds
488 if (counter_ - n->counter_ > 64)
489 {
490 for (unsigned int i = 0; i < 4; i++)
491 if (n->n_[i])
492 n->n_[i]->n_[(i+2)&3] = NULL;
493 it = nodes_.erase(it);
494 map_.remove (PAIR(n->pos_));
495 delete n;
496 }
497 else
498 ++it;
499 }
500}
501
502//------------------------------------------------------------------------------
503}
ElementOutput * output()
Output of this connection.
Definition: connection.cc:248
const QPolygonF & way() const
way of the connection
Definition: connection.cc:257
QList< Connection * > connections() const
Connections.
Definition: elementInOut.hh:98
const QList< SceneElement * > & elements() const
Scene elements.
ElementArea * elementArea() const
Element area.
ElementInput * dataIn()
Scene input.
Definition: sceneElement.hh:99
QVector< ElementInput * > inputs()
Inputs.
Definition: sceneElement.hh:90
QPolygonF findWay(Connection *_conn, QPoint _from, QPoint _to)
Finds a way from _from to _to ignoring any already existent connections from _conn.
Definition: wayfind.cc:94
WayFind(GraphicsScene *_scene)
Constructor.
Definition: wayfind.cc:75
~WayFind()
Destructor.
Definition: wayfind.cc:85