/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Guido Tack * * 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 #include #include #include namespace Gecode { namespace Gist { Shape* Shape::leaf; Shape* Shape::hidden; /// Allocate shapes statically class ShapeAllocator { public: /// Constructor ShapeAllocator(void) { Shape::leaf = Shape::allocate(1); (*Shape::leaf)[0] = Extent(Layout::extent); Shape::leaf->computeBoundingBox(); Shape::hidden = Shape::allocate(2); (*Shape::hidden)[0] = Extent(Layout::extent); (*Shape::hidden)[1] = Extent(Layout::extent); Shape::hidden->computeBoundingBox(); } ~ShapeAllocator(void) { Shape::deallocate(Shape::leaf); Shape::deallocate(Shape::hidden); } }; /// Allocate shapes statically ShapeAllocator shapeAllocator; VisualNode::VisualNode(int p) : SpaceNode(p) , offset(0) { shape = nullptr; setDirty(true); setChildrenLayoutDone(false); setHidden(false); setMarked(false); setOnPath(false); setBookmarked(false); } VisualNode::VisualNode(Space* root) : SpaceNode(root) , offset(0) { shape = nullptr; setDirty(true); setChildrenLayoutDone(false); setHidden(false); setMarked(false); setOnPath(false); setBookmarked(false); } void VisualNode::dispose(void) { Shape::deallocate(shape); SpaceNode::dispose(); } void VisualNode::dirtyUp(const NodeAllocator& na) { VisualNode* cur = this; while (!cur->isDirty()) { cur->setDirty(true); if (! cur->isRoot()) { cur = cur->getParent(na); } } } void VisualNode::layout(const NodeAllocator& na) { LayoutCursor l(this,na); PostorderNodeVisitor(l).run(); // int nodesLayouted = 1; // clock_t t0 = clock(); // while (p.next()) {} // while (p.next()) { nodesLayouted++; } // double t = (static_cast(clock()-t0) / CLOCKS_PER_SEC) * 1000.0; // double nps = static_cast(nodesLayouted) / // (static_cast(clock()-t0) / CLOCKS_PER_SEC); // std::cout << "Layout done. " << nodesLayouted << " nodes in " // << t << " ms. " << nps << " nodes/s." << std::endl; } void VisualNode::pathUp(const NodeAllocator& na) { VisualNode* cur = this; while (cur) { cur->setOnPath(true); cur = cur->getParent(na); } } void VisualNode::unPathUp(const NodeAllocator& na) { VisualNode* cur = this; while (!cur->isRoot()) { cur->setOnPath(false); cur = cur->getParent(na); } } int VisualNode::getPathAlternative(const NodeAllocator& na) { for (int i=getNumberOfChildren(); i--;) { if (getChild(na,i)->isOnPath()) return i; } return -1; } void VisualNode::toggleHidden(const NodeAllocator& na) { setHidden(!isHidden()); dirtyUp(na); } void VisualNode::hideFailed(const NodeAllocator& na, bool onlyDirty) { HideFailedCursor c(this,na,onlyDirty); PreorderNodeVisitor(c).run(); dirtyUp(na); } void VisualNode::labelBranches(NodeAllocator& na, BestNode* curBest, int c_d, int a_d) { bool clear = na.hasLabel(this); BranchLabelCursor c(this,curBest,c_d,a_d,clear,na); PreorderNodeVisitor(c).run(); dirtyUp(na); } void VisualNode::labelPath(NodeAllocator& na, BestNode* curBest, int c_d, int a_d) { if (na.hasLabel(this)) { // clear labels on path to root VisualNode* p = this; while (p) { na.clearLabel(p); p = p->getParent(na); } } else { // set labels on path to root std::vector > path; VisualNode* p = this; while (p) { path.push_back(std::pair(p,p->getAlternative(na))); p = p->getParent(na); } while (!path.empty()) { std::pair cur = path.back(); path.pop_back(); if (p) { std::string l = cur.first->getBranchLabel(na,p,p->getChoice(), curBest,c_d,a_d,cur.second); na.setLabel(cur.first,QString(l.c_str())); } p = cur.first; } } dirtyUp(na); } void VisualNode::unhideAll(const NodeAllocator& na) { UnhideAllCursor c(this,na); PreorderNodeVisitor(c).run(); dirtyUp(na); } void VisualNode::toggleStop(const NodeAllocator& na) { if (getStatus() == STOP) setStatus(UNSTOP); else if (getStatus() == UNSTOP) setStatus(STOP); dirtyUp(na); } void VisualNode::unstopAll(const NodeAllocator& na) { UnstopAllCursor c(this,na); PreorderNodeVisitor(c).run(); dirtyUp(na); } void VisualNode::changedStatus(const NodeAllocator& na) { dirtyUp(na); } bool VisualNode::containsCoordinateAtDepth(int x, int depth) { BoundingBox box = getShape()->getBoundingBox(); if (x < box.left || x > box.right || depth >= getShape()->depth()) { return false; } Extent theExtent; if (getShape()->getExtentAtDepth(depth, theExtent)) { return (theExtent.l <= x && x <= theExtent.r); } else { return false; } } VisualNode* VisualNode::findNode(const NodeAllocator& na, int x, int y) { VisualNode* cur = this; int depth = y / Layout::dist_y; while (depth > 0 && cur != nullptr) { if (cur->isHidden()) { break; } VisualNode* oldCur = cur; cur = nullptr; for (unsigned int i=0; igetNumberOfChildren(); i++) { VisualNode* nextChild = oldCur->getChild(na,i); int newX = x - nextChild->getOffset(); if (nextChild->containsCoordinateAtDepth(newX, depth - 1)) { cur = nextChild; x = newX; break; } } depth--; y -= Layout::dist_y; } if(cur == this && !cur->containsCoordinateAtDepth(x, 0)) { return nullptr; } return cur; } std::string VisualNode::toolTip(NodeAllocator&, BestNode*, int, int) { return ""; } std::string VisualNode::getBranchLabel(NodeAllocator& na, VisualNode* p, const Choice* c, BestNode* curBest, int c_d, int a_d, int alt) { std::ostringstream oss; p->acquireSpace(na,curBest,c_d,a_d); p->getWorkingSpace()->print(*c,alt,oss); return oss.str(); } /// \brief Helper functions for the layout algorithm class Layouter { public: /// Compute distance needed between \a shape1 and \a shape2 template static int getAlpha(const S1& shape1, int depth1, const S2& shape2, int depth2); /// Merge \a shape1 and \a shape2 into \a result with distance \a alpha template static void merge(Extent* result, const S1& shape1, int depth1, const S2& shape2, int depth2, int alpha); }; template int Layouter::getAlpha(const S1& shape1, int depth1, const S2& shape2, int depth2) { int alpha = Layout::minimalSeparation; int extentR = 0; int extentL = 0; for (int i=0; i void Layouter::merge(Extent* result, const S1& shape1, int depth1, const S2& shape2, int depth2, int alpha) { if (depth1 == 0) { for (int i=depth2; i--;) result[i] = shape2[i]; } else if (depth2 == 0) { for (int i=depth1; i--;) result[i] = shape1[i]; } else { // Extend the topmost right extent by alpha. This, in effect, // moves the second shape to the right by alpha units. int topmostL = shape1[0].l; int topmostR = shape2[0].r; int backoffTo1 = shape1[0].r - alpha - shape2[0].r; int backoffTo2 = shape2[0].l + alpha - shape1[0].l; result[0] = Extent(topmostL, topmostR+alpha); // Now, since extents are given in relative units, in order to // compute the extents of the merged shape, we can just collect the // extents of shape1 and shape2, until one of the shapes ends. If // this happens, we need to "back-off" to the axis of the deeper // shape in order to properly determine the remaining extents. int i=1; for (; icomputeBoundingBox(); } void VisualNode::computeShape(const NodeAllocator& na) { int numberOfShapes = getNumberOfChildren(); Extent extent; if (na.hasLabel(this)) { int ll = na.getLabel(this).length(); ll *= 7; VisualNode* p = getParent(na); int alt = 0; int n_alt = 1; if (p) { alt = getAlternative(na); n_alt = p->getNumberOfChildren(); } extent = Extent(Layout::extent); if (alt==0 && n_alt > 1) { extent.l = std::min(extent.l, -ll); } else if (alt==n_alt-1 && n_alt > 1) { extent.r = std::max(extent.r, ll); } else { extent.l = std::min(extent.l, -ll); extent.r = std::max(extent.r, ll); } } else { if (numberOfShapes==0) { setShape(Shape::leaf); return; } else { extent = Extent(Layout::extent); } } int maxDepth = 0; for (int i = numberOfShapes; i--;) maxDepth = std::max(maxDepth, getChild(na,i)->getShape()->depth()); Shape* mergedShape; if (getShape() && getShape() != Shape::leaf && getShape()->depth() >= maxDepth+1) { mergedShape = getShape(); mergedShape->setDepth(maxDepth+1); } else { mergedShape = Shape::allocate(maxDepth+1); } (*mergedShape)[0] = extent; if (numberOfShapes < 1) { setShape(mergedShape); } else if (numberOfShapes == 1) { getChild(na,0)->setOffset(0); const Shape* childShape = getChild(na,0)->getShape(); for (int i=childShape->depth(); i--;) (*mergedShape)[i+1] = (*childShape)[i]; (*mergedShape)[1].extend(- extent.l, - extent.r); setShape(mergedShape); } else { // alpha stores the necessary distances between the // axes of the shapes in the list: alpha[i].first gives the distance // between shape[i] and shape[i-1], when shape[i-1] and shape[i] // are merged left-to-right; alpha[i].second gives the distance between // shape[i] and shape[i+1], when shape[i] and shape[i+1] are merged // right-to-left. Region r; std::pair* alpha = r.alloc >(numberOfShapes); // distance between the leftmost and the rightmost axis in the list int width = 0; Extent* currentShapeL = r.alloc(maxDepth); int ldepth = getChild(na,0)->getShape()->depth(); for (int i=ldepth; i--;) currentShapeL[i] = (*getChild(na,0)->getShape())[i]; // After merging, we can pick the result of either merging left or right // Here we chose the result of merging right Shape* rShape = getChild(na,numberOfShapes-1)->getShape(); int rdepth = rShape->depth(); for (int i=rdepth; i--;) (*mergedShape)[i+1] = (*rShape)[i]; Extent* currentShapeR = &(*mergedShape)[1]; for (int i = 1; i < numberOfShapes; i++) { // Merge left-to-right. Note that due to the asymmetry of the // merge operation, nextAlphaL is the distance between the // *leftmost* axis in the shape list, and the axis of // nextShapeL; what we are really interested in is the distance // between the *previous* axis and the axis of nextShapeL. // This explains the correction. Shape* nextShapeL = getChild(na,i)->getShape(); int nextAlphaL = Layouter::getAlpha(¤tShapeL[0], ldepth, *nextShapeL, nextShapeL->depth()); Layouter::merge(¤tShapeL[0], ¤tShapeL[0], ldepth, *nextShapeL, nextShapeL->depth(), nextAlphaL); ldepth = std::max(ldepth,nextShapeL->depth()); alpha[i].first = nextAlphaL - width; width = nextAlphaL; // Merge right-to-left. Here, a correction of nextAlphaR is // not required. Shape* nextShapeR = getChild(na,numberOfShapes-1-i)->getShape(); int nextAlphaR = Layouter::getAlpha(*nextShapeR, nextShapeR->depth(), ¤tShapeR[0], rdepth); Layouter::merge(¤tShapeR[0], *nextShapeR, nextShapeR->depth(), ¤tShapeR[0], rdepth, nextAlphaR); rdepth = std::max(rdepth,nextShapeR->depth()); alpha[numberOfShapes - i].second = nextAlphaR; } // The merged shape has to be adjusted to its topmost extent (*mergedShape)[1].extend(- extent.l, - extent.r); // After the loop, the merged shape has the same axis as the // leftmost shape in the list. What we want is to move the axis // such that it is the center of the axis of the leftmost shape in // the list and the axis of the rightmost shape. int halfWidth = false ? 0 : width / 2; (*mergedShape)[1].move(- halfWidth); // Finally, for the offset lists. Now that the axis of the merged // shape is at the center of the two extreme axes, the first shape // needs to be offset by -halfWidth units with respect to the new // axis. As for the offsets for the other shapes, we take the // median of the alphaL and alphaR values, as suggested in // Kennedy's paper. int offset = - halfWidth; getChild(na,0)->setOffset(offset); for (int i = 1; i < numberOfShapes; i++) { offset += (alpha[i].first + alpha[i].second) / 2; getChild(na,i)->setOffset(offset); } setShape(mergedShape); } } }} // STATISTICS: gist-any