Fixed map items collision detection over map limits
[situare] / src / map / frienditemshandler.cpp
index 4334a3f..9562570 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "friendgroupitem.h"
 #include "friendlocationitem.h"
+#include "mapcommon.h"
 #include "mapengine.h"
 #include "mapscene.h"
 #include "user/user.h"
 
 FriendItemsHandler::FriendItemsHandler(MapScene *mapScene, QObject *parent)
     : QObject(parent),
-      m_mapScene(mapScene)
+      m_mapScene(mapScene),
+      m_zoomLevel(0)
 {
+    qDebug() << __PRETTY_FUNCTION__;
 }
 
-FriendItemsHandler::~FriendItemsHandler()
+void FriendItemsHandler::addFriendItem(User *friendData)
 {
-    qDeleteAll(m_friendItems.begin(),m_friendItems.end());
-    m_friendItems.clear();
+    qDebug() << __PRETTY_FUNCTION__;
+
+    FriendLocationItem *item = new FriendLocationItem(friendData->userId());
+
+    item->setProfileImage(friendData->profileImage(), friendData->profileImageUrl());
+    item->setPos(MapEngine::convertLatLonToSceneCoordinate(friendData->coordinates()));
+    m_friendItems.append(item);
+    m_mapScene->addItem(item);
+
+    connect(item, SIGNAL(locationItemClicked(QList<QString>)),
+            this, SIGNAL(locationItemClicked(QList<QString>)));
 }
 
-void FriendItemsHandler::checkFriendsForCollisions()
+void FriendItemsHandler::checkAllFriendsForCollidingFriends()
 {
-    qWarning() << __PRETTY_FUNCTION__;
-
-//    foreach(FriendGroupItem *item, m_friendGroupItems) {
-//        m_friendGroupItems.removeAll(item);
-//        m_mapScene->removeItem(item);
-//    }
-
-    int lastItem = m_friendItems.size() - 1;
-    for (int checking = 0; checking <= lastItem; checking++) {
-        QRect beginItemRect = m_friendItems.at(checking)->sceneTransformedBoundingRect(m_zoomLevel);
-
-        // don't check items which are already part of a group (and thus unvisible)
-        if (m_friendItems.at(checking)->isPartOfGroup())
-            continue;
-
-        // check all other items
-        for (int another = 0; another <= lastItem; another++)
-        {
-            // don't check against itself
-            if (checking == another)
-                continue;
-
-            // don't check against items which are already part of a group (and thus unvisible)
-            if (m_friendItems.at(another)->isPartOfGroup())
-                continue;
-
-            // does items collide
-            QRect iterItemRect = m_friendItems.at(another)->sceneTransformedBoundingRect(m_zoomLevel);
-            if (beginItemRect.intersects(iterItemRect)) {
-                qWarning() << __PRETTY_FUNCTION__ << "collision found" << checking << another;
-                FriendGroupItem *group = new FriendGroupItem();
-                m_friendGroupItems.append(group);
-                group->setPos(m_friendItems.at(checking)->pos());
-                group->joinFriend(m_friendItems.at(checking));
-                group->joinFriend(m_friendItems.at(another));
-                m_mapScene->addItem(group);
-                break;
-            }
+    qDebug() << __PRETTY_FUNCTION__;
+
+    QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
+    while (iter != m_friendItems.end()) {
+        // check only friends which are not part of group already
+        if (!(*iter)->isPartOfGroup()) {
+            checkFriendForCollidingFriends(*iter);
         }
+        iter++;
     }
 }
 
-void FriendItemsHandler::checkGroupsForCollisions()
+void FriendItemsHandler::checkAllGroupsForCollidingFriends()
 {
+    qDebug() << __PRETTY_FUNCTION__;
 
+    // loop through all groups
+    QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
+    while (iter != m_friendGroupItems.end()) {
+        checkGroupForCollidingFriends(*iter);
+        iter++;
+    }
 }
 
-void FriendItemsHandler::cleanOldFriendData(const QList<User *> &friendsList)
+void FriendItemsHandler::checkFriendForCollidingFriends(FriendLocationItem *item)
 {
-    bool friendGone = true;
-
-    for (int i=0; i<m_friendItems.count(); i++) {
+    // checkGroupsForCollisions() is used for checking if groups collide with another
+    // groups or friend items, so this method doesn't have to check against groups
 
-        for (int j=0; j<friendsList.count(); j++) {
+    qDebug() << __PRETTY_FUNCTION__;
 
-            if (m_friendItems.at(i)->userId() == friendsList.at(j)->userId())
-                friendGone = false;
+    FriendGroupItem *group = 0;
+
+    // loop through all friend items
+    QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
+    while (iter != m_friendItems.end()) {
+        // but don't check myself and friends which are already part of a group
+        if (item != *iter && !(*iter)->isPartOfGroup()) {
+            if (collides(item, *iter)) {
+                if (!group) {
+                    group = new FriendGroupItem(item);
+                    m_mapScene->addItem(group);
+                    m_friendGroupItems.append(group);
+
+                    connect(group, SIGNAL(locationItemClicked(QList<QString>)),
+                            this, SIGNAL(locationItemClicked(QList<QString>)));
+                }
+                group->joinFriend(*iter);
+            }
         }
+        iter++;
+    }
+}
 
-        if (friendGone) {
-            m_mapScene->removeItem(m_friendItems.at(i));
-            m_friendItems.removeAt(i);
-        }
+void FriendItemsHandler::checkGroupForCollidingFriends(FriendGroupItem *group)
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
-        friendGone = true;
+    // loop through all friend items
+    QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
+    while (iter != m_friendItems.end()) {
+        // but don't check friends which are already part of a group
+        if (!(*iter)->isPartOfGroup()) {
+            if (collides(group, *iter)) {
+                group->joinFriend(*iter);
+            }
+        }
+        iter++;
     }
 }
 
-void FriendItemsHandler::dropOutOfGroupFriends()
+void FriendItemsHandler::checkGroupForCollidingGroups(FriendGroupItem *group)
 {
-    // loop through all group items and drop friends which doesn't collide anymore
-    foreach (FriendGroupItem *group, m_friendGroupItems)
-        group->dropFriends(m_zoomLevel);
+    qDebug() << __PRETTY_FUNCTION__;
+
+    // loop through all groups
+    QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
+    while (iter != m_friendGroupItems.end()) {
+        // but don't check myself
+        if (group != *iter) {
+            if (collides(group, *iter)) {
+                (*iter)->mergeWithGroup(group);
+                m_mapScene->removeItem(*iter);
+                delete *iter;
+                iter = m_friendGroupItems.erase(iter);
+            }
+            else {
+                iter++;
+            }
+        }
+        else {
+            iter++;
+        }
+    }
 }
 
-void FriendItemsHandler::receiveFriendLocations(QList<User *> &friendsList)
+bool FriendItemsHandler::collides(BaseLocationItem *item1, BaseLocationItem *item2)
 {
-    qWarning() << __PRETTY_FUNCTION__;
+    QRect rect = item1->sceneTransformedBoundingRect(m_zoomLevel);
 
-    static int receiveFriendsLocationsCounter = 0;
+    if (rect.intersects(item2->sceneTransformedBoundingRect(m_zoomLevel)))
+        return true;
 
-    if (receiveFriendsLocationsCounter == 0) {
-        receiveFriendsLocationsCounter++;
-        updateFriendItemList(friendsList);
+    if (rect.left() < MAP_MIN_PIXEL_X) {
+        QRect translated = rect.translated(MAP_PIXELS_X, 0);
+        if (translated.intersects(item2->sceneTransformedBoundingRect(m_zoomLevel)))
+            return true;
     }
 
-    else {
-        /// @todo VILLE: New friends are never added to list & scene
-        receiveFriendsLocationsCounter++;
-        updateFriendLocationsAndImages(friendsList);
-        cleanOldFriendData(friendsList);
+    if (rect.right() > MAP_MAX_PIXEL_X) {
+        QRect translated = rect.translated(-MAP_PIXELS_X, 0);
+        if (translated.intersects(item2->sceneTransformedBoundingRect(m_zoomLevel)))
+            return true;
     }
+
+    return false;
 }
 
-void FriendItemsHandler::refactorFriendItems(int zoomLevel)
+void FriendItemsHandler::deleteFriendItem(FriendLocationItem *item)
 {
-    m_zoomLevel = zoomLevel;
+    qDebug() << __PRETTY_FUNCTION__;
 
-    dropOutOfGroupFriends();
-    checkGroupsForCollisions();
-    checkFriendsForCollisions();
+    dropFriendFromAllGroups(item);
+    m_mapScene->removeItem(item);
+    delete item;
 }
 
-void FriendItemsHandler::updateFriendItemList(const QList<User *> &friendsList)
+void FriendItemsHandler::dropFriendFromAllGroups(FriendLocationItem *item)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    qDeleteAll(m_friendItems.begin(),m_friendItems.end());
-    m_friendItems.clear();
+    foreach (FriendGroupItem *group, m_friendGroupItems) {
+        group->dropFriend(item);
+    }
+}
 
+void FriendItemsHandler::dropOutOfGroupFriends()
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
-    for (int i=0; i<friendsList.count(); i++) {
-        FriendLocationItem *friendLocation
-                = new FriendLocationItem(friendsList.at(i)->profileImage(),
-                                         friendsList.at(i)->coordinates(),0);
+    // loop through all group items and drop friends which doesn't collide anymore
+    // delete group if possible
+    foreach (FriendGroupItem *group, m_friendGroupItems) {
+        if (group->dropFriends(m_zoomLevel)) {
+            m_friendGroupItems.removeAll(group);
+            m_mapScene->removeItem(group);
+            delete group;
+        }
+    }
+}
 
-        friendLocation->setUserId(friendsList.at(i)->userId());
-        m_friendItems.append(friendLocation);
-        m_mapScene->addItem(friendLocation);
+void FriendItemsHandler::friendListUpdated(QList<User *> &friendsList)
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
-        //refactorFriendItems(m_zoomLevel);
+    // loop through friend items and find matching friend data. If matching data
+    // is not found then remove item
+    QLinkedList<FriendLocationItem *>::iterator iter = m_friendItems.begin();
+    while (iter != m_friendItems.end()) {
+        bool found = false;
+        foreach (User * friendData, friendsList) {
+            if (friendData->userId() == (*iter)->userId()) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            // data for friend item was not found so item must be deleted
+            deleteFriendItem(*iter);
+            iter = m_friendItems.erase(iter);
+        }
+        else {
+            iter++;
+        }
     }
+
+    // loop through new friend data, find matching friend items and update them, or add new items
+    // if old items are not found
+    foreach (User * friendData, friendsList) {
+        bool found = false;
+        foreach (FriendLocationItem *friendItem, m_friendItems) {
+            if (friendData->userId() == friendItem->userId()) {
+                // friend item was found so update the data
+                updateFriendItem(friendItem, friendData);
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            // friend item was not found so new item must be added
+            addFriendItem(friendData);
+        }
+    }
+
+    refactorFriendItems(m_zoomLevel);
 }
 
-void FriendItemsHandler::updateFriendLocationsAndImages(const QList<User *> &friendsList)
+void FriendItemsHandler::mergeCollidingGroups()
 {
-    for (int i=0; i<friendsList.count(); i++) {
+    qDebug() << __PRETTY_FUNCTION__;
 
-        for (int j=0; j<m_friendItems.count(); j++) {
+    // loop through all groups
+    QLinkedList<FriendGroupItem *>::iterator iter = m_friendGroupItems.begin();
+    while (iter != m_friendGroupItems.end()) {
+        checkGroupForCollidingGroups(*iter);
+        iter++;
+    }
+}
 
-            if (m_friendItems.at(j)->userId() == friendsList.at(i)->userId()) {
+void FriendItemsHandler::refactorFriendItems(int zoomLevel)
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
-                if (m_friendItems.at(j)->position()
-                    != MapEngine::convertLatLonToSceneCoordinate(friendsList.at(i)->coordinates()))
-                    m_friendItems.at(j)->setPosition(friendsList.at(i)->coordinates());
+    m_zoomLevel = zoomLevel;
 
-                if (m_friendItems.at(j)->profileImageUrl()
-                    != friendsList.at(i)->profileImageUrl()) {
-                    m_friendItems.at(j)->setPixmap(friendsList.at(i)->profileImage());
-                }
-            }
-        }
-    }
+    mergeCollidingGroups();
+    dropOutOfGroupFriends();
+    checkAllGroupsForCollidingFriends();
+    checkAllFriendsForCollidingFriends();
 }
 
+void FriendItemsHandler::updateFriendItem(FriendLocationItem *friendItem, User *friendData)
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
+    // update position
+    QPoint newPosition = MapEngine::convertLatLonToSceneCoordinate(friendData->coordinates());
+    if (friendItem->pos().toPoint() != newPosition)
+        friendItem->setPos(newPosition);
+
+    // update image
+    if (friendItem->profileImageUrl() != friendData->profileImageUrl())
+        friendItem->setProfileImage(friendData->profileImage(), friendData->profileImageUrl());
+}