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 ===============================================================
66 namespace 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 
94 QPolygonF 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 i = 1; i < c->way ().size (); i++)
116  {
117  p2 = c->way ()[i].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
421 WayFind::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
442 WayFind::Node::~ Node()
443 {
444 }
445 
446 //------------------------------------------------------------------------------
447 
448 // Next position in distance _step from _pnt in direction _dir
449 QPoint 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
467 int 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
476 void 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 }
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
const QList< SceneElement * > & elements() const
Scene elements.
~WayFind()
Destructor.
Definition: wayfind.cc:85
ElementInput * dataIn()
Scene input.
ElementArea * elementArea() const
Element area.
const QPolygonF & way() const
way of the connection
Definition: connection.cc:257
ElementOutput * output()
Output of this connection.
Definition: connection.cc:248
WayFind(GraphicsScene *_scene)
Constructor.
Definition: wayfind.cc:75
QVector< ElementInput * > inputs()
Inputs.
Definition: sceneElement.hh:91
QList< Connection * > connections() const
Connections.
Definition: elementInOut.hh:98