Qt 4.8
qgraphicsscenebsptreeindex.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
80 #include <QtCore/qglobal.h>
81 
82 #ifndef QT_NO_GRAPHICSVIEW
83 
84 #include <private/qgraphicsscene_p.h>
85 #include <private/qgraphicsscenebsptreeindex_p.h>
86 #include <private/qgraphicssceneindex_p.h>
87 
88 #include <QtCore/qmath.h>
89 #include <QtCore/qdebug.h>
90 
92 
93 static inline int intmaxlog(int n)
94 {
95  return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0);
96 }
97 
103  bspTreeDepth(0),
104  indexTimerId(0),
105  restartIndexTimer(false),
106  regenerateIndex(true),
107  lastItemCount(0),
108  purgePending(false),
109  sortCacheEnabled(false),
110  updatingSortCache(false)
111 {
112 }
113 
114 
124 {
126  if (!indexTimerId)
127  return;
128 
129  q->killTimer(indexTimerId);
130  indexTimerId = 0;
131 
133 
134  // Add unindexedItems to indexedItems
135  for (int i = 0; i < unindexedItems.size(); ++i) {
136  if (QGraphicsItem *item = unindexedItems.at(i)) {
137  Q_ASSERT(!item->d_ptr->itemDiscovered);
138  if (!freeItemIndexes.isEmpty()) {
139  int freeIndex = freeItemIndexes.takeFirst();
140  item->d_func()->index = freeIndex;
141  indexedItems[freeIndex] = item;
142  } else {
143  item->d_func()->index = indexedItems.size();
144  indexedItems << item;
145  }
146  }
147  }
148 
149  // Determine whether we should regenerate the BSP tree.
150  if (bspTreeDepth == 0) {
151  int oldDepth = intmaxlog(lastItemCount);
153  static const int slack = 100;
154  if (bsp.leafCount() == 0 || (oldDepth != bspTreeDepth && qAbs(lastItemCount - indexedItems.size()) > slack)) {
155  // ### Crude algorithm.
156  regenerateIndex = true;
157  }
158  }
159 
160  // Regenerate the tree.
161  if (regenerateIndex) {
162  regenerateIndex = false;
166  }
167 
168  // Insert all unindexed items into the tree.
169  for (int i = 0; i < unindexedItems.size(); ++i) {
170  if (QGraphicsItem *item = unindexedItems.at(i)) {
171  if (item->d_ptr->itemIsUntransformable()) {
172  untransformableItems << item;
173  continue;
174  }
175  if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
176  continue;
177 
178  bsp.insertItem(item, item->d_ptr->sceneEffectiveBoundingRect());
179  }
180  }
182 }
183 
184 
194 {
196  return;
197 
198  // Remove stale items from the BSP tree.
200  // Purge this list.
203  for (int i = 0; i < indexedItems.size(); ++i) {
204  if (!indexedItems.at(i))
205  freeItemIndexes << i;
206  }
207  purgePending = false;
208 }
209 
219 {
221  if (indexTimerId) {
222  restartIndexTimer = true;
223  } else {
224  indexTimerId = q->startTimer(interval);
225  }
226 }
227 
232 {
234  for (int i = 0; i < indexedItems.size(); ++i) {
235  if (QGraphicsItem *item = indexedItems.at(i)) {
236  item->d_ptr->index = -1;
237  Q_ASSERT(!item->d_ptr->itemDiscovered);
238  unindexedItems << item;
239  }
240  }
244  regenerateIndex = true;
245  startIndexTimer();
246 }
247 
252 {
253  if (!item->d_ptr->children.isEmpty()) {
254  QList<QGraphicsItem *> childList = item->d_ptr->children;
255  qSort(childList.begin(), childList.end(), qt_closestLeaf);
256  for (int i = 0; i < childList.size(); ++i) {
257  QGraphicsItem *item = childList.at(i);
259  climbTree(childList.at(i), stackingOrder);
260  }
261  item->d_ptr->globalStackingOrder = (*stackingOrder)++;
262  for (int i = 0; i < childList.size(); ++i) {
263  QGraphicsItem *item = childList.at(i);
265  climbTree(childList.at(i), stackingOrder);
266  }
267  } else {
268  item->d_ptr->globalStackingOrder = (*stackingOrder)++;
269  }
270 }
271 
276 {
278  _q_updateIndex();
279 
281  return;
282 
283  updatingSortCache = false;
284  int stackingOrder = 0;
285 
286  QList<QGraphicsItem *> topLevels;
287  const QList<QGraphicsItem *> items = q->items();
288  for (int i = 0; i < items.size(); ++i) {
289  QGraphicsItem *item = items.at(i);
290  if (item && !item->d_ptr->parent)
291  topLevels << item;
292  }
293 
294  qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf);
295  for (int i = 0; i < topLevels.size(); ++i)
296  climbTree(topLevels.at(i), &stackingOrder);
297 }
298 
303 {
306  return;
307 
308  updatingSortCache = true;
309  QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection);
310 }
311 
313 {
314  if (!item)
315  return;
316 
317  // Prevent reusing a recently deleted pointer: purge all removed item from our lists.
319 
320  // Invalidate any sort caching; arrival of a new item means we need to resort.
321  // Update the scene's sort cache settings.
322  item->d_ptr->globalStackingOrder = -1;
324 
325  // Indexing requires sceneBoundingRect(), but because \a item might
326  // not be completely constructed at this point, we need to store it in
327  // a temporary list and schedule an indexing for later.
328  if (item->d_ptr->index == -1) {
330  unindexedItems << item;
331  startIndexTimer(0);
332  } else {
334  qWarning("QGraphicsSceneBspTreeIndex::addItem: item has already been added to this BSP");
335  }
336 
337  if (recursive) {
338  for (int i = 0; i < item->d_ptr->children.size(); ++i)
339  addItem(item->d_ptr->children.at(i), recursive);
340  }
341 }
342 
344  bool moveToUnindexedItems)
345 {
346  if (!item)
347  return;
348 
349  if (item->d_ptr->index != -1) {
350  Q_ASSERT(item->d_ptr->index < indexedItems.size());
351  Q_ASSERT(indexedItems.at(item->d_ptr->index) == item);
352  Q_ASSERT(!item->d_ptr->itemDiscovered);
353  freeItemIndexes << item->d_ptr->index;
354  indexedItems[item->d_ptr->index] = 0;
355  item->d_ptr->index = -1;
356 
357  if (item->d_ptr->itemIsUntransformable()) {
359  } else if (item->d_ptr->inDestructor) {
360  // Avoid virtual function calls from the destructor.
361  purgePending = true;
362  removedItems << item;
365  }
366  } else {
368  }
369  invalidateSortCache(); // ### Only do this when removing from BSP?
370 
371  Q_ASSERT(item->d_ptr->index == -1);
375 
376  if (moveToUnindexedItems)
377  addItem(item);
378 
379  if (recursive) {
380  for (int i = 0; i < item->d_ptr->children.size(); ++i)
381  removeItem(item->d_ptr->children.at(i), recursive, moveToUnindexedItems);
382  }
383 }
384 
386  bool onlyTopLevelItems)
387 {
389  if (onlyTopLevelItems && rect.isNull())
390  return q->QGraphicsSceneIndex::estimateTopLevelItems(rect, order);
391 
395 
396  QList<QGraphicsItem *> rectItems = bsp.items(rect, onlyTopLevelItems);
397  if (onlyTopLevelItems) {
398  for (int i = 0; i < untransformableItems.size(); ++i) {
400  if (!item->d_ptr->parent) {
401  rectItems << item;
402  } else {
403  item = item->topLevelItem();
404  if (!rectItems.contains(item))
405  rectItems << item;
406  }
407  }
408  } else {
409  rectItems += untransformableItems;
410  }
411 
412  sortItems(&rectItems, order, sortCacheEnabled, onlyTopLevelItems);
413  return rectItems;
414 }
415 
422  bool sortCacheEnabled, bool onlyTopLevelItems)
423 {
424  if (order == Qt::SortOrder(-1))
425  return;
426 
427  if (onlyTopLevelItems) {
428  if (order == Qt::DescendingOrder)
429  qSort(itemList->begin(), itemList->end(), qt_closestLeaf);
430  else if (order == Qt::AscendingOrder)
431  qSort(itemList->begin(), itemList->end(), qt_notclosestLeaf);
432  return;
433  }
434 
435  if (sortCacheEnabled) {
436  if (order == Qt::DescendingOrder) {
437  qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache);
438  } else if (order == Qt::AscendingOrder) {
439  qSort(itemList->begin(), itemList->end(), closestItemLast_withCache);
440  }
441  } else {
442  if (order == Qt::DescendingOrder) {
443  qSort(itemList->begin(), itemList->end(), qt_closestItemFirst);
444  } else if (order == Qt::AscendingOrder) {
445  qSort(itemList->begin(), itemList->end(), qt_closestItemLast);
446  }
447  }
448 }
449 
455 {
456 
457 }
458 
460 {
462  for (int i = 0; i < d->indexedItems.size(); ++i) {
463  // Ensure item bits are reset properly.
464  if (QGraphicsItem *item = d->indexedItems.at(i)) {
465  Q_ASSERT(!item->d_ptr->itemDiscovered);
466  item->d_ptr->index = -1;
467  }
468  }
469 }
470 
479 {
481  d->bsp.clear();
482  d->lastItemCount = 0;
483  d->freeItemIndexes.clear();
484  for (int i = 0; i < d->indexedItems.size(); ++i) {
485  // Ensure item bits are reset properly.
486  if (QGraphicsItem *item = d->indexedItems.at(i)) {
487  Q_ASSERT(!item->d_ptr->itemDiscovered);
488  item->d_ptr->index = -1;
489  }
490  }
491  d->indexedItems.clear();
492  d->unindexedItems.clear();
493  d->untransformableItems.clear();
494  d->regenerateIndex = true;
495 }
496 
501 {
503  d->addItem(item);
504 }
505 
510 {
512  d->removeItem(item);
513 }
514 
523 {
524  if (!item)
525  return;
526 
527  if (item->d_ptr->index == -1 || item->d_ptr->itemIsUntransformable()
529  return; // Item is not in BSP tree; nothing to do.
530  }
531 
533  QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item);
534  d->removeItem(thatItem, /*recursive=*/false, /*moveToUnindexedItems=*/true);
535  for (int i = 0; i < item->d_ptr->children.size(); ++i) // ### Do we really need this?
537 }
538 
547 {
549  return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order);
550 }
551 
553 {
555  return const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->estimateItems(rect, order, /*onlyTopLevels=*/true);
556 }
557 
564 {
566  const_cast<QGraphicsSceneBspTreeIndexPrivate*>(d)->purgeRemovedItems();
567  QList<QGraphicsItem *> itemList;
568 
569  // If freeItemIndexes is empty, we know there are no holes in indexedItems and
570  // unindexedItems.
571  if (d->freeItemIndexes.isEmpty()) {
572  if (d->unindexedItems.isEmpty()) {
573  itemList = d->indexedItems;
574  } else {
575  itemList = d->indexedItems + d->unindexedItems;
576  }
577  } else {
578  // Rebuild the list of items to avoid holes. ### We could also just
579  // compress the item lists at this point.
580  foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) {
581  if (item)
582  itemList << item;
583  }
584  }
585  if (order != -1) {
586  //We sort descending order
587  d->sortItems(&itemList, order, d->sortCacheEnabled);
588  }
589  return itemList;
590 }
591 
623 {
625  return d->bspTreeDepth;
626 }
627 
629 {
631  if (d->bspTreeDepth == depth)
632  return;
633  d->bspTreeDepth = depth;
634  d->resetIndex();
635 }
636 
647 {
649  d->sceneRect = rect;
650  d->resetIndex();
651 }
652 
663 {
665  switch (change) {
667  // Handle ItemIgnoresTransformations
668  QGraphicsItem::GraphicsItemFlags newFlags = *static_cast<const QGraphicsItem::GraphicsItemFlags *>(value);
669  bool ignoredTransform = item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations;
670  bool willIgnoreTransform = newFlags & QGraphicsItem::ItemIgnoresTransformations;
671  bool clipsChildren = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape;
672  bool willClipChildren = newFlags & QGraphicsItem::ItemClipsChildrenToShape;
673  if ((ignoredTransform != willIgnoreTransform) || (clipsChildren != willClipChildren)) {
674  QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item);
675  // Remove item and its descendants from the index and append
676  // them to the list of unindexed items. Then, when the index
677  // is updated, they will be put into the bsp-tree or the list
678  // of untransformable items.
679  d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true);
680  }
681  break;
682  }
684  d->invalidateSortCache();
685  break;
687  d->invalidateSortCache();
688  // Handle ItemIgnoresTransformations
689  const QGraphicsItem *newParent = static_cast<const QGraphicsItem *>(value);
690  bool ignoredTransform = item->d_ptr->itemIsUntransformable();
691  bool willIgnoreTransform = (item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations)
692  || (newParent && newParent->d_ptr->itemIsUntransformable());
693  bool ancestorClippedChildren = item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren;
694  bool ancestorWillClipChildren = newParent
697  if ((ignoredTransform != willIgnoreTransform) || (ancestorClippedChildren != ancestorWillClipChildren)) {
698  QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item);
699  // Remove item and its descendants from the index and append
700  // them to the list of unindexed items. Then, when the index
701  // is updated, they will be put into the bsp-tree or the list
702  // of untransformable items.
703  d->removeItem(thatItem, /*recursive=*/true, /*moveToUnidexedItems=*/true);
704  }
705  break;
706  }
707  default:
708  break;
709  }
710 }
722 {
724  switch (event->type()) {
725  case QEvent::Timer:
726  if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) {
727  if (d->restartIndexTimer) {
728  d->restartIndexTimer = false;
729  } else {
730  // this call will kill the timer
731  d->_q_updateIndex();
732  }
733  }
734  // Fallthrough intended - support timers in subclasses.
735  default:
736  return QObject::event(event);
737  }
738  return true;
739 }
740 
742 
743 #include "moc_qgraphicsscenebsptreeindex_p.cpp"
744 
745 #endif // QT_NO_GRAPHICSVIEW
746 
double d
Definition: qnumeric_p.h:62
QList< QGraphicsItem * > estimateItems(const QRectF &, Qt::SortOrder, bool b=false)
The QGraphicsScene class provides a surface for managing a large number of 2D graphical items...
QRectF sceneEffectiveBoundingRect() const
Returns the effective bounding rect of this item in scene coordinates, by combining sceneTransform() ...
QGraphicsItem * parent
double qreal
Definition: qglobal.h:1193
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QScopedPointer< QGraphicsItemPrivate > d_ptr
qreal qLn(qreal v)
Definition: qmath.h:221
int qCeil(qreal v)
Definition: qmath.h:63
QGraphicsSceneBspTreeIndexPrivate(QGraphicsScene *scene)
Constructs a private scene bsp index.
void clear()
Clear the all the BSP index.
void addItem(QGraphicsItem *item)
Add the item into the BSP index.
bool isEmpty() const
Definition: qset.h:77
The QGraphicsItem class is the base class for all graphical items in a QGraphicsScene.
Definition: qgraphicsitem.h:89
void initialize(const QRectF &rect, int depth)
iterator begin()
Returns an STL-style iterator pointing to the first item in the list.
Definition: qlist.h:267
bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
The QGraphicsSceneBspTreeIndex class provides an implementation of a BSP indexing algorithm for disco...
QList< QGraphicsItem * > estimateTopLevelItems(const QRectF &rect, Qt::SortOrder order) const
bool event(QEvent *event)
Used to catch the timer event.
GraphicsItemChange
This enum describes the state changes that are notified by QGraphicsItem::itemChange().
QList< QGraphicsItem * > items(const QRectF &rect, bool onlyTopLevelItems=false) const
void updateSceneRect(const QRectF &rect)
This method react to the rect change of the scene and reset the BSP tree index.
Q_DECL_CONSTEXPR T qAbs(const T &t)
Definition: qglobal.h:1201
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
virtual bool event(QEvent *)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition: qobject.cpp:1200
#define Q_D(Class)
Definition: qglobal.h:2482
static bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
void purgeRemovedItems()
Removes stale pointers from all data structures.
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
#define Q_Q(Class)
Definition: qglobal.h:2483
bool qt_closestItemLast(const QGraphicsItem *item1, const QGraphicsItem *item2)
Returns true if item2 is on top of item1.
static bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
bool removeOne(const T &t)
Removes the first occurrence of value in the list and returns true on success; otherwise returns fals...
Definition: qlist.h:796
static void climbTree(QGraphicsItem *item, int *stackingOrder)
void addItem(QGraphicsItem *item, bool recursive=false)
bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2)
Returns true if item1 is on top of item2.
SortOrder
Definition: qnamespace.h:189
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
The QRectF class defines a rectangle in the plane using floating point precision. ...
Definition: qrect.h:511
QBool contains(const T &t) const
Returns true if the list contains an occurrence of value; otherwise returns false.
Definition: qlist.h:880
T takeFirst()
Removes the first item in the list and returns it.
Definition: qlist.h:489
bool itemIsUntransformable() const
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the list...
Definition: qlist.h:270
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
QList< QGraphicsItem * > items(Qt::SortOrder order=Qt::DescendingOrder) const
Return all items in the BSP index and sort them using order.
Q_CORE_EXPORT void qWarning(const char *,...)
void clear()
Definition: qset.h:87
void itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const void *const value)
This method react to the change of the item and use the value to update the BSP tree if necessary...
static void sortItems(QList< QGraphicsItem *> *itemList, Qt::SortOrder order, bool cached, bool onlyTopLevelItems=false)
Sort a list of itemList in a specific order and use the cache if requested.
QGraphicsItem * topLevelItem() const
Returns this item&#39;s top-level item.
The QGraphicsSceneIndex class provides a base class to implement a custom indexing algorithm for disc...
void clear()
Removes all items from the list.
Definition: qlist.h:764
GraphicsItemFlags flags() const
Returns this item&#39;s flags.
void insertItem(QGraphicsItem *item, const QRectF &rect)
void _q_updateIndex()
This method will update the BSP index by removing the items from the temporary unindexed list and add...
void qSort(RandomAccessIterator start, RandomAccessIterator end)
Definition: qalgorithms.h:177
void startIndexTimer(int interval=QGRAPHICSSCENE_INDEXTIMER_TIMEOUT)
Starts or restarts the timer used for reindexing unindexed items.
static int intmaxlog(int n)
QList< QGraphicsItem * > estimateItems(const QRectF &rect, Qt::SortOrder order) const
Returns an estimation visible items that are either inside or intersect with the specified rect and r...
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
void prepareBoundingRectChange(const QGraphicsItem *item)
Update the BSP when the item &#39;s bounding rect has changed.
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(0), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
Invokes the member (a signal or a slot name) on the object obj.
void removeItems(const QSet< QGraphicsItem *> &items)
QList< QGraphicsItem * > children
The QEvent class is the base class of all event classes.
Definition: qcoreevent.h:56
Type type() const
Returns the event type.
Definition: qcoreevent.h:303
QGraphicsSceneBspTreeIndex(QGraphicsScene *scene=0)
Constructs a BSP scene index for the given scene.
bool isNull() const
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition: qrect.h:655
void removeItem(QGraphicsItem *item, const QRectF &rect)
void removeItem(QGraphicsItem *item)
Remove the item from the BSP index.
bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
The QList class is a template class that provides lists.
Definition: qdatastream.h:62
void removeItem(QGraphicsItem *item, bool recursive=false, bool moveToUnindexedItems=false)