Generated on Mon Aug 27 2012 17:15:36 for Gecode by doxygen 1.8.1.2
treecanvas.cpp
Go to the documentation of this file.
1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 /*
3  * Main authors:
4  * Guido Tack <tack@gecode.org>
5  *
6  * Copyright:
7  * Guido Tack, 2006
8  *
9  * Last modified:
10  * $Date: 2011-08-30 19:40:00 +1000 (Tue, 30 Aug 2011) $ by $Author: tack $
11  * $Revision: 12374 $
12  *
13  * This file is part of Gecode, the generic constraint
14  * development environment:
15  * http://www.gecode.org
16  *
17  * Permission is hereby granted, free of charge, to any person obtaining
18  * a copy of this software and associated documentation files (the
19  * "Software"), to deal in the Software without restriction, including
20  * without limitation the rights to use, copy, modify, merge, publish,
21  * distribute, sublicense, and/or sell copies of the Software, and to
22  * permit persons to whom the Software is furnished to do so, subject to
23  * the following conditions:
24  *
25  * The above copyright notice and this permission notice shall be
26  * included in all copies or substantial portions of the Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35  *
36  */
37 
38 #include <QtGui/QPainter>
39 
40 #include <stack>
41 #include <fstream>
42 
44 
48 
49 #include <gecode/search.hh>
50 #include <gecode/search/support.hh>
51 
52 namespace Gecode { namespace Gist {
53 
54  TreeCanvas::TreeCanvas(Space* rootSpace, bool bab,
55  QWidget* parent, const Options& opt)
56  : QWidget(parent)
57  , mutex(QMutex::Recursive)
58  , layoutMutex(QMutex::Recursive)
59  , finishedFlag(false)
60  , compareNodes(false), compareNodesBeforeFP(false)
61  , autoHideFailed(true), autoZoom(false)
62  , refresh(500), refreshPause(0), smoothScrollAndZoom(false)
63  , zoomTimeLine(500)
64  , scrollTimeLine(1000), targetX(0), sourceX(0), targetY(0), sourceY(0)
65  , targetW(0), targetH(0), targetScale(0)
66  , layoutDoneTimerId(0) {
67  QMutexLocker locker(&mutex);
68  curBest = (bab ? new BestNode(NULL) : NULL);
69  if (rootSpace->status() == SS_FAILED) {
70  if (!opt.clone)
71  delete rootSpace;
72  rootSpace = NULL;
73  } else {
74  rootSpace = Gecode::Search::snapshot(rootSpace,opt);
75  }
76  na = new Node::NodeAllocator(bab);
77  int rootIdx = na->allocate(rootSpace);
78  assert(rootIdx == 0); (void) rootIdx;
79  root = (*na)[0];
80  root->layout(*na);
81  root->setMarked(true);
82  currentNode = root;
83  pathHead = root;
84  scale = LayoutConfig::defScale / 100.0;
85 
86  setAutoFillBackground(true);
87 
88  connect(&searcher, SIGNAL(update(int,int,int)), this,
89  SLOT(layoutDone(int,int,int)));
90  connect(&searcher, SIGNAL(statusChanged(bool)), this,
91  SLOT(statusChanged(bool)));
92 
93  connect(&searcher, SIGNAL(solution(const Space*)),
94  this, SIGNAL(solution(const Space*)),
95  Qt::BlockingQueuedConnection);
96  connect(this, SIGNAL(solution(const Space*)),
97  this, SLOT(inspectSolution(const Space*)));
98  connect(&searcher, SIGNAL(solution(const Space*)),
99  this, SLOT(inspectSolution(const Space*)),
100  Qt::BlockingQueuedConnection);
101 
102  connect(&searcher, SIGNAL(searchFinished(void)), this, SIGNAL(searchFinished(void)));
103 
104  connect(&scrollTimeLine, SIGNAL(frameChanged(int)),
105  this, SLOT(scroll(int)));
106  scrollTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
107 
108  scaleBar = new QSlider(Qt::Vertical, this);
109  scaleBar->setObjectName("scaleBar");
110  scaleBar->setMinimum(LayoutConfig::minScale);
111  scaleBar->setMaximum(LayoutConfig::maxScale);
112  scaleBar->setValue(LayoutConfig::defScale);
113  connect(scaleBar, SIGNAL(valueChanged(int)),
114  this, SLOT(scaleTree(int)));
115  connect(this, SIGNAL(scaleChanged(int)), scaleBar, SLOT(setValue(int)));
116  connect(&searcher, SIGNAL(scaleChanged(int)),
117  scaleBar, SLOT(setValue(int)));
118 
119  connect(&zoomTimeLine, SIGNAL(frameChanged(int)),
120  scaleBar, SLOT(setValue(int)));
121  zoomTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
122 
123  qRegisterMetaType<Statistics>("Statistics");
124  update();
125  }
126 
128  if (root) {
129  DisposeCursor dc(root,*na);
131  }
132  delete na;
133  }
134 
135  void
137  doubleClickInspectors.append(QPair<Inspector*,bool>(i,false));
138  }
139 
140  void
142  assert(i < doubleClickInspectors.size());
143  doubleClickInspectors[i].second = active;
144  }
145 
146  void
148  solutionInspectors.append(QPair<Inspector*,bool>(i,false));
149  }
150 
151  void
153  assert(i < solutionInspectors.size());
154  solutionInspectors[i].second = active;
155  }
156 
157  void
159  moveInspectors.append(QPair<Inspector*,bool>(i,false));
160  }
161 
162  void
164  assert(i < moveInspectors.size());
165  moveInspectors[i].second = active;
166  }
167 
168  void
170  comparators.append(QPair<Comparator*,bool>(c,false));
171  }
172 
173  void
174  TreeCanvas::activateComparator(int i, bool active) {
175  assert(i < comparators.size());
176  comparators[i].second = active;
177  }
178 
179  void
180  TreeCanvas::scaleTree(int scale0, int zoomx, int zoomy) {
181  QMutexLocker locker(&layoutMutex);
182 
183  QSize viewport_size = size();
184  QAbstractScrollArea* sa =
185  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
186 
187  if (zoomx==-1)
188  zoomx = viewport_size.width()/2;
189  if (zoomy==-1)
190  zoomy = viewport_size.height()/2;
191 
192  int xoff = (sa->horizontalScrollBar()->value()+zoomx)/scale;
193  int yoff = (sa->verticalScrollBar()->value()+zoomy)/scale;
194 
195  BoundingBox bb;
196  scale0 = std::min(std::max(scale0, LayoutConfig::minScale),
198  scale = (static_cast<double>(scale0)) / 100.0;
199  bb = root->getBoundingBox();
200  int w =
201  static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
202  int h =
203  static_cast<int>(2*Layout::extent+
205 
206  sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
207  sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
208  sa->horizontalScrollBar()->setPageStep(viewport_size.width());
209  sa->verticalScrollBar()->setPageStep(viewport_size.height());
210  sa->horizontalScrollBar()->setSingleStep(Layout::extent);
211  sa->verticalScrollBar()->setSingleStep(Layout::extent);
212 
213  xoff *= scale;
214  yoff *= scale;
215 
216  sa->horizontalScrollBar()->setValue(xoff-zoomx);
217  sa->verticalScrollBar()->setValue(yoff-zoomy);
218 
219  emit scaleChanged(scale0);
220  QWidget::update();
221  }
222 
223  void
225  QMutexLocker locker(&mutex);
226  layoutMutex.lock();
227  if (root != NULL) {
228  root->layout(*na);
230 
231  int w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
232  int h =
233  static_cast<int>(2*Layout::extent+
235  xtrans = -bb.left+(Layout::extent / 2);
236 
237  QSize viewport_size = size();
238  QAbstractScrollArea* sa =
239  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
240  sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
241  sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
242  sa->horizontalScrollBar()->setPageStep(viewport_size.width());
243  sa->verticalScrollBar()->setPageStep(viewport_size.height());
244  sa->horizontalScrollBar()->setSingleStep(Layout::extent);
245  sa->verticalScrollBar()->setSingleStep(Layout::extent);
246  }
247  if (autoZoom)
248  zoomToFit();
249  layoutMutex.unlock();
250  QWidget::update();
251  }
252 
253  void
255  QWidget::update();
256  }
257 
258  void
259  TreeCanvas::layoutDone(int w, int h, int scale0) {
260  targetW = w; targetH = h; targetScale = scale0;
261 
262  QSize viewport_size = size();
263  QAbstractScrollArea* sa =
264  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
265  sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
266  sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
267 
268  if (layoutDoneTimerId == 0)
269  layoutDoneTimerId = startTimer(15);
270  }
271 
272  void
273  TreeCanvas::statusChanged(bool finished) {
274  if (finished) {
275  update();
277  }
278  emit statusChanged(currentNode, stats, finished);
279  }
280 
281  void
283  node = n;
284 
285  depth = -1;
286  for (VisualNode* p = n; p != NULL; p = p->getParent(*ti->na))
287  depth++;
288 
289  a = all;
290  t = ti;
291  start();
292  }
293 
294  void
295  SearcherThread::updateCanvas(void) {
296  t->layoutMutex.lock();
297  if (t->root == NULL)
298  return;
299 
300  if (t->autoHideFailed) {
301  t->root->hideFailed(*t->na,true);
302  }
303  for (VisualNode* n = t->currentNode; n != NULL; n=n->getParent(*t->na)) {
304  if (n->isHidden()) {
305  t->currentNode->setMarked(false);
306  t->currentNode = n;
307  t->currentNode->setMarked(true);
308  break;
309  }
310  }
311 
312  t->root->layout(*t->na);
313  BoundingBox bb = t->root->getBoundingBox();
314 
315  int w = static_cast<int>((bb.right-bb.left+Layout::extent)*t->scale);
316  int h = static_cast<int>(2*Layout::extent+
317  t->root->getShape()->depth()
318  *Layout::dist_y*t->scale);
319  t->xtrans = -bb.left+(Layout::extent / 2);
320 
321  int scale0 = static_cast<int>(t->scale*100);
322  if (t->autoZoom) {
323  QWidget* p = t->parentWidget();
324  if (p) {
325  double newXScale =
326  static_cast<double>(p->width()) / (bb.right - bb.left +
328  double newYScale =
329  static_cast<double>(p->height()) /
331 
332  scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
333  if (scale0<LayoutConfig::minScale)
334  scale0 = LayoutConfig::minScale;
337  double scale = (static_cast<double>(scale0)) / 100.0;
338 
339  w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
340  h = static_cast<int>(2*Layout::extent+
341  t->root->getShape()->depth()*Layout::dist_y*scale);
342  }
343  }
344 
345  t->layoutMutex.unlock();
346  emit update(w,h,scale0);
347  }
348 
350  class SearchItem {
351  public:
355  int i;
359  SearchItem(VisualNode* n0, int noOfChildren0)
360  : n(n0), i(-1), noOfChildren(noOfChildren0) {}
361  };
362 
363  void
365  {
366  if (!node->isOpen())
367  return;
368  t->mutex.lock();
369  emit statusChanged(false);
370 
371  unsigned int kids =
372  node->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
373  t->c_d, t->a_d);
374  if (kids == 0 || node->getStatus() == STOP) {
375  t->mutex.unlock();
376  updateCanvas();
377  emit statusChanged(true);
378  return;
379  }
380 
381  std::stack<SearchItem> stck;
382  stck.push(SearchItem(node,kids));
383  t->stats.maxDepth =
384  std::max(static_cast<long unsigned int>(t->stats.maxDepth),
385  static_cast<long unsigned int>(depth+stck.size()));
386 
387  VisualNode* sol = NULL;
388  int nodeCount = 0;
389  t->stopSearchFlag = false;
390  while (!stck.empty() && !t->stopSearchFlag) {
391  if (t->refresh > 0 && nodeCount >= t->refresh) {
392  node->dirtyUp(*t->na);
393  updateCanvas();
394  emit statusChanged(false);
395  nodeCount = 0;
396  if (t->refreshPause > 0)
397  msleep(t->refreshPause);
398  }
399  SearchItem& si = stck.top();
400  si.i++;
401  if (si.i == si.noOfChildren) {
402  stck.pop();
403  } else {
404  VisualNode* n = si.n->getChild(*t->na,si.i);
405  if (n->isOpen()) {
406  if (n->getStatus() == UNDETERMINED)
407  nodeCount++;
408  kids = n->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
409  t->c_d, t->a_d);
410  if (kids == 0) {
411  if (n->getStatus() == SOLVED) {
412  assert(n->hasCopy());
413  emit solution(n->getWorkingSpace());
414  n->purge(*t->na);
415  sol = n;
416  if (!a)
417  break;
418  }
419  } else {
420  if ( n->getStatus() != STOP )
421  stck.push(SearchItem(n,kids));
422  else if (!a)
423  break;
424  t->stats.maxDepth =
425  std::max(static_cast<long unsigned int>(t->stats.maxDepth),
426  static_cast<long unsigned int>(depth+stck.size()));
427  }
428  }
429  }
430  }
431  node->dirtyUp(*t->na);
432  t->stopSearchFlag = false;
433  t->mutex.unlock();
434  if (sol != NULL) {
435  t->setCurrentNode(sol,false);
436  } else {
437  t->setCurrentNode(node,false);
438  }
439  }
440  updateCanvas();
441  emit statusChanged(true);
442  if (t->finishedFlag)
443  emit searchFinished();
444  }
445 
446  void
448  QMutexLocker locker(&mutex);
449  searcher.search(currentNode, true, this);
450  }
451 
452  void
454  QMutexLocker locker(&mutex);
455  searcher.search(currentNode, false, this);
456  }
457 
458  void
460  QMutexLocker locker(&mutex);
462  update();
464  emit statusChanged(currentNode, stats, true);
465  }
466 
467  void
469  QMutexLocker locker(&mutex);
471  update();
473  emit statusChanged(currentNode, stats, true);
474  }
475 
476  void
478  QMutexLocker locker(&mutex);
479  QMutexLocker layoutLocker(&layoutMutex);
481  update();
483  emit statusChanged(currentNode, stats, true);
484  }
485 
486  void
488  QMutexLocker locker(&mutex);
490  update();
492  emit statusChanged(currentNode, stats, true);
493  }
494 
495  void
497  QMutexLocker locker(&mutex);
498  QMutexLocker layoutLocker(&layoutMutex);
500  update();
502  emit statusChanged(currentNode, stats, true);
503  }
504 
505  void
506  TreeCanvas::timerEvent(QTimerEvent* e) {
507  if (e->timerId() == layoutDoneTimerId) {
508  if (!smoothScrollAndZoom) {
510  } else {
511  zoomTimeLine.stop();
512  int zoomCurrent = static_cast<int>(scale*100);
513  int targetZoom = targetScale;
514  targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
516  zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
517  zoomTimeLine.start();
518  }
519  QWidget::update();
520  killTimer(layoutDoneTimerId);
521  layoutDoneTimerId = 0;
522  }
523  }
524 
525  void
527  QMutexLocker locker(&layoutMutex);
528  if (root != NULL) {
529  BoundingBox bb;
530  bb = root->getBoundingBox();
531  QWidget* p = parentWidget();
532  if (p) {
533  double newXScale =
534  static_cast<double>(p->width()) / (bb.right - bb.left +
536  double newYScale =
537  static_cast<double>(p->height()) / (root->getShape()->depth() *
539  2*Layout::extent);
540  int scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
541  if (scale0<LayoutConfig::minScale)
542  scale0 = LayoutConfig::minScale;
545 
546  if (!smoothScrollAndZoom) {
547  scaleTree(scale0);
548  } else {
549  zoomTimeLine.stop();
550  int zoomCurrent = static_cast<int>(scale*100);
551  int targetZoom = scale0;
552  targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
554  zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
555  zoomTimeLine.start();
556  }
557  }
558  }
559  }
560 
561  void
563  QMutexLocker locker(&mutex);
564  int x=0;
565  int y=0;
566 
568  while (c != NULL) {
569  x += c->getOffset();
570  y += Layout::dist_y;
571  c = c->getParent(*na);
572  }
573 
574  x = static_cast<int>((xtrans+x)*scale); y = static_cast<int>(y*scale);
575 
576  QAbstractScrollArea* sa =
577  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
578 
579  x -= sa->viewport()->width() / 2;
580  y -= sa->viewport()->height() / 2;
581 
582  sourceX = sa->horizontalScrollBar()->value();
583  targetX = std::max(sa->horizontalScrollBar()->minimum(), x);
584  targetX = std::min(sa->horizontalScrollBar()->maximum(),
585  targetX);
586  sourceY = sa->verticalScrollBar()->value();
587  targetY = std::max(sa->verticalScrollBar()->minimum(), y);
588  targetY = std::min(sa->verticalScrollBar()->maximum(),
589  targetY);
590  if (!smoothScrollAndZoom) {
591  sa->horizontalScrollBar()->setValue(targetX);
592  sa->verticalScrollBar()->setValue(targetY);
593  } else {
594  scrollTimeLine.stop();
595  scrollTimeLine.setFrameRange(0,100);
596  scrollTimeLine.setDuration(std::max(200,
597  std::min(1000,
598  std::min(std::abs(sourceX-targetX),
599  std::abs(sourceY-targetY)))));
600  scrollTimeLine.start();
601  }
602  }
603 
604  void
605  TreeCanvas::scroll(int i) {
606  QAbstractScrollArea* sa =
607  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
608  double p = static_cast<double>(i)/100.0;
609  double xdiff = static_cast<double>(targetX-sourceX)*p;
610  double ydiff = static_cast<double>(targetY-sourceY)*p;
611  sa->horizontalScrollBar()->setValue(sourceX+static_cast<int>(xdiff));
612  sa->verticalScrollBar()->setValue(sourceY+static_cast<int>(ydiff));
613  }
614 
615  void
616  TreeCanvas::inspectCurrentNode(bool fix, int inspectorNo) {
617  QMutexLocker locker(&mutex);
618 
619  if (currentNode->isHidden()) {
620  toggleHidden();
621  return;
622  }
623 
624  int failedInspectorType = -1;
625  int failedInspector = -1;
626  bool needCentering = false;
627  try {
628  switch (currentNode->getStatus()) {
629  case UNDETERMINED:
630  {
631  unsigned int kids =
633  int depth = -1;
634  for (VisualNode* p = currentNode; p != NULL; p=p->getParent(*na))
635  depth++;
636  if (kids > 0) {
637  needCentering = true;
638  depth++;
639  }
640  stats.maxDepth =
641  std::max(stats.maxDepth, depth);
642  if (currentNode->getStatus() == SOLVED) {
643  assert(currentNode->hasCopy());
645  }
646  emit statusChanged(currentNode,stats,true);
647  for (int i=0; i<moveInspectors.size(); i++) {
648  if (moveInspectors[i].second) {
649  failedInspectorType = 0;
650  failedInspector = i;
651  moveInspectors[i].first->
652  inspect(*currentNode->getWorkingSpace());
653  failedInspectorType = -1;
654  }
655  }
656  if (currentNode->getStatus() == SOLVED) {
657  currentNode->purge(*na);
658  }
659  }
660  break;
661  case FAILED:
662  case STOP:
663  case UNSTOP:
664  case BRANCH:
665  case SOLVED:
666  {
667  // SizeCursor sc(currentNode);
668  // PreorderNodeVisitor<SizeCursor> pnv(sc);
669  // int nodes = 1;
670  // while (pnv.next()) { nodes++; }
671  // std::cout << "sizeof(VisualNode): " << sizeof(VisualNode)
672  // << std::endl;
673  // std::cout << "Size: " << (pnv.getCursor().s)/1024 << std::endl;
674  // std::cout << "Nodes: " << nodes << std::endl;
675  // std::cout << "Size / node: " << (pnv.getCursor().s)/nodes
676  // << std::endl;
677 
678  Space* curSpace;
679 
680  if (fix) {
682  break;
683  curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
684  if (currentNode->getStatus() == SOLVED &&
685  curSpace->status() != SS_SOLVED) {
686  // in the presence of weakly monotonic propagators, we may have to
687  // use search to find the solution here
688  Space* dfsSpace = Gecode::dfs(curSpace);
689  delete curSpace;
690  curSpace = dfsSpace;
691  }
692  } else {
693  if (currentNode->isRoot())
694  break;
696  curSpace = p->getSpace(*na,curBest,c_d,a_d);
697  switch (curSpace->status()) {
698  case SS_SOLVED:
699  case SS_FAILED:
700  break;
701  case SS_BRANCH:
702  curSpace->commit(*p->getChoice(),
704  break;
705  default:
706  GECODE_NEVER;
707  }
708  }
709 
710  if (inspectorNo==-1) {
711  for (int i=0; i<doubleClickInspectors.size(); i++) {
712  if (doubleClickInspectors[i].second) {
713  failedInspectorType = 1;
714  failedInspector = i;
715  doubleClickInspectors[i].first->inspect(*curSpace);
716  failedInspectorType = -1;
717  }
718  }
719  } else {
720  failedInspectorType = 1;
721  failedInspector = inspectorNo;
722  doubleClickInspectors[inspectorNo].first->inspect(*curSpace);
723  failedInspectorType = -1;
724  }
725  delete curSpace;
726  }
727  break;
728  }
729  } catch (Exception e) {
730  switch (failedInspectorType) {
731  case 0:
732  std::cerr << "Exception in move inspector "
733  << failedInspector;
734  break;
735  case 1:
736  std::cerr << "Exception in double-click inspector "
737  << failedInspector;
738  break;
739  default:
740  std::cerr << "Exception ";
741  break;
742  }
743  std::cerr << ": " << e.what() << "." << std::endl;
744  std::cerr << "Stopping..." << std::endl;
745  std::exit(EXIT_FAILURE);
746  }
747 
749  update();
750  if (needCentering)
752  }
753 
754  void
756  inspectCurrentNode(false);
757  }
758 
759  void
760  TreeCanvas::inspectSolution(const Space* s) {
761  int failedInspectorType = -1;
762  int failedInspector = -1;
763  try {
764  Space* c = NULL;
765  for (int i=0; i<solutionInspectors.size(); i++) {
766  if (solutionInspectors[i].second) {
767  if (c == NULL)
768  c = s->clone();
769  failedInspectorType = 1;
770  failedInspector = i;
771  solutionInspectors[i].first->inspect(*c);
772  failedInspectorType = -1;
773  }
774  }
775  delete c;
776  } catch (Exception e) {
777  switch (failedInspectorType) {
778  case 0:
779  std::cerr << "Exception in move inspector "
780  << failedInspector;
781  break;
782  case 1:
783  std::cerr << "Exception in solution inspector "
784  << failedInspector;
785  break;
786  default:
787  std::cerr << "Exception ";
788  break;
789  }
790  std::cerr << ": " << e.what() << "." << std::endl;
791  std::cerr << "Stopping..." << std::endl;
792  std::exit(EXIT_FAILURE);
793  }
794  }
795 
796  void
798  stopSearchFlag = true;
799  layoutDoneTimerId = startTimer(15);
800  }
801 
802  void
804  QMutexLocker locker(&mutex);
805  Space* rootSpace =
806  root->getStatus() == FAILED ? NULL :
808  if (curBest != NULL) {
809  delete curBest;
810  curBest = new BestNode(NULL);
811  }
812  if (root) {
813  DisposeCursor dc(root,*na);
815  }
816  delete na;
817  na = new Node::NodeAllocator(curBest != NULL);
818  int rootIdx = na->allocate(rootSpace);
819  assert(rootIdx == 0); (void) rootIdx;
820  root = (*na)[0];
821  root->setMarked(true);
822  currentNode = root;
823  pathHead = root;
824  scale = 1.0;
825  stats = Statistics();
826  for (int i=bookmarks.size(); i--;)
827  emit removedBookmark(i);
828  bookmarks.clear();
829  root->layout(*na);
830 
831  emit statusChanged(currentNode, stats, true);
832  update();
833  }
834 
835  void
837  QMutexLocker locker(&mutex);
838  if (!currentNode->isBookmarked()) {
839  bool ok;
840  QString text =
841  QInputDialog::getText(this, "Add bookmark", "Name:",
842  QLineEdit::Normal,"",&ok);
843  if (ok) {
844  currentNode->setBookmarked(true);
845  bookmarks.append(currentNode);
846  if (text == "")
847  text = QString("Node ")+QString().setNum(bookmarks.size());
848  emit addedBookmark(text);
849  }
850  } else {
851  currentNode->setBookmarked(false);
852  int idx = bookmarks.indexOf(currentNode);
853  bookmarks.remove(idx);
854  emit removedBookmark(idx);
855  }
857  update();
858  }
859 
860  void
862  QMutexLocker locker(&mutex);
863  if(currentNode == pathHead)
864  return;
865 
866  pathHead->unPathUp(*na);
868 
869  currentNode->pathUp(*na);
871  update();
872  }
873 
874  void
876  QMutexLocker locker(&mutex);
878  if (currentNode->isOnPath()) {
880  int nextAlt = currentNode->getPathAlternative(*na);
881  while (nextAlt >= 0) {
884  nextAlt = currentNode->getPathAlternative(*na);
885  }
886  }
887  update();
888  }
889 
890  void
892  QMutexLocker locker(&mutex);
893  compareNodes = true;
894  compareNodesBeforeFP = false;
895  setCursor(QCursor(Qt::CrossCursor));
896  }
897 
898  void
900  QMutexLocker locker(&mutex);
901  compareNodes = true;
902  compareNodesBeforeFP = true;
903  setCursor(QCursor(Qt::CrossCursor));
904  }
905 
906  void
908  emit statusChanged(currentNode, stats, true);
909  }
910 
911  void
913  QMutexLocker locker(&mutex);
914 
916 
917  setCurrentNode(p);
918 
919  if (p != NULL) {
921  }
922  }
923 
924  void
926  QMutexLocker locker(&mutex);
927  if (!currentNode->isHidden()) {
928  switch (currentNode->getStatus()) {
929  case STOP:
930  case UNSTOP:
931  case BRANCH:
932  {
933  int alt = std::max(0, currentNode->getPathAlternative(*na));
934  VisualNode* n = currentNode->getChild(*na,alt);
935  setCurrentNode(n);
937  break;
938  }
939  case SOLVED:
940  case FAILED:
941  case UNDETERMINED:
942  break;
943  }
944  }
945  }
946 
947  void
949  QMutexLocker locker(&mutex);
951  if (p != NULL) {
952  int alt = currentNode->getAlternative(*na);
953  if (alt > 0) {
954  VisualNode* n = p->getChild(*na,alt-1);
955  setCurrentNode(n);
957  }
958  }
959  }
960 
961  void
963  QMutexLocker locker(&mutex);
965  if (p != NULL) {
966  unsigned int alt = currentNode->getAlternative(*na);
967  if (alt + 1 < p->getNumberOfChildren()) {
968  VisualNode* n = p->getChild(*na,alt+1);
969  setCurrentNode(n);
971  }
972  }
973  }
974 
975  void
977  QMutexLocker locker(&mutex);
980  }
981 
982  void
984  QMutexLocker locker(&mutex);
985  NextSolCursor nsc(currentNode,back,*na);
987  nsv.run();
988  VisualNode* n = nsv.getCursor().node();
989  if (n != root) {
990  setCurrentNode(n);
992  }
993  }
994 
995  void
997  navNextSol(true);
998  }
999 
1000  void
1001  TreeCanvas::exportNodePDF(VisualNode* n) {
1002 #if QT_VERSION >= 0x040400
1003  QString filename = QFileDialog::getSaveFileName(this, tr("Export tree as pdf"), "", tr("PDF (*.pdf)"));
1004  if (filename != "") {
1005  QPrinter printer(QPrinter::ScreenResolution);
1006  QMutexLocker locker(&mutex);
1007 
1008  BoundingBox bb = n->getBoundingBox();
1009  printer.setFullPage(true);
1010  printer.setPaperSize(QSizeF(bb.right-bb.left+Layout::extent,
1011  n->getShape()->depth() * Layout::dist_y +
1012  Layout::extent), QPrinter::Point);
1013  printer.setOutputFileName(filename);
1014  QPainter painter(&printer);
1015 
1016  painter.setRenderHint(QPainter::Antialiasing);
1017 
1018  QRect pageRect = printer.pageRect();
1019  double newXScale =
1020  static_cast<double>(pageRect.width()) / (bb.right - bb.left +
1021  Layout::extent);
1022  double newYScale =
1023  static_cast<double>(pageRect.height()) /
1024  (n->getShape()->depth() * Layout::dist_y +
1025  Layout::extent);
1026  double printScale = std::min(newXScale, newYScale);
1027  painter.scale(printScale,printScale);
1028 
1029  int printxtrans = -bb.left+(Layout::extent / 2);
1030 
1031  painter.translate(printxtrans, Layout::dist_y / 2);
1032  QRect clip(0,0,0,0);
1033  DrawingCursor dc(n, *na, curBest, painter, clip, showCopies);
1034  currentNode->setMarked(false);
1036  currentNode->setMarked(true);
1037  }
1038 #else
1039  (void) n;
1040 #endif
1041  }
1042 
1043  void
1045 #if QT_VERSION >= 0x040400
1046  exportNodePDF(root);
1047 #endif
1048  }
1049 
1050  void
1052 #if QT_VERSION >= 0x040400
1053  exportNodePDF(currentNode);
1054 #endif
1055  }
1056 
1057  void
1059  QPrinter printer;
1060  if (QPrintDialog(&printer, this).exec() == QDialog::Accepted) {
1061  QMutexLocker locker(&mutex);
1062 
1064  QRect pageRect = printer.pageRect();
1065  double newXScale =
1066  static_cast<double>(pageRect.width()) / (bb.right - bb.left +
1067  Layout::extent);
1068  double newYScale =
1069  static_cast<double>(pageRect.height()) /
1070  (root->getShape()->depth() * Layout::dist_y +
1071  2*Layout::extent);
1072  double printScale = std::min(newXScale, newYScale)*100;
1073  if (printScale<1.0)
1074  printScale = 1.0;
1075  if (printScale > 400.0)
1076  printScale = 400.0;
1077  printScale = printScale / 100.0;
1078 
1079  QPainter painter(&printer);
1080  painter.setRenderHint(QPainter::Antialiasing);
1081  painter.scale(printScale,printScale);
1082  painter.translate(xtrans, 0);
1083  QRect clip(0,0,0,0);
1084  DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
1086  }
1087  }
1088 
1089  VisualNode*
1090  TreeCanvas::eventNode(QEvent* event) {
1091  int x = 0;
1092  int y = 0;
1093  switch (event->type()) {
1094  case QEvent::ToolTip:
1095  {
1096  QHelpEvent* he = static_cast<QHelpEvent*>(event);
1097  x = he->x();
1098  y = he->y();
1099  break;
1100  }
1101  case QEvent::MouseButtonDblClick:
1102  case QEvent::MouseButtonPress:
1103  case QEvent::MouseButtonRelease:
1104  case QEvent::MouseMove:
1105  {
1106  QMouseEvent* me = static_cast<QMouseEvent*>(event);
1107  x = me->x();
1108  y = me->y();
1109  break;
1110  }
1111  case QEvent::ContextMenu:
1112  {
1113  QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
1114  x = ce->x();
1115  y = ce->y();
1116  break;
1117  }
1118  default:
1119  return NULL;
1120  }
1121  QAbstractScrollArea* sa =
1122  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
1123  int xoff = sa->horizontalScrollBar()->value()/scale;
1124  int yoff = sa->verticalScrollBar()->value()/scale;
1125 
1127  int w =
1128  static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
1129  if (w < sa->viewport()->width())
1130  xoff -= (sa->viewport()->width()-w)/2;
1131 
1132  VisualNode* n;
1133  n = root->findNode(*na,
1134  static_cast<int>(x/scale-xtrans+xoff),
1135  static_cast<int>((y-30)/scale+yoff));
1136  return n;
1137  }
1138 
1139  bool
1140  TreeCanvas::event(QEvent* event) {
1141  if (mutex.tryLock()) {
1142  if (event->type() == QEvent::ToolTip) {
1143  VisualNode* n = eventNode(event);
1144  if (n != NULL && !n->isHidden() &&
1145  (n->getStatus() == BRANCH || n->getStatus() == STOP)) {
1146  QHelpEvent* he = static_cast<QHelpEvent*>(event);
1147  QToolTip::showText(he->globalPos(),
1148  QString(n->toolTip(curBest,c_d,a_d).c_str()));
1149  } else {
1150  QToolTip::hideText();
1151  }
1152  }
1153  mutex.unlock();
1154  }
1155  return QWidget::event(event);
1156  }
1157 
1158  void
1160  if (autoZoom)
1161  zoomToFit();
1162  }
1163 
1164  void
1165  TreeCanvas::paintEvent(QPaintEvent* event) {
1166  QMutexLocker locker(&layoutMutex);
1167  QPainter painter(this);
1168  painter.setRenderHint(QPainter::Antialiasing);
1169 
1170  QAbstractScrollArea* sa =
1171  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
1172  int xoff = sa->horizontalScrollBar()->value()/scale;
1173  int yoff = sa->verticalScrollBar()->value()/scale;
1174 
1176  int w =
1177  static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
1178  if (w < sa->viewport()->width())
1179  xoff -= (sa->viewport()->width()-w)/2;
1180 
1181  QRect origClip = event->rect();
1182  painter.translate(0, 30);
1183  painter.scale(scale,scale);
1184  painter.translate(xtrans-xoff, -yoff);
1185  QRect clip(static_cast<int>(origClip.x()/scale-xtrans+xoff),
1186  static_cast<int>(origClip.y()/scale+yoff),
1187  static_cast<int>(origClip.width()/scale),
1188  static_cast<int>(origClip.height()/scale));
1189  DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
1191 
1192  // int nodesLayouted = 1;
1193  // clock_t t0 = clock();
1194  // while (v.next()) { nodesLayouted++; }
1195  // double t = (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC) * 1000.0;
1196  // double nps = static_cast<double>(nodesLayouted) /
1197  // (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC);
1198  // std::cout << "Drawing done. " << nodesLayouted << " nodes in "
1199  // << t << " ms. " << nps << " nodes/s." << std::endl;
1200 
1201  }
1202 
1203  void
1205  if (mutex.tryLock()) {
1206  if(event->button() == Qt::LeftButton) {
1207  VisualNode* n = eventNode(event);
1208  if(n == currentNode) {
1210  event->accept();
1211  mutex.unlock();
1212  return;
1213  }
1214  }
1215  mutex.unlock();
1216  }
1217  event->ignore();
1218  }
1219 
1220  void
1221  TreeCanvas::contextMenuEvent(QContextMenuEvent* event) {
1222  if (mutex.tryLock()) {
1223  VisualNode* n = eventNode(event);
1224  if (n != NULL) {
1225  setCurrentNode(n);
1226  emit contextMenu(event);
1227  event->accept();
1228  mutex.unlock();
1229  return;
1230  }
1231  mutex.unlock();
1232  }
1233  event->ignore();
1234  }
1235 
1236  void
1237  TreeCanvas::resizeEvent(QResizeEvent* e) {
1238  QAbstractScrollArea* sa =
1239  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
1240 
1241  int w = sa->horizontalScrollBar()->maximum()+e->oldSize().width();
1242  int h = sa->verticalScrollBar()->maximum()+e->oldSize().height();
1243 
1244  sa->horizontalScrollBar()->setRange(0,w-e->size().width());
1245  sa->verticalScrollBar()->setRange(0,h-e->size().height());
1246  sa->horizontalScrollBar()->setPageStep(e->size().width());
1247  sa->verticalScrollBar()->setPageStep(e->size().height());
1248  }
1249 
1250  void
1251  TreeCanvas::wheelEvent(QWheelEvent* event) {
1252  if (event->modifiers() & Qt::ShiftModifier) {
1253  event->accept();
1254  if (event->orientation() == Qt::Vertical && !autoZoom)
1255  scaleTree(scale*100+ceil(static_cast<double>(event->delta())/4.0),
1256  event->x(), event->y());
1257  } else {
1258  event->ignore();
1259  }
1260  }
1261 
1262  bool
1264  if (finishedFlag)
1265  return true;
1266  stopSearchFlag = true;
1267  finishedFlag = true;
1268  for (int i=0; i<doubleClickInspectors.size(); i++)
1269  doubleClickInspectors[i].first->finalize();
1270  for (int i=0; i<solutionInspectors.size(); i++)
1271  solutionInspectors[i].first->finalize();
1272  for (int i=0; i<moveInspectors.size(); i++)
1273  moveInspectors[i].first->finalize();
1274  for (int i=0; i<comparators.size(); i++)
1275  comparators[i].first->finalize();
1276  return !searcher.isRunning();
1277  }
1278 
1279  void
1281  QMutexLocker locker(&mutex);
1282  if (update && n != NULL && n != currentNode &&
1283  n->getStatus() != UNDETERMINED && !n->isHidden()) {
1284  Space* curSpace = NULL;
1285  for (int i=0; i<moveInspectors.size(); i++) {
1286  if (moveInspectors[i].second) {
1287  if (curSpace == NULL)
1288  curSpace = n->getSpace(*na,curBest,c_d,a_d);
1289  try {
1290  moveInspectors[i].first->inspect(*curSpace);
1291  } catch (Exception e) {
1292  std::cerr << "Exception in move inspector " << i
1293  << ": " << e.what() << "." << std::endl;
1294  std::cerr << "Stopping..." << std::endl;
1295  std::exit(EXIT_FAILURE);
1296  }
1297  }
1298  }
1299  }
1300  if (n != NULL) {
1301  currentNode->setMarked(false);
1302  currentNode = n;
1303  currentNode->setMarked(true);
1304  emit statusChanged(currentNode,stats,true);
1305  if (update) {
1306  compareNodes = false;
1307  setCursor(QCursor(Qt::ArrowCursor));
1308  QWidget::update();
1309  }
1310  }
1311  }
1312 
1313  void
1314  TreeCanvas::mousePressEvent(QMouseEvent* event) {
1315  if (mutex.tryLock()) {
1316  if (event->button() == Qt::LeftButton) {
1317  VisualNode* n = eventNode(event);
1318  if (compareNodes) {
1319  if (n != NULL && n->getStatus() != UNDETERMINED &&
1320  currentNode != NULL &&
1322  Space* curSpace = NULL;
1323  Space* compareSpace = NULL;
1324  for (int i=0; i<comparators.size(); i++) {
1325  if (comparators[i].second) {
1326  if (curSpace == NULL) {
1327  curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
1328 
1329  if (!compareNodesBeforeFP || n->isRoot()) {
1330  compareSpace = n->getSpace(*na,curBest,c_d,a_d);
1331  } else {
1332  VisualNode* p = n->getParent(*na);
1333  compareSpace = p->getSpace(*na,curBest,c_d,a_d);
1334  switch (compareSpace->status()) {
1335  case SS_SOLVED:
1336  case SS_FAILED:
1337  break;
1338  case SS_BRANCH:
1339  compareSpace->commit(*p->getChoice(),
1340  n->getAlternative(*na));
1341  break;
1342  default:
1343  GECODE_NEVER;
1344  }
1345  }
1346  }
1347  comparators[i].first->compare(*curSpace,*compareSpace);
1348  }
1349  }
1350  }
1351  } else {
1352  setCurrentNode(n);
1353  }
1354  compareNodes = false;
1355  setCursor(QCursor(Qt::ArrowCursor));
1356  if (n != NULL) {
1357  event->accept();
1358  mutex.unlock();
1359  return;
1360  }
1361  }
1362  mutex.unlock();
1363  }
1364  event->ignore();
1365  }
1366 
1367  void
1368  TreeCanvas::setRecompDistances(int c_d0, int a_d0) {
1369  c_d = c_d0; a_d = a_d0;
1370  }
1371 
1372  void
1374  autoHideFailed = b;
1375  }
1376 
1377  void
1379  autoZoom = b;
1380  if (autoZoom) {
1381  zoomToFit();
1382  }
1383  emit autoZoomChanged(b);
1384  scaleBar->setEnabled(!b);
1385  }
1386 
1387  void
1389  showCopies = b;
1390  }
1391  bool
1393  return showCopies;
1394  }
1395 
1396  bool
1398  return autoHideFailed;
1399  }
1400 
1401  bool
1403  return autoZoom;
1404  }
1405 
1406  void
1408  refresh = i;
1409  }
1410 
1411  void
1413  refreshPause = i;
1414  if (refreshPause > 0)
1415  refresh = 1;
1416  }
1417 
1418  bool
1420  return smoothScrollAndZoom;
1421  }
1422 
1423  void
1426  }
1427 
1428 }}
1429 
1430 // STATISTICS: gist-any