Generated on Tue Oct 22 2013 00:49:02 for Gecode by doxygen 1.8.4
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: 2013-05-06 09:02:17 +0200 (Mon, 06 May 2013) $ by $Author: tack $
11  * $Revision: 13613 $
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 #include <QPrinter>
40 #include <QPrintDialog>
41 
42 #include <stack>
43 #include <fstream>
44 
46 
50 
51 #include <gecode/search.hh>
52 #include <gecode/search/support.hh>
53 
54 namespace Gecode { namespace Gist {
55 
56  TreeCanvas::TreeCanvas(Space* rootSpace, bool bab,
57  QWidget* parent, const Options& opt)
58  : QWidget(parent)
59  , mutex(QMutex::Recursive)
60  , layoutMutex(QMutex::Recursive)
61  , finishedFlag(false)
62  , compareNodes(false), compareNodesBeforeFP(false)
63  , autoHideFailed(true), autoZoom(false)
64  , refresh(500), refreshPause(0), smoothScrollAndZoom(false)
65  , moveDuringSearch(false)
66  , zoomTimeLine(500)
67  , scrollTimeLine(1000), targetX(0), sourceX(0), targetY(0), sourceY(0)
68  , targetW(0), targetH(0), targetScale(0)
69  , layoutDoneTimerId(0) {
70  QMutexLocker locker(&mutex);
71  curBest = (bab ? new BestNode(NULL) : NULL);
72  if (rootSpace->status() == SS_FAILED) {
73  if (!opt.clone)
74  delete rootSpace;
75  rootSpace = NULL;
76  } else {
77  rootSpace = Gecode::Search::snapshot(rootSpace,opt);
78  }
79  na = new Node::NodeAllocator(bab);
80  int rootIdx = na->allocate(rootSpace);
81  assert(rootIdx == 0); (void) rootIdx;
82  root = (*na)[0];
83  root->layout(*na);
84  root->setMarked(true);
85  currentNode = root;
86  pathHead = root;
87  scale = LayoutConfig::defScale / 100.0;
88 
89  setAutoFillBackground(true);
90 
91  connect(&searcher, SIGNAL(update(int,int,int)), this,
92  SLOT(layoutDone(int,int,int)));
93  connect(&searcher, SIGNAL(statusChanged(bool)), this,
94  SLOT(statusChanged(bool)));
95 
96  connect(&searcher, SIGNAL(solution(const Space*)),
97  this, SIGNAL(solution(const Space*)),
98  Qt::BlockingQueuedConnection);
99  connect(this, SIGNAL(solution(const Space*)),
100  this, SLOT(inspectSolution(const Space*)));
101  connect(&searcher, SIGNAL(solution(const Space*)),
102  this, SLOT(inspectSolution(const Space*)),
103  Qt::BlockingQueuedConnection);
104 
105  connect(&searcher, SIGNAL(moveToNode(VisualNode*,bool)),
106  this, SLOT(setCurrentNode(VisualNode*,bool)),
107  Qt::BlockingQueuedConnection);
108 
109  connect(&searcher, SIGNAL(searchFinished(void)), this, SIGNAL(searchFinished(void)));
110 
111  connect(&scrollTimeLine, SIGNAL(frameChanged(int)),
112  this, SLOT(scroll(int)));
113  scrollTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
114 
115  scaleBar = new QSlider(Qt::Vertical, this);
116  scaleBar->setObjectName("scaleBar");
117  scaleBar->setMinimum(LayoutConfig::minScale);
118  scaleBar->setMaximum(LayoutConfig::maxScale);
119  scaleBar->setValue(LayoutConfig::defScale);
120  connect(scaleBar, SIGNAL(valueChanged(int)),
121  this, SLOT(scaleTree(int)));
122  connect(this, SIGNAL(scaleChanged(int)), scaleBar, SLOT(setValue(int)));
123  connect(&searcher, SIGNAL(scaleChanged(int)),
124  scaleBar, SLOT(setValue(int)));
125 
126  connect(&zoomTimeLine, SIGNAL(frameChanged(int)),
127  scaleBar, SLOT(setValue(int)));
128  zoomTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
129 
130  qRegisterMetaType<Statistics>("Statistics");
131  update();
132  }
133 
135  if (root) {
136  DisposeCursor dc(root,*na);
138  }
139  delete na;
140  }
141 
142  void
144  doubleClickInspectors.append(QPair<Inspector*,bool>(i,false));
145  }
146 
147  void
149  assert(i < doubleClickInspectors.size());
150  doubleClickInspectors[i].second = active;
151  }
152 
153  void
155  solutionInspectors.append(QPair<Inspector*,bool>(i,false));
156  }
157 
158  void
160  assert(i < solutionInspectors.size());
161  solutionInspectors[i].second = active;
162  }
163 
164  void
166  moveInspectors.append(QPair<Inspector*,bool>(i,false));
167  }
168 
169  void
171  assert(i < moveInspectors.size());
172  moveInspectors[i].second = active;
173  }
174 
175  void
177  comparators.append(QPair<Comparator*,bool>(c,false));
178  }
179 
180  void
181  TreeCanvas::activateComparator(int i, bool active) {
182  assert(i < comparators.size());
183  comparators[i].second = active;
184  }
185 
186  void
187  TreeCanvas::scaleTree(int scale0, int zoomx, int zoomy) {
188  QMutexLocker locker(&layoutMutex);
189 
190  QSize viewport_size = size();
191  QAbstractScrollArea* sa =
192  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
193 
194  if (zoomx==-1)
195  zoomx = viewport_size.width()/2;
196  if (zoomy==-1)
197  zoomy = viewport_size.height()/2;
198 
199  int xoff = (sa->horizontalScrollBar()->value()+zoomx)/scale;
200  int yoff = (sa->verticalScrollBar()->value()+zoomy)/scale;
201 
202  BoundingBox bb;
203  scale0 = std::min(std::max(scale0, LayoutConfig::minScale),
205  scale = (static_cast<double>(scale0)) / 100.0;
206  bb = root->getBoundingBox();
207  int w =
208  static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
209  int h =
210  static_cast<int>(2*Layout::extent+
212 
213  sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
214  sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
215  sa->horizontalScrollBar()->setPageStep(viewport_size.width());
216  sa->verticalScrollBar()->setPageStep(viewport_size.height());
217  sa->horizontalScrollBar()->setSingleStep(Layout::extent);
218  sa->verticalScrollBar()->setSingleStep(Layout::extent);
219 
220  xoff *= scale;
221  yoff *= scale;
222 
223  sa->horizontalScrollBar()->setValue(xoff-zoomx);
224  sa->verticalScrollBar()->setValue(yoff-zoomy);
225 
226  emit scaleChanged(scale0);
227  QWidget::update();
228  }
229 
230  void
232  QMutexLocker locker(&mutex);
233  layoutMutex.lock();
234  if (root != NULL) {
235  root->layout(*na);
237 
238  int w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
239  int h =
240  static_cast<int>(2*Layout::extent+
242  xtrans = -bb.left+(Layout::extent / 2);
243 
244  QSize viewport_size = size();
245  QAbstractScrollArea* sa =
246  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
247  sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
248  sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
249  sa->horizontalScrollBar()->setPageStep(viewport_size.width());
250  sa->verticalScrollBar()->setPageStep(viewport_size.height());
251  sa->horizontalScrollBar()->setSingleStep(Layout::extent);
252  sa->verticalScrollBar()->setSingleStep(Layout::extent);
253  }
254  if (autoZoom)
255  zoomToFit();
256  layoutMutex.unlock();
257  QWidget::update();
258  }
259 
260  void
262  QWidget::update();
263  }
264 
265  void
266  TreeCanvas::layoutDone(int w, int h, int scale0) {
267  targetW = w; targetH = h; targetScale = scale0;
268 
269  QSize viewport_size = size();
270  QAbstractScrollArea* sa =
271  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
272  sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
273  sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
274 
275  if (layoutDoneTimerId == 0)
276  layoutDoneTimerId = startTimer(15);
277  }
278 
279  void
280  TreeCanvas::statusChanged(bool finished) {
281  if (finished) {
282  update();
284  }
285  emit statusChanged(currentNode, stats, finished);
286  }
287 
288  void
290  node = n;
291 
292  depth = -1;
293  for (VisualNode* p = n; p != NULL; p = p->getParent(*ti->na))
294  depth++;
295 
296  a = all;
297  t = ti;
298  start();
299  }
300 
301  void
302  SearcherThread::updateCanvas(void) {
303  t->layoutMutex.lock();
304  if (t->root == NULL)
305  return;
306 
307  if (t->autoHideFailed) {
308  t->root->hideFailed(*t->na,true);
309  }
310  for (VisualNode* n = t->currentNode; n != NULL; n=n->getParent(*t->na)) {
311  if (n->isHidden()) {
312  t->currentNode->setMarked(false);
313  t->currentNode = n;
314  t->currentNode->setMarked(true);
315  break;
316  }
317  }
318 
319  t->root->layout(*t->na);
320  BoundingBox bb = t->root->getBoundingBox();
321 
322  int w = static_cast<int>((bb.right-bb.left+Layout::extent)*t->scale);
323  int h = static_cast<int>(2*Layout::extent+
324  t->root->getShape()->depth()
325  *Layout::dist_y*t->scale);
326  t->xtrans = -bb.left+(Layout::extent / 2);
327 
328  int scale0 = static_cast<int>(t->scale*100);
329  if (t->autoZoom) {
330  QWidget* p = t->parentWidget();
331  if (p) {
332  double newXScale =
333  static_cast<double>(p->width()) / (bb.right - bb.left +
335  double newYScale =
336  static_cast<double>(p->height()) /
338 
339  scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
340  if (scale0<LayoutConfig::minScale)
341  scale0 = LayoutConfig::minScale;
344  double scale = (static_cast<double>(scale0)) / 100.0;
345 
346  w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
347  h = static_cast<int>(2*Layout::extent+
348  t->root->getShape()->depth()*Layout::dist_y*scale);
349  }
350  }
351 
352  t->layoutMutex.unlock();
353  emit update(w,h,scale0);
354  }
355 
357  class SearchItem {
358  public:
362  int i;
366  SearchItem(VisualNode* n0, int noOfChildren0)
367  : n(n0), i(-1), noOfChildren(noOfChildren0) {}
368  };
369 
370  void
372  {
373  if (!node->isOpen())
374  return;
375  t->mutex.lock();
376  emit statusChanged(false);
377 
378  unsigned int kids =
379  node->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
380  t->c_d, t->a_d);
381  if (kids == 0 || node->getStatus() == STOP) {
382  t->mutex.unlock();
383  updateCanvas();
384  emit statusChanged(true);
385  return;
386  }
387 
388  std::stack<SearchItem> stck;
389  stck.push(SearchItem(node,kids));
390  t->stats.maxDepth =
391  std::max(static_cast<long unsigned int>(t->stats.maxDepth),
392  static_cast<long unsigned int>(depth+stck.size()));
393 
394  VisualNode* sol = NULL;
395  int nodeCount = 0;
396  t->stopSearchFlag = false;
397  while (!stck.empty() && !t->stopSearchFlag) {
398  if (t->refresh > 0 && nodeCount >= t->refresh) {
399  node->dirtyUp(*t->na);
400  updateCanvas();
401  emit statusChanged(false);
402  nodeCount = 0;
403  if (t->refreshPause > 0)
404  msleep(t->refreshPause);
405  }
406  SearchItem& si = stck.top();
407  si.i++;
408  if (si.i == si.noOfChildren) {
409  stck.pop();
410  } else {
411  VisualNode* n = si.n->getChild(*t->na,si.i);
412  if (n->isOpen()) {
413  if (n->getStatus() == UNDETERMINED)
414  nodeCount++;
415  kids = n->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
416  t->c_d, t->a_d);
417  if (kids == 0) {
418  if (n->getStatus() == SOLVED) {
419  assert(n->hasCopy());
420  emit solution(n->getWorkingSpace());
421  n->purge(*t->na);
422  sol = n;
423  if (!a)
424  break;
425  }
426  } else {
427  if ( n->getStatus() != STOP )
428  stck.push(SearchItem(n,kids));
429  else if (!a)
430  break;
431  t->stats.maxDepth =
432  std::max(static_cast<long unsigned int>(t->stats.maxDepth),
433  static_cast<long unsigned int>(depth+stck.size()));
434  }
435  }
436  if (t->moveDuringSearch)
437  emit moveToNode(n,false);
438  }
439  }
440  node->dirtyUp(*t->na);
441  t->stopSearchFlag = false;
442  t->mutex.unlock();
443  if (sol != NULL) {
444  t->setCurrentNode(sol,true,false);
445  } else {
446  t->setCurrentNode(node,true,false);
447  }
448  }
449  updateCanvas();
450  emit statusChanged(true);
451  if (t->finishedFlag)
452  emit searchFinished();
453  }
454 
455  void
457  QMutexLocker locker(&mutex);
458  searcher.search(currentNode, true, this);
459  }
460 
461  void
463  QMutexLocker locker(&mutex);
464  searcher.search(currentNode, false, this);
465  }
466 
467  void
469  QMutexLocker locker(&mutex);
471  update();
473  emit statusChanged(currentNode, stats, true);
474  }
475 
476  void
478  QMutexLocker locker(&mutex);
480  update();
482  emit statusChanged(currentNode, stats, true);
483  }
484 
485  void
487  QMutexLocker locker(&mutex);
488  QMutexLocker layoutLocker(&layoutMutex);
490  update();
492  emit statusChanged(currentNode, stats, true);
493  }
494 
495  void
497  QMutexLocker locker(&mutex);
499  update();
501  emit statusChanged(currentNode, stats, true);
502  }
503 
504  void
506  QMutexLocker locker(&mutex);
507  QMutexLocker layoutLocker(&layoutMutex);
509  update();
511  emit statusChanged(currentNode, stats, true);
512  }
513 
514  void
515  TreeCanvas::timerEvent(QTimerEvent* e) {
516  if (e->timerId() == layoutDoneTimerId) {
517  if (!smoothScrollAndZoom) {
519  } else {
520  zoomTimeLine.stop();
521  int zoomCurrent = static_cast<int>(scale*100);
522  int targetZoom = targetScale;
523  targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
525  zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
526  zoomTimeLine.start();
527  }
528  QWidget::update();
529  killTimer(layoutDoneTimerId);
530  layoutDoneTimerId = 0;
531  }
532  }
533 
534  void
536  QMutexLocker locker(&layoutMutex);
537  if (root != NULL) {
538  BoundingBox bb;
539  bb = root->getBoundingBox();
540  QWidget* p = parentWidget();
541  if (p) {
542  double newXScale =
543  static_cast<double>(p->width()) / (bb.right - bb.left +
545  double newYScale =
546  static_cast<double>(p->height()) / (root->getShape()->depth() *
548  2*Layout::extent);
549  int scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
550  if (scale0<LayoutConfig::minScale)
551  scale0 = LayoutConfig::minScale;
554 
555  if (!smoothScrollAndZoom) {
556  scaleTree(scale0);
557  } else {
558  zoomTimeLine.stop();
559  int zoomCurrent = static_cast<int>(scale*100);
560  int targetZoom = scale0;
561  targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
563  zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
564  zoomTimeLine.start();
565  }
566  }
567  }
568  }
569 
570  void
572  QMutexLocker locker(&mutex);
573  int x=0;
574  int y=0;
575 
577  while (c != NULL) {
578  x += c->getOffset();
579  y += Layout::dist_y;
580  c = c->getParent(*na);
581  }
582 
583  x = static_cast<int>((xtrans+x)*scale); y = static_cast<int>(y*scale);
584 
585  QAbstractScrollArea* sa =
586  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
587 
588  x -= sa->viewport()->width() / 2;
589  y -= sa->viewport()->height() / 2;
590 
591  sourceX = sa->horizontalScrollBar()->value();
592  targetX = std::max(sa->horizontalScrollBar()->minimum(), x);
593  targetX = std::min(sa->horizontalScrollBar()->maximum(),
594  targetX);
595  sourceY = sa->verticalScrollBar()->value();
596  targetY = std::max(sa->verticalScrollBar()->minimum(), y);
597  targetY = std::min(sa->verticalScrollBar()->maximum(),
598  targetY);
599  if (!smoothScrollAndZoom) {
600  sa->horizontalScrollBar()->setValue(targetX);
601  sa->verticalScrollBar()->setValue(targetY);
602  } else {
603  scrollTimeLine.stop();
604  scrollTimeLine.setFrameRange(0,100);
605  scrollTimeLine.setDuration(std::max(200,
606  std::min(1000,
607  std::min(std::abs(sourceX-targetX),
608  std::abs(sourceY-targetY)))));
609  scrollTimeLine.start();
610  }
611  }
612 
613  void
614  TreeCanvas::scroll(int i) {
615  QAbstractScrollArea* sa =
616  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
617  double p = static_cast<double>(i)/100.0;
618  double xdiff = static_cast<double>(targetX-sourceX)*p;
619  double ydiff = static_cast<double>(targetY-sourceY)*p;
620  sa->horizontalScrollBar()->setValue(sourceX+static_cast<int>(xdiff));
621  sa->verticalScrollBar()->setValue(sourceY+static_cast<int>(ydiff));
622  }
623 
624  void
625  TreeCanvas::inspectCurrentNode(bool fix, int inspectorNo) {
626  QMutexLocker locker(&mutex);
627 
628  if (currentNode->isHidden()) {
629  toggleHidden();
630  return;
631  }
632 
633  int failedInspectorType = -1;
634  int failedInspector = -1;
635  bool needCentering = false;
636  try {
637  switch (currentNode->getStatus()) {
638  case UNDETERMINED:
639  {
640  unsigned int kids =
642  int depth = -1;
643  for (VisualNode* p = currentNode; p != NULL; p=p->getParent(*na))
644  depth++;
645  if (kids > 0) {
646  needCentering = true;
647  depth++;
648  }
649  stats.maxDepth =
650  std::max(stats.maxDepth, depth);
651  if (currentNode->getStatus() == SOLVED) {
652  assert(currentNode->hasCopy());
654  }
655  emit statusChanged(currentNode,stats,true);
656  for (int i=0; i<moveInspectors.size(); i++) {
657  if (moveInspectors[i].second) {
658  failedInspectorType = 0;
659  failedInspector = i;
660  if (currentNode->getStatus() == FAILED) {
661  if (!currentNode->isRoot()) {
662  Space* curSpace =
664  moveInspectors[i].first->inspect(*curSpace);
665  delete curSpace;
666  }
667  } else {
668  moveInspectors[i].first->
669  inspect(*currentNode->getWorkingSpace());
670  }
671  failedInspectorType = -1;
672  }
673  }
674  if (currentNode->getStatus() == SOLVED) {
675  currentNode->purge(*na);
676  }
677  }
678  break;
679  case FAILED:
680  case STOP:
681  case UNSTOP:
682  case BRANCH:
683  case SOLVED:
684  {
685  // SizeCursor sc(currentNode);
686  // PreorderNodeVisitor<SizeCursor> pnv(sc);
687  // int nodes = 1;
688  // while (pnv.next()) { nodes++; }
689  // std::cout << "sizeof(VisualNode): " << sizeof(VisualNode)
690  // << std::endl;
691  // std::cout << "Size: " << (pnv.getCursor().s)/1024 << std::endl;
692  // std::cout << "Nodes: " << nodes << std::endl;
693  // std::cout << "Size / node: " << (pnv.getCursor().s)/nodes
694  // << std::endl;
695 
696  Space* curSpace;
697 
698  if (fix) {
700  break;
701  curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
702  if (currentNode->getStatus() == SOLVED &&
703  curSpace->status() != SS_SOLVED) {
704  // in the presence of weakly monotonic propagators, we may have
705  // to use search to find the solution here
706  assert(curSpace->status() == SS_BRANCH &&
707  "Something went wrong - probably an incorrect brancher");
708  Space* dfsSpace = Gecode::dfs(curSpace);
709  delete curSpace;
710  curSpace = dfsSpace;
711  }
712  } else {
713  if (currentNode->isRoot())
714  break;
716  curSpace = p->getSpace(*na,curBest,c_d,a_d);
717  switch (curSpace->status()) {
718  case SS_SOLVED:
719  case SS_FAILED:
720  break;
721  case SS_BRANCH:
722  curSpace->commit(*p->getChoice(),
724  break;
725  default:
726  GECODE_NEVER;
727  }
728  }
729 
730  if (inspectorNo==-1) {
731  for (int i=0; i<doubleClickInspectors.size(); i++) {
732  if (doubleClickInspectors[i].second) {
733  failedInspectorType = 1;
734  failedInspector = i;
735  doubleClickInspectors[i].first->inspect(*curSpace);
736  failedInspectorType = -1;
737  }
738  }
739  } else {
740  failedInspectorType = 1;
741  failedInspector = inspectorNo;
742  doubleClickInspectors[inspectorNo].first->inspect(*curSpace);
743  failedInspectorType = -1;
744  }
745  delete curSpace;
746  }
747  break;
748  }
749  } catch (Exception& e) {
750  switch (failedInspectorType) {
751  case 0:
752  qFatal("Exception in move inspector %d: %s.\n Stopping.",
753  failedInspector, e.what());
754  break;
755  case 1:
756  qFatal("Exception in double click inspector %d: %s.\n Stopping.",
757  failedInspector, e.what());
758  break;
759  default:
760  qFatal("Exception: %s.\n Stopping.", e.what());
761  break;
762  }
763  }
764 
766  update();
767  if (needCentering)
769  }
770 
771  void
773  inspectCurrentNode(false);
774  }
775 
776  void
778  QMutexLocker locker(&mutex);
780  update();
782  emit statusChanged(currentNode, stats, true);
783  }
784  void
786  QMutexLocker locker(&mutex);
788  update();
790  emit statusChanged(currentNode, stats, true);
791  }
792 
793  void
794  TreeCanvas::inspectSolution(const Space* s) {
795  int failedInspectorType = -1;
796  int failedInspector = -1;
797  try {
798  Space* c = NULL;
799  for (int i=0; i<solutionInspectors.size(); i++) {
800  if (solutionInspectors[i].second) {
801  if (c == NULL)
802  c = s->clone();
803  failedInspectorType = 1;
804  failedInspector = i;
805  solutionInspectors[i].first->inspect(*c);
806  failedInspectorType = -1;
807  }
808  }
809  delete c;
810  } catch (Exception& e) {
811  switch (failedInspectorType) {
812  case 0:
813  qFatal("Exception in move inspector %d: %s.\n Stopping.",
814  failedInspector, e.what());
815  break;
816  case 1:
817  qFatal("Exception in solution inspector %d: %s.\n Stopping.",
818  failedInspector, e.what());
819  break;
820  default:
821  qFatal("Exception: %s.\n Stopping.", e.what());
822  break;
823  }
824  }
825  }
826 
827  void
829  stopSearchFlag = true;
830  layoutDoneTimerId = startTimer(15);
831  }
832 
833  void
835  QMutexLocker locker(&mutex);
836  Space* rootSpace =
837  root->getStatus() == FAILED ? NULL :
839  if (curBest != NULL) {
840  delete curBest;
841  curBest = new BestNode(NULL);
842  }
843  if (root) {
844  DisposeCursor dc(root,*na);
846  }
847  delete na;
848  na = new Node::NodeAllocator(curBest != NULL);
849  int rootIdx = na->allocate(rootSpace);
850  assert(rootIdx == 0); (void) rootIdx;
851  root = (*na)[0];
852  root->setMarked(true);
853  currentNode = root;
854  pathHead = root;
855  scale = 1.0;
856  stats = Statistics();
857  for (int i=bookmarks.size(); i--;)
858  emit removedBookmark(i);
859  bookmarks.clear();
860  root->layout(*na);
861 
862  emit statusChanged(currentNode, stats, true);
863  update();
864  }
865 
866  void
868  QMutexLocker locker(&mutex);
869  if (!currentNode->isBookmarked()) {
870  bool ok;
871  QString text =
872  QInputDialog::getText(this, "Add bookmark", "Name:",
873  QLineEdit::Normal,"",&ok);
874  if (ok) {
875  currentNode->setBookmarked(true);
876  bookmarks.append(currentNode);
877  if (text == "")
878  text = QString("Node ")+QString().setNum(bookmarks.size());
879  emit addedBookmark(text);
880  }
881  } else {
882  currentNode->setBookmarked(false);
883  int idx = bookmarks.indexOf(currentNode);
884  bookmarks.remove(idx);
885  emit removedBookmark(idx);
886  }
888  update();
889  }
890 
891  void
893  QMutexLocker locker(&mutex);
894  if(currentNode == pathHead)
895  return;
896 
897  pathHead->unPathUp(*na);
899 
900  currentNode->pathUp(*na);
902  update();
903  }
904 
905  void
907  QMutexLocker locker(&mutex);
909  if (currentNode->isOnPath()) {
911  int nextAlt = currentNode->getPathAlternative(*na);
912  while (nextAlt >= 0) {
915  nextAlt = currentNode->getPathAlternative(*na);
916  }
917  }
918  update();
919  }
920 
921  void
923  QMutexLocker locker(&mutex);
924  compareNodes = true;
925  compareNodesBeforeFP = false;
926  setCursor(QCursor(Qt::CrossCursor));
927  }
928 
929  void
931  QMutexLocker locker(&mutex);
932  compareNodes = true;
933  compareNodesBeforeFP = true;
934  setCursor(QCursor(Qt::CrossCursor));
935  }
936 
937  void
939  emit statusChanged(currentNode, stats, true);
940  }
941 
942  void
944  QMutexLocker locker(&mutex);
945 
947 
948  setCurrentNode(p);
949 
950  if (p != NULL) {
952  }
953  }
954 
955  void
957  QMutexLocker locker(&mutex);
958  if (!currentNode->isHidden()) {
959  switch (currentNode->getStatus()) {
960  case STOP:
961  case UNSTOP:
962  case BRANCH:
963  {
964  int alt = std::max(0, currentNode->getPathAlternative(*na));
965  VisualNode* n = currentNode->getChild(*na,alt);
966  setCurrentNode(n);
968  break;
969  }
970  case SOLVED:
971  case FAILED:
972  case UNDETERMINED:
973  break;
974  }
975  }
976  }
977 
978  void
980  QMutexLocker locker(&mutex);
982  if (p != NULL) {
983  int alt = currentNode->getAlternative(*na);
984  if (alt > 0) {
985  VisualNode* n = p->getChild(*na,alt-1);
986  setCurrentNode(n);
988  }
989  }
990  }
991 
992  void
994  QMutexLocker locker(&mutex);
996  if (p != NULL) {
997  unsigned int alt = currentNode->getAlternative(*na);
998  if (alt + 1 < p->getNumberOfChildren()) {
999  VisualNode* n = p->getChild(*na,alt+1);
1000  setCurrentNode(n);
1002  }
1003  }
1004  }
1005 
1006  void
1008  QMutexLocker locker(&mutex);
1011  }
1012 
1013  void
1015  QMutexLocker locker(&mutex);
1016  NextSolCursor nsc(currentNode,back,*na);
1018  nsv.run();
1019  VisualNode* n = nsv.getCursor().node();
1020  if (n != root) {
1021  setCurrentNode(n);
1023  }
1024  }
1025 
1026  void
1028  navNextSol(true);
1029  }
1030 
1031  void
1032  TreeCanvas::exportNodePDF(VisualNode* n) {
1033 #if QT_VERSION >= 0x040400
1034  QString filename = QFileDialog::getSaveFileName(this, tr("Export tree as pdf"), "", tr("PDF (*.pdf)"));
1035  if (filename != "") {
1036  QPrinter printer(QPrinter::ScreenResolution);
1037  QMutexLocker locker(&mutex);
1038 
1039  BoundingBox bb = n->getBoundingBox();
1040  printer.setFullPage(true);
1041  printer.setPaperSize(QSizeF(bb.right-bb.left+Layout::extent,
1042  n->getShape()->depth() * Layout::dist_y +
1043  Layout::extent), QPrinter::Point);
1044  printer.setOutputFileName(filename);
1045  QPainter painter(&printer);
1046 
1047  painter.setRenderHint(QPainter::Antialiasing);
1048 
1049  QRect pageRect = printer.pageRect();
1050  double newXScale =
1051  static_cast<double>(pageRect.width()) / (bb.right - bb.left +
1052  Layout::extent);
1053  double newYScale =
1054  static_cast<double>(pageRect.height()) /
1055  (n->getShape()->depth() * Layout::dist_y +
1056  Layout::extent);
1057  double printScale = std::min(newXScale, newYScale);
1058  painter.scale(printScale,printScale);
1059 
1060  int printxtrans = -bb.left+(Layout::extent / 2);
1061 
1062  painter.translate(printxtrans, Layout::dist_y / 2);
1063  QRect clip(0,0,0,0);
1064  DrawingCursor dc(n, *na, curBest, painter, clip, showCopies);
1065  currentNode->setMarked(false);
1067  currentNode->setMarked(true);
1068  }
1069 #else
1070  (void) n;
1071 #endif
1072  }
1073 
1074  void
1076 #if QT_VERSION >= 0x040400
1077  exportNodePDF(root);
1078 #endif
1079  }
1080 
1081  void
1083 #if QT_VERSION >= 0x040400
1084  exportNodePDF(currentNode);
1085 #endif
1086  }
1087 
1088  void
1090  QPrinter printer;
1091  if (QPrintDialog(&printer, this).exec() == QDialog::Accepted) {
1092  QMutexLocker locker(&mutex);
1093 
1095  QRect pageRect = printer.pageRect();
1096  double newXScale =
1097  static_cast<double>(pageRect.width()) / (bb.right - bb.left +
1098  Layout::extent);
1099  double newYScale =
1100  static_cast<double>(pageRect.height()) /
1101  (root->getShape()->depth() * Layout::dist_y +
1102  2*Layout::extent);
1103  double printScale = std::min(newXScale, newYScale)*100;
1104  if (printScale<1.0)
1105  printScale = 1.0;
1106  if (printScale > 400.0)
1107  printScale = 400.0;
1108  printScale = printScale / 100.0;
1109 
1110  QPainter painter(&printer);
1111  painter.setRenderHint(QPainter::Antialiasing);
1112  painter.scale(printScale,printScale);
1113  painter.translate(xtrans, 0);
1114  QRect clip(0,0,0,0);
1115  DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
1117  }
1118  }
1119 
1120  VisualNode*
1121  TreeCanvas::eventNode(QEvent* event) {
1122  int x = 0;
1123  int y = 0;
1124  switch (event->type()) {
1125  case QEvent::ToolTip:
1126  {
1127  QHelpEvent* he = static_cast<QHelpEvent*>(event);
1128  x = he->x();
1129  y = he->y();
1130  break;
1131  }
1132  case QEvent::MouseButtonDblClick:
1133  case QEvent::MouseButtonPress:
1134  case QEvent::MouseButtonRelease:
1135  case QEvent::MouseMove:
1136  {
1137  QMouseEvent* me = static_cast<QMouseEvent*>(event);
1138  x = me->x();
1139  y = me->y();
1140  break;
1141  }
1142  case QEvent::ContextMenu:
1143  {
1144  QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
1145  x = ce->x();
1146  y = ce->y();
1147  break;
1148  }
1149  default:
1150  return NULL;
1151  }
1152  QAbstractScrollArea* sa =
1153  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
1154  int xoff = sa->horizontalScrollBar()->value()/scale;
1155  int yoff = sa->verticalScrollBar()->value()/scale;
1156 
1158  int w =
1159  static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
1160  if (w < sa->viewport()->width())
1161  xoff -= (sa->viewport()->width()-w)/2;
1162 
1163  VisualNode* n;
1164  n = root->findNode(*na,
1165  static_cast<int>(x/scale-xtrans+xoff),
1166  static_cast<int>((y-30)/scale+yoff));
1167  return n;
1168  }
1169 
1170  bool
1171  TreeCanvas::event(QEvent* event) {
1172  if (mutex.tryLock()) {
1173  if (event->type() == QEvent::ToolTip) {
1174  VisualNode* n = eventNode(event);
1175  if (n != NULL) {
1176  QHelpEvent* he = static_cast<QHelpEvent*>(event);
1177  QToolTip::showText(he->globalPos(),
1178  QString(n->toolTip(*na,curBest,
1179  c_d,a_d).c_str()));
1180  } else {
1181  QToolTip::hideText();
1182  }
1183  }
1184  mutex.unlock();
1185  }
1186  return QWidget::event(event);
1187  }
1188 
1189  void
1191  if (autoZoom)
1192  zoomToFit();
1193  }
1194 
1195  void
1196  TreeCanvas::paintEvent(QPaintEvent* event) {
1197  QMutexLocker locker(&layoutMutex);
1198  QPainter painter(this);
1199  painter.setRenderHint(QPainter::Antialiasing);
1200 
1201  QAbstractScrollArea* sa =
1202  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
1203  int xoff = sa->horizontalScrollBar()->value()/scale;
1204  int yoff = sa->verticalScrollBar()->value()/scale;
1205 
1207  int w =
1208  static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
1209  if (w < sa->viewport()->width())
1210  xoff -= (sa->viewport()->width()-w)/2;
1211 
1212  QRect origClip = event->rect();
1213  painter.translate(0, 30);
1214  painter.scale(scale,scale);
1215  painter.translate(xtrans-xoff, -yoff);
1216  QRect clip(static_cast<int>(origClip.x()/scale-xtrans+xoff),
1217  static_cast<int>(origClip.y()/scale+yoff),
1218  static_cast<int>(origClip.width()/scale),
1219  static_cast<int>(origClip.height()/scale));
1220  DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
1222 
1223  // int nodesLayouted = 1;
1224  // clock_t t0 = clock();
1225  // while (v.next()) { nodesLayouted++; }
1226  // double t = (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC) * 1000.0;
1227  // double nps = static_cast<double>(nodesLayouted) /
1228  // (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC);
1229  // std::cout << "Drawing done. " << nodesLayouted << " nodes in "
1230  // << t << " ms. " << nps << " nodes/s." << std::endl;
1231 
1232  }
1233 
1234  void
1236  if (mutex.tryLock()) {
1237  if(event->button() == Qt::LeftButton) {
1238  VisualNode* n = eventNode(event);
1239  if(n == currentNode) {
1241  event->accept();
1242  mutex.unlock();
1243  return;
1244  }
1245  }
1246  mutex.unlock();
1247  }
1248  event->ignore();
1249  }
1250 
1251  void
1252  TreeCanvas::contextMenuEvent(QContextMenuEvent* event) {
1253  if (mutex.tryLock()) {
1254  VisualNode* n = eventNode(event);
1255  if (n != NULL) {
1256  setCurrentNode(n);
1257  emit contextMenu(event);
1258  event->accept();
1259  mutex.unlock();
1260  return;
1261  }
1262  mutex.unlock();
1263  }
1264  event->ignore();
1265  }
1266 
1267  void
1268  TreeCanvas::resizeEvent(QResizeEvent* e) {
1269  QAbstractScrollArea* sa =
1270  static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
1271 
1272  int w = sa->horizontalScrollBar()->maximum()+e->oldSize().width();
1273  int h = sa->verticalScrollBar()->maximum()+e->oldSize().height();
1274 
1275  sa->horizontalScrollBar()->setRange(0,w-e->size().width());
1276  sa->verticalScrollBar()->setRange(0,h-e->size().height());
1277  sa->horizontalScrollBar()->setPageStep(e->size().width());
1278  sa->verticalScrollBar()->setPageStep(e->size().height());
1279  }
1280 
1281  void
1282  TreeCanvas::wheelEvent(QWheelEvent* event) {
1283  if (event->modifiers() & Qt::ShiftModifier) {
1284  event->accept();
1285  if (event->orientation() == Qt::Vertical && !autoZoom)
1286  scaleTree(scale*100+ceil(static_cast<double>(event->delta())/4.0),
1287  event->x(), event->y());
1288  } else {
1289  event->ignore();
1290  }
1291  }
1292 
1293  bool
1295  if (finishedFlag)
1296  return true;
1297  stopSearchFlag = true;
1298  finishedFlag = true;
1299  for (int i=0; i<doubleClickInspectors.size(); i++)
1300  doubleClickInspectors[i].first->finalize();
1301  for (int i=0; i<solutionInspectors.size(); i++)
1302  solutionInspectors[i].first->finalize();
1303  for (int i=0; i<moveInspectors.size(); i++)
1304  moveInspectors[i].first->finalize();
1305  for (int i=0; i<comparators.size(); i++)
1306  comparators[i].first->finalize();
1307  return !searcher.isRunning();
1308  }
1309 
1310  void
1311  TreeCanvas::setCurrentNode(VisualNode* n, bool finished, bool update) {
1312  if (finished)
1313  mutex.lock();
1314  if (update && n != NULL && n != currentNode &&
1315  n->getStatus() != UNDETERMINED && !n->isHidden()) {
1316  Space* curSpace = NULL;
1317  for (int i=0; i<moveInspectors.size(); i++) {
1318  if (moveInspectors[i].second) {
1319  if (curSpace == NULL)
1320  curSpace = n->getSpace(*na,curBest,c_d,a_d);
1321  try {
1322  moveInspectors[i].first->inspect(*curSpace);
1323  } catch (Exception& e) {
1324  qFatal("Exception in move inspector %d: %s.\n Stopping.",
1325  i, e.what());
1326  }
1327  }
1328  }
1329  }
1330  if (n != NULL) {
1331  currentNode->setMarked(false);
1332  currentNode = n;
1333  currentNode->setMarked(true);
1334  emit statusChanged(currentNode,stats,finished);
1335  if (update) {
1336  compareNodes = false;
1337  setCursor(QCursor(Qt::ArrowCursor));
1338  QWidget::update();
1339  }
1340  }
1341  if (finished)
1342  mutex.unlock();
1343  }
1344 
1345  void
1346  TreeCanvas::mousePressEvent(QMouseEvent* event) {
1347  if (mutex.tryLock()) {
1348  if (event->button() == Qt::LeftButton) {
1349  VisualNode* n = eventNode(event);
1350  if (compareNodes) {
1351  if (n != NULL && n->getStatus() != UNDETERMINED &&
1352  currentNode != NULL &&
1354  Space* curSpace = NULL;
1355  Space* compareSpace = NULL;
1356  for (int i=0; i<comparators.size(); i++) {
1357  if (comparators[i].second) {
1358  if (curSpace == NULL) {
1359  curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
1360 
1361  if (!compareNodesBeforeFP || n->isRoot()) {
1362  compareSpace = n->getSpace(*na,curBest,c_d,a_d);
1363  } else {
1364  VisualNode* p = n->getParent(*na);
1365  compareSpace = p->getSpace(*na,curBest,c_d,a_d);
1366  switch (compareSpace->status()) {
1367  case SS_SOLVED:
1368  case SS_FAILED:
1369  break;
1370  case SS_BRANCH:
1371  compareSpace->commit(*p->getChoice(),
1372  n->getAlternative(*na));
1373  break;
1374  default:
1375  GECODE_NEVER;
1376  }
1377  }
1378  }
1379  try {
1380  comparators[i].first->compare(*curSpace,*compareSpace);
1381  } catch (Exception& e) {
1382  qFatal("Exception in comparator %d: %s.\n Stopping.",
1383  i, e.what());
1384  }
1385  }
1386  }
1387  }
1388  } else {
1389  setCurrentNode(n);
1390  }
1391  compareNodes = false;
1392  setCursor(QCursor(Qt::ArrowCursor));
1393  if (n != NULL) {
1394  event->accept();
1395  mutex.unlock();
1396  return;
1397  }
1398  }
1399  mutex.unlock();
1400  }
1401  event->ignore();
1402  }
1403 
1404  void
1405  TreeCanvas::setRecompDistances(int c_d0, int a_d0) {
1406  c_d = c_d0; a_d = a_d0;
1407  }
1408 
1409  void
1411  autoHideFailed = b;
1412  }
1413 
1414  void
1416  autoZoom = b;
1417  if (autoZoom) {
1418  zoomToFit();
1419  }
1420  emit autoZoomChanged(b);
1421  scaleBar->setEnabled(!b);
1422  }
1423 
1424  void
1426  showCopies = b;
1427  }
1428  bool
1430  return showCopies;
1431  }
1432 
1433  bool
1435  return autoHideFailed;
1436  }
1437 
1438  bool
1440  return autoZoom;
1441  }
1442 
1443  void
1445  refresh = i;
1446  }
1447 
1448  void
1450  refreshPause = i;
1451  if (refreshPause > 0)
1452  refresh = 1;
1453  }
1454 
1455  bool
1457  return smoothScrollAndZoom;
1458  }
1459 
1460  void
1463  }
1464 
1465  bool
1467  return moveDuringSearch;
1468  }
1469 
1470  void
1472  moveDuringSearch = b;
1473  }
1474 
1475 }}
1476 
1477 // STATISTICS: gist-any