bool IcicleTreeCanvas::compressInit(VisualNode& root, int idx, int absX) { const int kids = root.getNumberOfChildren(); auto& na = node_tree.getNA(); bool hasSolved = false; int leafCnt = 0, expectSolvedCnt = 0, actualSolvedCnt = 0; statistic[idx].ns = root.getStatus(); statistic[idx].absX = absX; for (int i = 0; i < kids; i++) { int kidIdx = root.getChild(i); VisualNode& kid = *na[kidIdx]; if (kid.hasSolvedChildren()) expectSolvedCnt++; if (statistic[kidIdx].height >= compressLevel) { bool kidRes = compressInit(kid, kidIdx, absX + leafCnt); hasSolved |= kidRes; leafCnt += statistic[kidIdx].leafCnt; if (kidRes) actualSolvedCnt++; } } statistic[idx].leafCnt = leafCnt? leafCnt: 1; if (kids && statistic[idx].height == compressLevel) statistic[idx].ns = root.hasSolvedChildren()? SOLVED: FAILED; else if (expectSolvedCnt > actualSolvedCnt) statistic[idx].ns = SOLVED; return hasSolved | (statistic[idx].ns == SOLVED); }
void IcicleTreeCanvas::dfsVisible(VisualNode& root, int idx, int curx, int cury, const int xoff, const int width, const int yoff, const int depth) { if (cury > depth) return; const int kids = root.getNumberOfChildren(); auto& na = node_tree.getNA(); int nextxL = curx; int nextxR; for (int i = 0; i < kids; i++) { if (nextxL > xoff + width) break; int kidIdx = root.getChild(i); if (statistic[kidIdx].height >= compressLevel) { VisualNode& kid = *na[kidIdx]; nextxR = nextxL + statistic[kidIdx].leafCnt; if (nextxR >= xoff) dfsVisible(kid, kidIdx, nextxL, cury+1, xoff, width, yoff, depth); nextxL = nextxR; } } if (cury >= yoff && cury <= yoff + depth) { int rectAbsXL = std::max(curx, xoff); int rectAbsXR = std::min(curx + statistic[idx].leafCnt, xoff + width); int rectAbsY = cury; int height = icicle_image_.pixel_height(); int x = rectAbsXL - xoff; int y = rectAbsY - yoff; int width = rectAbsXR - rectAbsXL; icicle_rects_.push_back(IcicleRect{x, y, width, height, root}); } }
void copyTree(VisualNode* node_t, NodeTree& tree_t, const VisualNode* node_s, const NodeTree& tree_s) { auto& na_t = tree_t.getNA(); const auto& na_s = tree_s.getNA(); stack<VisualNode*> stk_t; stack<const VisualNode*> stk_s; stk_s.push(node_s); stk_t.push(node_t); while (stk_s.size() > 0) { const VisualNode* n = stk_s.top(); stk_s.pop(); VisualNode* next = stk_t.top(); stk_t.pop(); auto kids = n->getNumberOfChildren(); next->setNumberOfChildren(kids, na_t); next->setStatus(n->getStatus()); next->dirtyUp(na_t); for (auto i = 0u; i < kids; ++i) { stk_s.push(n->getChild(na_s, i)); stk_t.push(next->getChild(na_t, i)); } } }
forceinline bool HideFailedCursor::mayMoveDownwards(void) { VisualNode* n = node(); return (!onlyDirty || n->isDirty()) && NodeCursor<VisualNode>::mayMoveDownwards() && (n->hasSolvedChildren() || n->getNoOfOpenChildren(na) > 0) && (! n->isHidden()); }
forceinline void UnhideAllCursor::processCurrentNode(void) { VisualNode* n = node(); if (n->isHidden()) { n->setHidden(false); n->dirtyUp(na); } }
forceinline void UnstopAllCursor::processCurrentNode(void) { VisualNode* n = node(); if (n->getStatus() == STOP) { n->setStop(false); n->dirtyUp(na); } }
forceinline void StatCursor::processCurrentNode(void) { VisualNode* n = node(); switch (n->getStatus()) { case SOLVED: solved++; break; case FAILED: failed++; break; case BRANCH: choice++; break; case UNDETERMINED: open++; break; default: break; } }
void SearcherThread::updateCanvas(void) { t->layoutMutex.lock(); if (t->root == NULL) return; if (t->autoHideFailed) { t->root->hideFailed(*t->na,true); } for (VisualNode* n = t->currentNode; n != NULL; 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); }
void SearcherThread::search(VisualNode* n, bool all, TreeCanvas* ti) { node = n; depth = -1; for (VisualNode* p = n; p != NULL; p = p->getParent(*ti->na)) depth++; a = all; t = ti; start(); }
void TreeCanvas::mousePressEvent(QMouseEvent* event) { if (mutex.tryLock()) { if (event->button() == Qt::LeftButton) { VisualNode* n = eventNode(event); if (compareNodes) { if (n != NULL && n->getStatus() != UNDETERMINED && currentNode != NULL && currentNode->getStatus() != UNDETERMINED) { Space* curSpace = NULL; Space* compareSpace = NULL; for (int i=0; i<comparators.size(); i++) { if (comparators[i].second) { if (curSpace == NULL) { 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 != NULL) { event->accept(); mutex.unlock(); return; } } mutex.unlock(); } event->ignore(); }
void TreeCanvas::navRight(void) { QMutexLocker locker(&mutex); VisualNode* p = currentNode->getParent(*na); if (p != NULL) { unsigned int alt = currentNode->getAlternative(*na); if (alt + 1 < p->getNumberOfChildren()) { VisualNode* n = p->getChild(*na,alt+1); setCurrentNode(n); centerCurrentNode(); } } }
void TreeCanvas::navLeft(void) { QMutexLocker locker(&mutex); VisualNode* p = currentNode->getParent(*na); if (p != NULL) { int alt = currentNode->getAlternative(*na); if (alt > 0) { VisualNode* n = p->getChild(*na,alt-1); setCurrentNode(n); centerCurrentNode(); } } }
IcicleNodeStatistic IcicleTreeCanvas::initTreeStatistic(VisualNode& root, int idx, int absX) { const int kids = root.getNumberOfChildren(); auto& na = node_tree.getNA(); IcicleNodeStatistic cntRoot = IcicleNodeStatistic{kids?0: 1, 0, absX, root.getStatus()}; for (int i=0; i < kids; i++) { VisualNode& kid = *root.getChild(na, i); int kidIdx = root.getChild(i); IcicleNodeStatistic cntKid = initTreeStatistic(kid, kidIdx, absX + cntRoot.leafCnt); cntRoot.leafCnt += cntKid.leafCnt; cntRoot.height = std::max(cntRoot.height, cntKid.height + 1); } statistic[idx] = cntRoot; return cntRoot; }
void IcicleTreeCanvas::compressLevelChanged(int value) { auto& na = node_tree.getNA(); int leafIndex; compressLevel = value; compressInit(*na[0], 0, 0); VisualNode* lftLeaf = findLeftLeaf(); leafIndex = lftLeaf->getIndex(na); int xoff = statistic[lftLeaf->getIndex(na)].absX * icicle_image_.pixel_height(); sa_.horizontalScrollBar()->setValue(xoff); redrawAll(); leafIndex = findLeftLeaf()->getIndex(na); sa_.horizontalScrollBar()->setValue(xoff); qDebug() << "compressionChanged to: " << compressLevel; }
QRgb IcicleTreeCanvas::getColorByType(const VisualNode& node) { if (selectedNode == &node) { return QColor::fromHsv(0, 150, 150).rgba();} QRgb color; auto& na = node_tree.getNA(); auto& data = tc_.getExecution()->getData(); auto gid = node.getIndex(na); auto* entry = data.getEntry(gid); auto domain_red = entry == nullptr ? 0 : entry->domain; domain_red_sum += domain_red; switch (IcicleTreeCanvas::color_mapping_type) { case ColorMappingType::DEFAULT: { NodeStatus ns = statistic[gid].ns; color = getColor(ns); } break; case ColorMappingType::DOMAIN_REDUCTION: { /// the smaller the value, the darker the color int color_value = 255 * static_cast<float>(domain_red); if (color_value < 0) color_value = 0; color = QColor::fromHsv(0, 0, color_value).rgba(); } break; case ColorMappingType::NODE_TIME: { auto node_time = entry == nullptr ? 0 : entry->node_time; /// TODO(maxim): need to normalize the node time int color_value = static_cast<float>(node_time); color = QColor::fromHsv(0, 0, color_value).rgba(); } } return color; }
void Gist::updateActions(VisualNode* n, bool finished) { // qDebug() << "!! updateActions triggered"; if (!finished) { navUp->setEnabled(false); navDown->setEnabled(false); navLeft->setEnabled(false); navRight->setEnabled(false); navRoot->setEnabled(false); unhideAll->setEnabled(false); } else { navRoot->setEnabled(true); if (n->getNumberOfChildren() > 0) { navDown->setEnabled(true); } else { navDown->setEnabled(false); } VisualNode* p = n->getParent(execution->getNA()); if (p == nullptr) { navUp->setEnabled(false); navRight->setEnabled(false); navLeft->setEnabled(false); } else { navUp->setEnabled(true); unsigned int alt = n->getAlternative(execution->getNA()); navRight->setEnabled(alt + 1 < p->getNumberOfChildren()); navLeft->setEnabled(alt > 0); } if (n->getNumberOfChildren() > 0) { unhideAll->setEnabled(true); } else { unhideAll->setEnabled(false); } } }
bool TreeCanvas::event(QEvent* event) { if (mutex.tryLock()) { if (event->type() == QEvent::ToolTip) { VisualNode* n = eventNode(event); if (n != NULL && !n->isHidden() && (n->getStatus() == BRANCH || n->getStatus() == STOP)) { QHelpEvent* he = static_cast<QHelpEvent*>(event); QToolTip::showText(he->globalPos(), QString(n->toolTip(curBest,c_d,a_d).c_str())); } else { QToolTip::hideText(); } } mutex.unlock(); } return QWidget::event(event); }
forceinline void LayoutCursor::processCurrentNode() { VisualNode* currentNode = node(); if (currentNode->isDirty()) { if (currentNode->isHidden()) { // do nothing } else if (currentNode->getNumberOfChildren() < 1) { currentNode->setShape(Shape::leaf); } else { currentNode->computeShape(na,startNode()); } currentNode->setDirty(false); } if (currentNode->getNumberOfChildren() >= 1) currentNode->setChildrenLayoutDone(true); }
void TreeCanvas::centerCurrentNode(void) { QMutexLocker locker(&mutex); int x=0; int y=0; VisualNode* c = currentNode; while (c != NULL) { 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(); } }
forceinline void HideFailedCursor::processCurrentNode(void) { VisualNode* n = node(); if (n->getStatus() == BRANCH && !n->hasSolvedChildren() && n->getNoOfOpenChildren(na) == 0) { n->setHidden(true); n->setChildrenLayoutDone(false); n->dirtyUp(na); } }
void NodeStatInspector::node(const VisualNode::NodeAllocator& na, VisualNode* n, const Statistics&, bool) { if (isVisible()) { int nd = -1; for (VisualNode* p = n; p != NULL; p = p->getParent(na)) nd++; nodeDepthLabel->setPlainText(QString("%1").arg(nd));; StatCursor sc(n,na); PreorderNodeVisitor<StatCursor> pnv(sc); pnv.run(); subtreeDepthLabel->setPlainText( QString("%1").arg(pnv.getCursor().depth)); solvedLabel->setPlainText(QString("%1").arg(pnv.getCursor().solved)); solvedLabel->setPos(78-solvedLabel->document()->size().width()/2,120); failedLabel->setPlainText(QString("%1").arg(pnv.getCursor().failed)); failedLabel->setPos(44-failedLabel->document()->size().width(),120); choicesLabel->setPlainText(QString("%1").arg(pnv.getCursor().choice)); choicesLabel->setPos(66-choicesLabel->document()->size().width(),57); openLabel->setPlainText(QString("%1").arg(pnv.getCursor().open)); } }
bool compareSubtrees(const NodeTree& nt, const VisualNode& root1, const VisualNode& root2) { // compare roots bool equal = compareNodes(root1, root2); if (!equal) return false; // if nodes have children, compare them recursively: for (auto i = 0u; i < root1.getNumberOfChildren(); ++i) { auto new_root_1 = nt.getChild(root1, i); auto new_root_2 = nt.getChild(root2, i); bool equal = compareSubtrees(nt, *new_root_1, *new_root_2); if (!equal) return false; } return true; }
void SearcherThread::run() { { 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 = NULL; 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 (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 != NULL) { t->setCurrentNode(sol,false); } else { t->setCurrentNode(node,false); } } updateCanvas(); emit statusChanged(true); if (t->finishedFlag) emit searchFinished(); }
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 != NULL; 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()); currentNode->purge(*na); } emit statusChanged(currentNode,stats,true); for (int i=0; i<moveInspectors.size(); i++) { if (moveInspectors[i].second) { failedInspectorType = 0; failedInspector = i; moveInspectors[i].first-> inspect(*currentNode->getWorkingSpace()); failedInspectorType = -1; } } } break; case FAILED: case STOP: case UNSTOP: case BRANCH: case SOLVED: { // SizeCursor sc(currentNode); // PreorderNodeVisitor<SizeCursor> pnv(sc); // int nodes = 1; // while (pnv.next()) { nodes++; } // std::cout << "sizeof(VisualNode): " << sizeof(VisualNode) // << std::endl; // std::cout << "Size: " << (pnv.getCursor().s)/1024 << std::endl; // std::cout << "Nodes: " << nodes << std::endl; // std::cout << "Size / node: " << (pnv.getCursor().s)/nodes // << std::endl; 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 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: std::cerr << "Exception in move inspector " << failedInspector; break; case 1: std::cerr << "Exception in double-click inspector " << failedInspector; break; default: std::cerr << "Exception "; break; } std::cerr << ": " << e.what() << "." << std::endl; std::cerr << "Stopping..." << std::endl; std::exit(EXIT_FAILURE); } currentNode->dirtyUp(*na); update(); if (needCentering) centerCurrentNode(); }
/// TODO void Gist::on_canvas_statusChanged(VisualNode* n, const Statistics& stats, bool finished) { nodeStatInspector->node(execution->getNA(),n,stats,finished); /// for single node stats if (!finished) { showNodeStats->setEnabled(false); // stop-> setEnabled(true); // reset->setEnabled(false); navNextSol->setEnabled(false); navPrevSol->setEnabled(false); // searchNext->setEnabled(false); // searchAll->setEnabled(false); toggleHidden->setEnabled(false); hideFailed->setEnabled(false); // hideSize->setEnabled(false); // labelBranches->setEnabled(false); // labelPath->setEnabled(false); // toggleStop->setEnabled(false); // unstopAll->setEnabled(false); center->setEnabled(false); /// ?? exportPDF->setEnabled(false); exportWholeTreePDF->setEnabled(false); print->setEnabled(false); // printSearchLog->setEnabled(false); bookmarkNode->setEnabled(false); bookmarksGroup->setEnabled(false); } else { // stop->setEnabled(false); // reset->setEnabled(true); if ( (n->isOpen() || n->hasOpenChildren()) && (!n->isHidden()) ) { // searchNext->setEnabled(true); // searchAll->setEnabled(true); } else { // searchNext->setEnabled(false); // searchAll->setEnabled(false); } if (n->getNumberOfChildren() > 0) { toggleHidden->setEnabled(true); hideFailed->setEnabled(true); // hideSize->setEnabled(true); // unstopAll->setEnabled(true); } else { toggleHidden->setEnabled(false); hideFailed->setEnabled(false); // hideSize->setEnabled(false); // unhideAll->setEnabled(false); // unstopAll->setEnabled(false); } toggleStop->setEnabled(n->getStatus() == STOP || n->getStatus() == UNSTOP); showNodeStats->setEnabled(true); labelPath->setEnabled(true); VisualNode* root = n; while (!root->isRoot()) { root = root->getParent(execution->getNA()); } NextSolCursor nsc(n, false, execution->getNA()); PreorderNodeVisitor<NextSolCursor> nsv(nsc); nsv.run(); navNextSol->setEnabled(nsv.getCursor().node() != root); NextSolCursor psc(n, true, execution->getNA()); PreorderNodeVisitor<NextSolCursor> psv(psc); psv.run(); navPrevSol->setEnabled(psv.getCursor().node() != root); center->setEnabled(true); exportPDF->setEnabled(true); exportWholeTreePDF->setEnabled(true); print->setEnabled(true); printSearchLog->setEnabled(true); bookmarkNode->setEnabled(true); bookmarksGroup->setEnabled(true); } emit statusChanged(stats,finished); }
void VisualSubtreeHighlighter::UpdateSubtreeExtents() { if(!m_node || !isVisible()) return; prepareGeometryChange(); QRectF bb = m_node->boundingRect(); float xMin = bb.left() + m_node->x(); float xMax = bb.right() + m_node->x(); float yMin = bb.top() + m_node->y(); float yMax = bb.bottom() + m_node->y(); QQueue<VisualNode*> queue; queue.enqueue(m_node); while(!queue.empty()) { VisualNode* node = queue.dequeue(); if(node->IsCollapsed() || node->IsLeaf()) { QRectF bb = node->boundingRect(); if(bb.left() + node->x() < xMin) xMin = bb.left() + node->x(); if(bb.right() + node->x() > xMax) xMax = bb.right() + node->x(); if(bb.top() + node->y() < yMin) yMin = bb.top() + node->y(); if(bb.bottom() + node->y() > yMax) yMax = bb.bottom() + node->y(); } else // not collapsed and not a leaf node { foreach(VisualNode* child, node->GetChildren()) queue.enqueue(child); } } m_extents = QRectF(xMin - HIGHLIGHTER_OFFSET, yMin - HIGHLIGHTER_OFFSET, (xMax-xMin) + 2*HIGHLIGHTER_OFFSET, (yMax-yMin) + 2*HIGHLIGHTER_OFFSET); update(); }