1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 981be2067e Squashed 'software/gecode_on_replay/' content from commit 8051d92b9
git-subtree-dir: software/gecode_on_replay
git-subtree-split: 8051d92b9c89e49cccfbd1c201371580d7703ab4
2021-06-16 14:04:29 +10:00

1460 lines
42 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Guido Tack <tack@gecode.org>
*
* Copyright:
* Guido Tack, 2006
*
* This file is part of Gecode, the generic constraint
* development environment:
* http://www.gecode.org
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <QtGui/QPainter>
#include <QPrinter>
#include <QPrintDialog>
#include <stack>
#include <fstream>
#include <gecode/gist/treecanvas.hh>
#include <gecode/gist/nodevisitor.hh>
#include <gecode/gist/visualnode.hh>
#include <gecode/gist/drawingcursor.hh>
#include <gecode/search.hh>
#include <gecode/search/support.hh>
namespace Gecode { namespace Gist {
TreeCanvas::TreeCanvas(Space* rootSpace, bool bab,
QWidget* parent, const Options& opt)
: QWidget(parent)
, mutex(QMutex::Recursive)
, layoutMutex(QMutex::Recursive)
, finishedFlag(false)
, compareNodes(false), compareNodesBeforeFP(false)
, autoHideFailed(true), autoZoom(false)
, refresh(500), refreshPause(0), smoothScrollAndZoom(false)
, moveDuringSearch(false)
, zoomTimeLine(500)
, scrollTimeLine(1000), targetX(0), sourceX(0), targetY(0), sourceY(0)
, targetW(0), targetH(0), targetScale(0)
, layoutDoneTimerId(0) {
QMutexLocker locker(&mutex);
curBest = (bab ? new BestNode(nullptr) : nullptr);
if (rootSpace->status() == SS_FAILED) {
if (!opt.clone)
delete rootSpace;
rootSpace = nullptr;
} else {
rootSpace = Gecode::Search::snapshot(rootSpace,opt);
}
na = new Node::NodeAllocator(bab);
int rootIdx = na->allocate(rootSpace);
assert(rootIdx == 0); (void) rootIdx;
root = (*na)[0];
root->layout(*na);
root->setMarked(true);
currentNode = root;
pathHead = root;
scale = LayoutConfig::defScale / 100.0;
setAutoFillBackground(true);
connect(&searcher, SIGNAL(update(int,int,int)), this,
SLOT(layoutDone(int,int,int)));
connect(&searcher, SIGNAL(statusChanged(bool)), this,
SLOT(statusChanged(bool)));
connect(&searcher, SIGNAL(solution(const Space*)),
this, SIGNAL(solution(const Space*)),
Qt::BlockingQueuedConnection);
connect(this, SIGNAL(solution(const Space*)),
this, SLOT(inspectSolution(const Space*)));
connect(&searcher, SIGNAL(moveToNode(VisualNode*,bool)),
this, SLOT(setCurrentNode(VisualNode*,bool)),
Qt::BlockingQueuedConnection);
connect(&searcher, SIGNAL(searchFinished(void)), this, SIGNAL(searchFinished(void)));
connect(&scrollTimeLine, SIGNAL(frameChanged(int)),
this, SLOT(scroll(int)));
scrollTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
scaleBar = new QSlider(Qt::Vertical, this);
scaleBar->setObjectName("scaleBar");
scaleBar->setMinimum(LayoutConfig::minScale);
scaleBar->setMaximum(LayoutConfig::maxScale);
scaleBar->setValue(LayoutConfig::defScale);
connect(scaleBar, SIGNAL(valueChanged(int)),
this, SLOT(scaleTree(int)));
connect(this, SIGNAL(scaleChanged(int)), scaleBar, SLOT(setValue(int)));
connect(&searcher, SIGNAL(scaleChanged(int)),
scaleBar, SLOT(setValue(int)));
connect(&zoomTimeLine, SIGNAL(frameChanged(int)),
scaleBar, SLOT(setValue(int)));
zoomTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
qRegisterMetaType<Statistics>("Statistics");
update();
}
TreeCanvas::~TreeCanvas(void) {
if (root) {
DisposeCursor dc(root,*na);
PreorderNodeVisitor<DisposeCursor>(dc).run();
}
delete na;
}
void
TreeCanvas::addDoubleClickInspector(Inspector* i) {
doubleClickInspectors.append(QPair<Inspector*,bool>(i,false));
}
void
TreeCanvas::activateDoubleClickInspector(int i, bool active) {
assert(i < doubleClickInspectors.size());
doubleClickInspectors[i].second = active;
}
void
TreeCanvas::addSolutionInspector(Inspector* i) {
solutionInspectors.append(QPair<Inspector*,bool>(i,false));
}
void
TreeCanvas::activateSolutionInspector(int i, bool active) {
assert(i < solutionInspectors.size());
solutionInspectors[i].second = active;
}
void
TreeCanvas::addMoveInspector(Inspector* i) {
moveInspectors.append(QPair<Inspector*,bool>(i,false));
}
void
TreeCanvas::activateMoveInspector(int i, bool active) {
assert(i < moveInspectors.size());
moveInspectors[i].second = active;
}
void
TreeCanvas::addComparator(Comparator* c) {
comparators.append(QPair<Comparator*,bool>(c,false));
}
void
TreeCanvas::activateComparator(int i, bool active) {
assert(i < comparators.size());
comparators[i].second = active;
}
void
TreeCanvas::scaleTree(int scale0, int zoomx, int zoomy) {
QMutexLocker locker(&layoutMutex);
QSize viewport_size = size();
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
if (zoomx==-1)
zoomx = viewport_size.width()/2;
if (zoomy==-1)
zoomy = viewport_size.height()/2;
int xoff = (sa->horizontalScrollBar()->value()+zoomx)/scale;
int yoff = (sa->verticalScrollBar()->value()+zoomy)/scale;
BoundingBox bb;
scale0 = std::min(std::max(scale0, LayoutConfig::minScale),
LayoutConfig::maxScale);
scale = (static_cast<double>(scale0)) / 100.0;
bb = root->getBoundingBox();
int w =
static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
int h =
static_cast<int>(2*Layout::extent+
root->getShape()->depth()*Layout::dist_y*scale);
sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
sa->horizontalScrollBar()->setPageStep(viewport_size.width());
sa->verticalScrollBar()->setPageStep(viewport_size.height());
sa->horizontalScrollBar()->setSingleStep(Layout::extent);
sa->verticalScrollBar()->setSingleStep(Layout::extent);
xoff *= scale;
yoff *= scale;
sa->horizontalScrollBar()->setValue(xoff-zoomx);
sa->verticalScrollBar()->setValue(yoff-zoomy);
emit scaleChanged(scale0);
QWidget::update();
}
void
TreeCanvas::update(void) {
QMutexLocker locker(&mutex);
layoutMutex.lock();
if (root != nullptr) {
root->layout(*na);
BoundingBox bb = root->getBoundingBox();
int w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
int h =
static_cast<int>(2*Layout::extent+
root->getShape()->depth()*Layout::dist_y*scale);
xtrans = -bb.left+(Layout::extent / 2);
QSize viewport_size = size();
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
sa->horizontalScrollBar()->setPageStep(viewport_size.width());
sa->verticalScrollBar()->setPageStep(viewport_size.height());
sa->horizontalScrollBar()->setSingleStep(Layout::extent);
sa->verticalScrollBar()->setSingleStep(Layout::extent);
}
if (autoZoom)
zoomToFit();
layoutMutex.unlock();
QWidget::update();
}
void
TreeCanvas::scroll(void) {
QWidget::update();
}
void
TreeCanvas::layoutDone(int w, int h, int scale0) {
targetW = w; targetH = h; targetScale = scale0;
QSize viewport_size = size();
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
if (layoutDoneTimerId == 0)
layoutDoneTimerId = startTimer(15);
}
void
TreeCanvas::statusChanged(bool finished) {
if (finished) {
update();
centerCurrentNode();
}
emit statusChanged(currentNode, stats, finished);
}
void
SearcherThread::search(VisualNode* n, bool all, TreeCanvas* ti) {
node = n;
depth = -1;
for (VisualNode* p = n; p != nullptr; p = p->getParent(*ti->na))
depth++;
a = all;
t = ti;
start();
}
void
SearcherThread::updateCanvas(void) {
t->layoutMutex.lock();
if (t->root == nullptr)
return;
if (t->autoHideFailed) {
t->root->hideFailed(*t->na,true);
}
for (VisualNode* n = t->currentNode; n != nullptr; n=n->getParent(*t->na)) {
if (n->isHidden()) {
t->currentNode->setMarked(false);
t->currentNode = n;
t->currentNode->setMarked(true);
break;
}
}
t->root->layout(*t->na);
BoundingBox bb = t->root->getBoundingBox();
int w = static_cast<int>((bb.right-bb.left+Layout::extent)*t->scale);
int h = static_cast<int>(2*Layout::extent+
t->root->getShape()->depth()
*Layout::dist_y*t->scale);
t->xtrans = -bb.left+(Layout::extent / 2);
int scale0 = static_cast<int>(t->scale*100);
if (t->autoZoom) {
QWidget* p = t->parentWidget();
if (p) {
double newXScale =
static_cast<double>(p->width()) / (bb.right - bb.left +
Layout::extent);
double newYScale =
static_cast<double>(p->height()) /
(t->root->getShape()->depth() * Layout::dist_y + 2*Layout::extent);
scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
if (scale0<LayoutConfig::minScale)
scale0 = LayoutConfig::minScale;
if (scale0>LayoutConfig::maxAutoZoomScale)
scale0 = LayoutConfig::maxAutoZoomScale;
double scale = (static_cast<double>(scale0)) / 100.0;
w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
h = static_cast<int>(2*Layout::extent+
t->root->getShape()->depth()*Layout::dist_y*scale);
}
}
t->layoutMutex.unlock();
emit update(w,h,scale0);
}
/// A stack item for depth first search
class SearchItem {
public:
/// The node
VisualNode* n;
/// The currently explored child
int i;
/// The number of children
int noOfChildren;
/// Constructor
SearchItem(VisualNode* n0, int noOfChildren0)
: n(n0), i(-1), noOfChildren(noOfChildren0) {}
};
void
SearcherThread::run(void) {
{
if (!node->isOpen())
return;
t->mutex.lock();
emit statusChanged(false);
unsigned int kids =
node->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
t->c_d, t->a_d);
if (kids == 0 || node->getStatus() == STOP) {
t->mutex.unlock();
updateCanvas();
emit statusChanged(true);
return;
}
std::stack<SearchItem> stck;
stck.push(SearchItem(node,kids));
t->stats.maxDepth =
std::max(static_cast<long unsigned int>(t->stats.maxDepth),
static_cast<long unsigned int>(depth+stck.size()));
VisualNode* sol = nullptr;
int nodeCount = 0;
t->stopSearchFlag = false;
while (!stck.empty() && !t->stopSearchFlag) {
if (t->refresh > 0 && nodeCount >= t->refresh) {
node->dirtyUp(*t->na);
updateCanvas();
emit statusChanged(false);
nodeCount = 0;
if (t->refreshPause > 0)
msleep(t->refreshPause);
}
SearchItem& si = stck.top();
si.i++;
if (si.i == si.noOfChildren) {
stck.pop();
} else {
VisualNode* n = si.n->getChild(*t->na,si.i);
if (n->isOpen()) {
if (n->getStatus() == UNDETERMINED)
nodeCount++;
kids = n->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
t->c_d, t->a_d);
if (t->moveDuringSearch)
emit moveToNode(n,false);
if (kids == 0) {
if (n->getStatus() == SOLVED) {
assert(n->hasCopy());
emit solution(n->getWorkingSpace());
n->purge(*t->na);
sol = n;
if (!a)
break;
}
} else {
if ( n->getStatus() != STOP )
stck.push(SearchItem(n,kids));
else if (!a)
break;
t->stats.maxDepth =
std::max(static_cast<long unsigned int>(t->stats.maxDepth),
static_cast<long unsigned int>(depth+stck.size()));
}
}
}
}
node->dirtyUp(*t->na);
t->stopSearchFlag = false;
t->mutex.unlock();
if (sol != nullptr) {
t->setCurrentNode(sol,true,false);
} else {
t->setCurrentNode(node,true,false);
}
}
updateCanvas();
emit statusChanged(true);
if (t->finishedFlag)
emit searchFinished();
}
void
TreeCanvas::searchAll(void) {
QMutexLocker locker(&mutex);
searcher.search(currentNode, true, this);
}
void
TreeCanvas::searchOne(void) {
QMutexLocker locker(&mutex);
searcher.search(currentNode, false, this);
}
void
TreeCanvas::toggleHidden(void) {
QMutexLocker locker(&mutex);
currentNode->toggleHidden(*na);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::hideFailed(void) {
QMutexLocker locker(&mutex);
currentNode->hideFailed(*na);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::unhideAll(void) {
QMutexLocker locker(&mutex);
QMutexLocker layoutLocker(&layoutMutex);
currentNode->unhideAll(*na);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::toggleStop(void) {
QMutexLocker locker(&mutex);
currentNode->toggleStop(*na);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::unstopAll(void) {
QMutexLocker locker(&mutex);
QMutexLocker layoutLocker(&layoutMutex);
currentNode->unstopAll(*na);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::timerEvent(QTimerEvent* e) {
if (e->timerId() == layoutDoneTimerId) {
if (!smoothScrollAndZoom) {
scaleTree(targetScale);
} else {
zoomTimeLine.stop();
int zoomCurrent = static_cast<int>(scale*100);
int targetZoom = targetScale;
targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
LayoutConfig::maxAutoZoomScale);
zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
zoomTimeLine.start();
}
QWidget::update();
killTimer(layoutDoneTimerId);
layoutDoneTimerId = 0;
}
}
void
TreeCanvas::zoomToFit(void) {
QMutexLocker locker(&layoutMutex);
if (root != nullptr) {
BoundingBox bb;
bb = root->getBoundingBox();
QWidget* p = parentWidget();
if (p) {
double newXScale =
static_cast<double>(p->width()) / (bb.right - bb.left +
Layout::extent);
double newYScale =
static_cast<double>(p->height()) / (root->getShape()->depth() *
Layout::dist_y +
2*Layout::extent);
int scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
if (scale0<LayoutConfig::minScale)
scale0 = LayoutConfig::minScale;
if (scale0>LayoutConfig::maxAutoZoomScale)
scale0 = LayoutConfig::maxAutoZoomScale;
if (!smoothScrollAndZoom) {
scaleTree(scale0);
} else {
zoomTimeLine.stop();
int zoomCurrent = static_cast<int>(scale*100);
int targetZoom = scale0;
targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
LayoutConfig::maxAutoZoomScale);
zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
zoomTimeLine.start();
}
}
}
}
void
TreeCanvas::centerCurrentNode(void) {
QMutexLocker locker(&mutex);
int x=0;
int y=0;
VisualNode* c = currentNode;
while (c != nullptr) {
x += c->getOffset();
y += Layout::dist_y;
c = c->getParent(*na);
}
x = static_cast<int>((xtrans+x)*scale); y = static_cast<int>(y*scale);
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
x -= sa->viewport()->width() / 2;
y -= sa->viewport()->height() / 2;
sourceX = sa->horizontalScrollBar()->value();
targetX = std::max(sa->horizontalScrollBar()->minimum(), x);
targetX = std::min(sa->horizontalScrollBar()->maximum(),
targetX);
sourceY = sa->verticalScrollBar()->value();
targetY = std::max(sa->verticalScrollBar()->minimum(), y);
targetY = std::min(sa->verticalScrollBar()->maximum(),
targetY);
if (!smoothScrollAndZoom) {
sa->horizontalScrollBar()->setValue(targetX);
sa->verticalScrollBar()->setValue(targetY);
} else {
scrollTimeLine.stop();
scrollTimeLine.setFrameRange(0,100);
scrollTimeLine.setDuration(std::max(200,
std::min(1000,
std::min(std::abs(sourceX-targetX),
std::abs(sourceY-targetY)))));
scrollTimeLine.start();
}
}
void
TreeCanvas::scroll(int i) {
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
double p = static_cast<double>(i)/100.0;
double xdiff = static_cast<double>(targetX-sourceX)*p;
double ydiff = static_cast<double>(targetY-sourceY)*p;
sa->horizontalScrollBar()->setValue(sourceX+static_cast<int>(xdiff));
sa->verticalScrollBar()->setValue(sourceY+static_cast<int>(ydiff));
}
void
TreeCanvas::inspectCurrentNode(bool fix, int inspectorNo) {
QMutexLocker locker(&mutex);
if (currentNode->isHidden()) {
toggleHidden();
return;
}
int failedInspectorType = -1;
int failedInspector = -1;
bool needCentering = false;
try {
switch (currentNode->getStatus()) {
case UNDETERMINED:
{
unsigned int kids =
currentNode->getNumberOfChildNodes(*na,curBest,stats,c_d,a_d);
int depth = -1;
for (VisualNode* p = currentNode; p != nullptr; p=p->getParent(*na))
depth++;
if (kids > 0) {
needCentering = true;
depth++;
}
stats.maxDepth =
std::max(stats.maxDepth, depth);
if (currentNode->getStatus() == SOLVED) {
assert(currentNode->hasCopy());
emit solution(currentNode->getWorkingSpace());
}
emit statusChanged(currentNode,stats,true);
for (int i=0; i<moveInspectors.size(); i++) {
if (moveInspectors[i].second) {
failedInspectorType = 0;
failedInspector = i;
if (currentNode->getStatus() == FAILED) {
if (!currentNode->isRoot()) {
Space* curSpace =
currentNode->getSpace(*na,curBest,c_d,a_d);
moveInspectors[i].first->inspect(*curSpace);
delete curSpace;
}
} else {
moveInspectors[i].first->
inspect(*currentNode->getWorkingSpace());
}
failedInspectorType = -1;
}
}
if (currentNode->getStatus() == SOLVED) {
currentNode->purge(*na);
}
}
break;
case FAILED:
case STOP:
case UNSTOP:
case BRANCH:
case SOLVED:
{
Space* curSpace;
if (fix) {
if (currentNode->isRoot() && currentNode->getStatus() == FAILED)
break;
curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
if (currentNode->getStatus() == SOLVED &&
curSpace->status() != SS_SOLVED) {
// in the presence of weakly monotonic propagators, we may have
// to use search to find the solution here
assert(curSpace->status() == SS_BRANCH &&
"Something went wrong - probably an incorrect brancher");
Space* dfsSpace = Gecode::dfs(curSpace);
delete curSpace;
curSpace = dfsSpace;
}
} else {
if (currentNode->isRoot())
break;
VisualNode* p = currentNode->getParent(*na);
curSpace = p->getSpace(*na,curBest,c_d,a_d);
switch (curSpace->status()) {
case SS_SOLVED:
case SS_FAILED:
break;
case SS_BRANCH:
curSpace->commit(*p->getChoice(),
currentNode->getAlternative(*na));
break;
default:
GECODE_NEVER;
}
}
if (inspectorNo==-1) {
for (int i=0; i<doubleClickInspectors.size(); i++) {
if (doubleClickInspectors[i].second) {
failedInspectorType = 1;
failedInspector = i;
doubleClickInspectors[i].first->inspect(*curSpace);
failedInspectorType = -1;
}
}
} else {
failedInspectorType = 1;
failedInspector = inspectorNo;
doubleClickInspectors[inspectorNo].first->inspect(*curSpace);
failedInspectorType = -1;
}
delete curSpace;
}
break;
}
} catch (Exception& e) {
switch (failedInspectorType) {
case 0:
qFatal("Exception in move inspector %d: %s.\n Stopping.",
failedInspector, e.what());
break;
case 1:
qFatal("Exception in double click inspector %d: %s.\n Stopping.",
failedInspector, e.what());
break;
default:
qFatal("Exception: %s.\n Stopping.", e.what());
break;
}
}
currentNode->dirtyUp(*na);
update();
if (needCentering)
centerCurrentNode();
}
void
TreeCanvas::inspectBeforeFP(void) {
inspectCurrentNode(false);
}
void
TreeCanvas::labelBranches(void) {
QMutexLocker locker(&mutex);
currentNode->labelBranches(*na,curBest,c_d,a_d);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::labelPath(void) {
QMutexLocker locker(&mutex);
currentNode->labelPath(*na,curBest,c_d,a_d);
update();
centerCurrentNode();
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::inspectSolution(const Space* s) {
int failedInspectorType = -1;
int failedInspector = -1;
try {
Space* c = nullptr;
for (int i=0; i<solutionInspectors.size(); i++) {
if (solutionInspectors[i].second) {
if (c == nullptr)
c = s->clone();
failedInspectorType = 1;
failedInspector = i;
solutionInspectors[i].first->inspect(*c);
failedInspectorType = -1;
}
}
delete c;
} catch (Exception& e) {
switch (failedInspectorType) {
case 0:
qFatal("Exception in move inspector %d: %s.\n Stopping.",
failedInspector, e.what());
break;
case 1:
qFatal("Exception in solution inspector %d: %s.\n Stopping.",
failedInspector, e.what());
break;
default:
qFatal("Exception: %s.\n Stopping.", e.what());
break;
}
}
}
void
TreeCanvas::stopSearch(void) {
stopSearchFlag = true;
layoutDoneTimerId = startTimer(15);
}
void
TreeCanvas::reset(void) {
QMutexLocker locker(&mutex);
Space* rootSpace =
root->getStatus() == FAILED ? nullptr :
root->getSpace(*na,curBest,c_d,a_d);
if (curBest != nullptr) {
delete curBest;
curBest = new BestNode(nullptr);
}
if (root) {
DisposeCursor dc(root,*na);
PreorderNodeVisitor<DisposeCursor>(dc).run();
}
delete na;
na = new Node::NodeAllocator(curBest != nullptr);
int rootIdx = na->allocate(rootSpace);
assert(rootIdx == 0); (void) rootIdx;
root = (*na)[0];
root->setMarked(true);
currentNode = root;
pathHead = root;
scale = 1.0;
stats = Statistics();
for (int i=bookmarks.size(); i--;)
emit removedBookmark(i);
bookmarks.clear();
root->layout(*na);
emit statusChanged(currentNode, stats, true);
update();
}
void
TreeCanvas::bookmarkNode(void) {
QMutexLocker locker(&mutex);
if (!currentNode->isBookmarked()) {
bool ok;
QString text =
QInputDialog::getText(this, "Add bookmark", "Name:",
QLineEdit::Normal,"",&ok);
if (ok) {
currentNode->setBookmarked(true);
bookmarks.append(currentNode);
if (text == "")
text = QString("Node ")+QString().setNum(bookmarks.size());
emit addedBookmark(text);
}
} else {
currentNode->setBookmarked(false);
int idx = bookmarks.indexOf(currentNode);
bookmarks.remove(idx);
emit removedBookmark(idx);
}
currentNode->dirtyUp(*na);
update();
}
void
TreeCanvas::setPath(void) {
QMutexLocker locker(&mutex);
if(currentNode == pathHead)
return;
pathHead->unPathUp(*na);
pathHead = currentNode;
currentNode->pathUp(*na);
currentNode->dirtyUp(*na);
update();
}
void
TreeCanvas::inspectPath(void) {
QMutexLocker locker(&mutex);
setCurrentNode(root);
if (currentNode->isOnPath()) {
inspectCurrentNode();
int nextAlt = currentNode->getPathAlternative(*na);
while (nextAlt >= 0) {
setCurrentNode(currentNode->getChild(*na,nextAlt));
inspectCurrentNode();
nextAlt = currentNode->getPathAlternative(*na);
}
}
update();
}
void
TreeCanvas::startCompareNodes(void) {
QMutexLocker locker(&mutex);
compareNodes = true;
compareNodesBeforeFP = false;
setCursor(QCursor(Qt::CrossCursor));
}
void
TreeCanvas::startCompareNodesBeforeFP(void) {
QMutexLocker locker(&mutex);
compareNodes = true;
compareNodesBeforeFP = true;
setCursor(QCursor(Qt::CrossCursor));
}
void
TreeCanvas::emitStatusChanged(void) {
emit statusChanged(currentNode, stats, true);
}
void
TreeCanvas::navUp(void) {
QMutexLocker locker(&mutex);
VisualNode* p = currentNode->getParent(*na);
setCurrentNode(p);
if (p != nullptr) {
centerCurrentNode();
}
}
void
TreeCanvas::navDown(void) {
QMutexLocker locker(&mutex);
if (!currentNode->isHidden()) {
switch (currentNode->getStatus()) {
case STOP:
case UNSTOP:
case BRANCH:
{
int alt = std::max(0, currentNode->getPathAlternative(*na));
VisualNode* n = currentNode->getChild(*na,alt);
setCurrentNode(n);
centerCurrentNode();
break;
}
case SOLVED:
case FAILED:
case UNDETERMINED:
break;
}
}
}
void
TreeCanvas::navLeft(void) {
QMutexLocker locker(&mutex);
VisualNode* p = currentNode->getParent(*na);
if (p != nullptr) {
int alt = currentNode->getAlternative(*na);
if (alt > 0) {
VisualNode* n = p->getChild(*na,alt-1);
setCurrentNode(n);
centerCurrentNode();
}
}
}
void
TreeCanvas::navRight(void) {
QMutexLocker locker(&mutex);
VisualNode* p = currentNode->getParent(*na);
if (p != nullptr) {
unsigned int alt = currentNode->getAlternative(*na);
if (alt + 1 < p->getNumberOfChildren()) {
VisualNode* n = p->getChild(*na,alt+1);
setCurrentNode(n);
centerCurrentNode();
}
}
}
void
TreeCanvas::navRoot(void) {
QMutexLocker locker(&mutex);
setCurrentNode(root);
centerCurrentNode();
}
void
TreeCanvas::navNextSol(bool back) {
QMutexLocker locker(&mutex);
NextSolCursor nsc(currentNode,back,*na);
PreorderNodeVisitor<NextSolCursor> nsv(nsc);
nsv.run();
VisualNode* n = nsv.getCursor().node();
if (n != root) {
setCurrentNode(n);
centerCurrentNode();
}
}
void
TreeCanvas::navPrevSol(void) {
navNextSol(true);
}
void
TreeCanvas::exportNodePDF(VisualNode* n) {
#if QT_VERSION >= 0x040400
QString filename = QFileDialog::getSaveFileName(this, tr("Export tree as pdf"), "", tr("PDF (*.pdf)"));
if (filename != "") {
QPrinter printer(QPrinter::ScreenResolution);
QMutexLocker locker(&mutex);
BoundingBox bb = n->getBoundingBox();
printer.setFullPage(true);
printer.setPaperSize(QSizeF(bb.right-bb.left+Layout::extent,
n->getShape()->depth() * Layout::dist_y +
Layout::extent), QPrinter::Point);
printer.setOutputFileName(filename);
QPainter painter(&printer);
painter.setRenderHint(QPainter::Antialiasing);
QRect pageRect = printer.pageRect();
double newXScale =
static_cast<double>(pageRect.width()) / (bb.right - bb.left +
Layout::extent);
double newYScale =
static_cast<double>(pageRect.height()) /
(n->getShape()->depth() * Layout::dist_y +
Layout::extent);
double printScale = std::min(newXScale, newYScale);
painter.scale(printScale,printScale);
int printxtrans = -bb.left+(Layout::extent / 2);
painter.translate(printxtrans, Layout::dist_y / 2);
QRect clip(0,0,0,0);
DrawingCursor dc(n, *na, curBest, painter, clip, showCopies);
currentNode->setMarked(false);
PreorderNodeVisitor<DrawingCursor>(dc).run();
currentNode->setMarked(true);
}
#else
(void) n;
#endif
}
void
TreeCanvas::exportWholeTreePDF(void) {
#if QT_VERSION >= 0x040400
exportNodePDF(root);
#endif
}
void
TreeCanvas::exportPDF(void) {
#if QT_VERSION >= 0x040400
exportNodePDF(currentNode);
#endif
}
void
TreeCanvas::print(void) {
QPrinter printer;
if (QPrintDialog(&printer, this).exec() == QDialog::Accepted) {
QMutexLocker locker(&mutex);
BoundingBox bb = root->getBoundingBox();
QRect pageRect = printer.pageRect();
double newXScale =
static_cast<double>(pageRect.width()) / (bb.right - bb.left +
Layout::extent);
double newYScale =
static_cast<double>(pageRect.height()) /
(root->getShape()->depth() * Layout::dist_y +
2*Layout::extent);
double printScale = std::min(newXScale, newYScale)*100;
if (printScale<1.0)
printScale = 1.0;
if (printScale > 400.0)
printScale = 400.0;
printScale = printScale / 100.0;
QPainter painter(&printer);
painter.setRenderHint(QPainter::Antialiasing);
painter.scale(printScale,printScale);
painter.translate(xtrans, 0);
QRect clip(0,0,0,0);
DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
PreorderNodeVisitor<DrawingCursor>(dc).run();
}
}
VisualNode*
TreeCanvas::eventNode(QEvent* event) {
int x = 0;
int y = 0;
switch (event->type()) {
case QEvent::ToolTip:
{
QHelpEvent* he = static_cast<QHelpEvent*>(event);
x = he->x();
y = he->y();
break;
}
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
{
QMouseEvent* me = static_cast<QMouseEvent*>(event);
x = me->x();
y = me->y();
break;
}
case QEvent::ContextMenu:
{
QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
x = ce->x();
y = ce->y();
break;
}
default:
return nullptr;
}
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
int xoff = sa->horizontalScrollBar()->value()/scale;
int yoff = sa->verticalScrollBar()->value()/scale;
BoundingBox bb = root->getBoundingBox();
int w =
static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
if (w < sa->viewport()->width())
xoff -= (sa->viewport()->width()-w)/2;
VisualNode* n;
n = root->findNode(*na,
static_cast<int>(x/scale-xtrans+xoff),
static_cast<int>((y-30)/scale+yoff));
return n;
}
bool
TreeCanvas::event(QEvent* event) {
if (mutex.tryLock()) {
if (event->type() == QEvent::ToolTip) {
VisualNode* n = eventNode(event);
if (n != nullptr) {
QHelpEvent* he = static_cast<QHelpEvent*>(event);
QToolTip::showText(he->globalPos(),
QString(n->toolTip(*na,curBest,
c_d,a_d).c_str()));
} else {
QToolTip::hideText();
}
}
mutex.unlock();
}
return QWidget::event(event);
}
void
TreeCanvas::resizeToOuter(void) {
if (autoZoom)
zoomToFit();
}
void
TreeCanvas::paintEvent(QPaintEvent* event) {
QMutexLocker locker(&layoutMutex);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
int xoff = sa->horizontalScrollBar()->value()/scale;
int yoff = sa->verticalScrollBar()->value()/scale;
BoundingBox bb = root->getBoundingBox();
int w =
static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
if (w < sa->viewport()->width())
xoff -= (sa->viewport()->width()-w)/2;
QRect origClip = event->rect();
painter.translate(0, 30);
painter.scale(scale,scale);
painter.translate(xtrans-xoff, -yoff);
QRect clip(static_cast<int>(origClip.x()/scale-xtrans+xoff),
static_cast<int>(origClip.y()/scale+yoff),
static_cast<int>(origClip.width()/scale),
static_cast<int>(origClip.height()/scale));
DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
PreorderNodeVisitor<DrawingCursor>(dc).run();
// int nodesLayouted = 1;
// clock_t t0 = clock();
// while (v.next()) { nodesLayouted++; }
// double t = (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC) * 1000.0;
// double nps = static_cast<double>(nodesLayouted) /
// (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC);
// std::cout << "Drawing done. " << nodesLayouted << " nodes in "
// << t << " ms. " << nps << " nodes/s." << std::endl;
}
void
TreeCanvas::mouseDoubleClickEvent(QMouseEvent* event) {
if (mutex.tryLock()) {
if(event->button() == Qt::LeftButton) {
VisualNode* n = eventNode(event);
if(n == currentNode) {
inspectCurrentNode();
event->accept();
mutex.unlock();
return;
}
}
mutex.unlock();
}
event->ignore();
}
void
TreeCanvas::contextMenuEvent(QContextMenuEvent* event) {
if (mutex.tryLock()) {
VisualNode* n = eventNode(event);
if (n != nullptr) {
setCurrentNode(n);
emit contextMenu(event);
event->accept();
mutex.unlock();
return;
}
mutex.unlock();
}
event->ignore();
}
void
TreeCanvas::resizeEvent(QResizeEvent* e) {
QAbstractScrollArea* sa =
static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
int w = sa->horizontalScrollBar()->maximum()+e->oldSize().width();
int h = sa->verticalScrollBar()->maximum()+e->oldSize().height();
sa->horizontalScrollBar()->setRange(0,w-e->size().width());
sa->verticalScrollBar()->setRange(0,h-e->size().height());
sa->horizontalScrollBar()->setPageStep(e->size().width());
sa->verticalScrollBar()->setPageStep(e->size().height());
}
void
TreeCanvas::wheelEvent(QWheelEvent* event) {
if (event->modifiers() & Qt::ShiftModifier) {
event->accept();
if (event->orientation() == Qt::Vertical && !autoZoom)
scaleTree(scale*100+ceil(static_cast<double>(event->delta())/4.0),
event->x(), event->y());
} else {
event->ignore();
}
}
bool
TreeCanvas::finish(void) {
if (finishedFlag)
return true;
stopSearchFlag = true;
finishedFlag = true;
for (int i=0; i<doubleClickInspectors.size(); i++)
doubleClickInspectors[i].first->finalize();
for (int i=0; i<solutionInspectors.size(); i++)
solutionInspectors[i].first->finalize();
for (int i=0; i<moveInspectors.size(); i++)
moveInspectors[i].first->finalize();
for (int i=0; i<comparators.size(); i++)
comparators[i].first->finalize();
return !searcher.isRunning();
}
void
TreeCanvas::setCurrentNode(VisualNode* n, bool finished, bool update) {
if (finished)
mutex.lock();
if (update && n != nullptr && n != currentNode &&
n->getStatus() != UNDETERMINED && !n->isHidden()) {
Space* curSpace = nullptr;
for (int i=0; i<moveInspectors.size(); i++) {
if (moveInspectors[i].second) {
if (curSpace == nullptr)
curSpace = n->getSpace(*na,curBest,c_d,a_d);
try {
moveInspectors[i].first->inspect(*curSpace);
} catch (Exception& e) {
qFatal("Exception in move inspector %d: %s.\n Stopping.",
i, e.what());
}
}
}
}
if (n != nullptr) {
currentNode->setMarked(false);
currentNode = n;
currentNode->setMarked(true);
emit statusChanged(currentNode,stats,finished);
if (update) {
compareNodes = false;
setCursor(QCursor(Qt::ArrowCursor));
QWidget::update();
}
}
if (finished)
mutex.unlock();
}
void
TreeCanvas::mousePressEvent(QMouseEvent* event) {
if (mutex.tryLock()) {
if (event->button() == Qt::LeftButton) {
VisualNode* n = eventNode(event);
if (compareNodes) {
if (n != nullptr && n->getStatus() != UNDETERMINED &&
currentNode != nullptr &&
currentNode->getStatus() != UNDETERMINED) {
Space* curSpace = nullptr;
Space* compareSpace = nullptr;
for (int i=0; i<comparators.size(); i++) {
if (comparators[i].second) {
if (curSpace == nullptr) {
curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
if (!compareNodesBeforeFP || n->isRoot()) {
compareSpace = n->getSpace(*na,curBest,c_d,a_d);
} else {
VisualNode* p = n->getParent(*na);
compareSpace = p->getSpace(*na,curBest,c_d,a_d);
switch (compareSpace->status()) {
case SS_SOLVED:
case SS_FAILED:
break;
case SS_BRANCH:
compareSpace->commit(*p->getChoice(),
n->getAlternative(*na));
break;
default:
GECODE_NEVER;
}
}
}
try {
comparators[i].first->compare(*curSpace,*compareSpace);
} catch (Exception& e) {
qFatal("Exception in comparator %d: %s.\n Stopping.",
i, e.what());
}
}
}
}
} else {
setCurrentNode(n);
}
compareNodes = false;
setCursor(QCursor(Qt::ArrowCursor));
if (n != nullptr) {
event->accept();
mutex.unlock();
return;
}
}
mutex.unlock();
}
event->ignore();
}
void
TreeCanvas::setRecompDistances(int c_d0, int a_d0) {
c_d = c_d0; a_d = a_d0;
}
void
TreeCanvas::setAutoHideFailed(bool b) {
autoHideFailed = b;
}
void
TreeCanvas::setAutoZoom(bool b) {
autoZoom = b;
if (autoZoom) {
zoomToFit();
}
emit autoZoomChanged(b);
scaleBar->setEnabled(!b);
}
void
TreeCanvas::setShowCopies(bool b) {
showCopies = b;
}
bool
TreeCanvas::getShowCopies(void) {
return showCopies;
}
bool
TreeCanvas::getAutoHideFailed(void) {
return autoHideFailed;
}
bool
TreeCanvas::getAutoZoom(void) {
return autoZoom;
}
void
TreeCanvas::setRefresh(int i) {
refresh = i;
}
void
TreeCanvas::setRefreshPause(int i) {
refreshPause = i;
if (refreshPause > 0)
refresh = 1;
}
bool
TreeCanvas::getSmoothScrollAndZoom(void) {
return smoothScrollAndZoom;
}
void
TreeCanvas::setSmoothScrollAndZoom(bool b) {
smoothScrollAndZoom = b;
}
bool
TreeCanvas::getMoveDuringSearch(void) {
return moveDuringSearch;
}
void
TreeCanvas::setMoveDuringSearch(bool b) {
moveDuringSearch = b;
}
}}
// STATISTICS: gist-any