LCOV - code coverage report
Current view: top level - src/foreign/rtree - SUMORTree.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 68.2 % 66 45
Test Date: 2024-11-22 15:46:21 Functions: 100.0 % 8 8

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    SUMORTree.h
      15              : /// @author  Daniel Krajzewicz
      16              : /// @date    27.10.2008
      17              : ///
      18              : // A RT-tree for efficient storing of SUMO's GL-objects
      19              : /****************************************************************************/
      20              : #pragma once
      21              : #include <config.h>
      22              : 
      23              : #include <utils/foxtools/fxheader.h>
      24              : #include <utils/common/MsgHandler.h>
      25              : #include <utils/geom/Boundary.h>
      26              : #include <utils/gui/globjects/GUIGlObject.h>
      27              : #include <utils/gui/settings/GUIVisualizationSettings.h>
      28              : #include <utils/gui/div/GUIIOGlobals.h>
      29              : 
      30              : #include "RTree.h"
      31              : 
      32              : 
      33              : #define GUI_RTREE_QUAL RTree<GUIGlObject*, GUIGlObject, float, 2, GUIVisualizationSettings>
      34              : 
      35              : // specialized implementation for speedup and avoiding warnings
      36              : 
      37              : template<>
      38              : inline float GUI_RTREE_QUAL::RectSphericalVolume(Rect* a_rect) {
      39              :   ASSERT(a_rect);
      40     10103440 :   const float extent0 = a_rect->m_max[0] - a_rect->m_min[0];
      41     10103440 :   const float extent1 = a_rect->m_max[1] - a_rect->m_min[1];
      42      6973373 :   return .78539816f * (extent0 * extent0 + extent1 * extent1);
      43              : }
      44              : 
      45              : template<>
      46              : inline GUI_RTREE_QUAL::Rect GUI_RTREE_QUAL::CombineRect(Rect* a_rectA, Rect* a_rectB) {
      47              :     ASSERT(a_rectA && a_rectB);
      48              :     Rect newRect;
      49      9861706 :     newRect.m_min[0] = rtree_min(a_rectA->m_min[0], a_rectB->m_min[0]);
      50      9861706 :     newRect.m_max[0] = rtree_max(a_rectA->m_max[0], a_rectB->m_max[0]);
      51      9861706 :     newRect.m_min[1] = rtree_min(a_rectA->m_min[1], a_rectB->m_min[1]);
      52      9861706 :     newRect.m_max[1] = rtree_max(a_rectA->m_max[1], a_rectB->m_max[1]);
      53              :     return newRect;
      54              : }
      55              : 
      56              : 
      57              : // ===========================================================================
      58              : // class definitions
      59              : // ===========================================================================
      60              : /** @class SUMORTree
      61              :  * @brief A RT-tree for efficient storing of SUMO's GL-objects
      62              :  *
      63              :  * This class specialises the used RT-tree implementation from "rttree.h" and
      64              :  *  extends it by a mutex for avoiding parallel change and traversal of the tree.
      65              :  */
      66              : class SUMORTree : private GUI_RTREE_QUAL, public Boundary {
      67              : public:
      68              :     /// @brief Constructor
      69        30100 :     SUMORTree() :
      70              :         GUI_RTREE_QUAL(&GUIGlObject::drawGL),
      71        30100 :         myLock(true) {
      72        30100 :     }
      73              : 
      74              :     /// @brief Destructor
      75        45072 :     virtual ~SUMORTree() {
      76              :         // check if lock is locked before insert objects
      77        30048 :         if (myLock.locked()) {
      78              :             // cannot throw exception in destructor
      79            0 :             WRITE_ERROR("Mutex of SUMORTree is locked during call of the destructor");
      80              :         }
      81              :         // show information in gui testing debug gl mode
      82        30048 :         WRITE_GLDEBUG("Number of objects in SUMORTree during call of the destructor: " + toString(myTreeDebug.size()));
      83        45072 :     }
      84              : 
      85              :     /** @brief Insert entry
      86              :      * @param a_min Min of bounding rect
      87              :      * @param a_max Max of bounding rect
      88              :      * @param a_dataId Positive Id of data.  Maybe zero, but negative numbers not allowed.
      89              :      * @see RTree::Insert
      90              :      */
      91       409525 :     virtual void Insert(const float a_min[2], const float a_max[2], GUIGlObject* const & a_dataId) {
      92       409525 :         FXMutexLock locker(myLock);
      93       409525 :         GUI_RTREE_QUAL::Insert(a_min, a_max, a_dataId);
      94       409525 :     }
      95              : 
      96              :     /** @brief Remove entry
      97              :      * @param a_min Min of bounding rect
      98              :      * @param a_max Max of bounding rect
      99              :      * @param a_dataId Positive Id of data.  Maybe zero, but negative numbers not allowed.
     100              :      * @see RTree::Remove
     101              :      */
     102         2396 :     virtual void Remove(const float a_min[2], const float a_max[2], GUIGlObject* const & a_dataId) {
     103         2396 :         FXMutexLock locker(myLock);
     104         2396 :         GUI_RTREE_QUAL::Remove(a_min, a_max, a_dataId);
     105         2396 :     }
     106              : 
     107              :     /** @brief Find all within search rectangle
     108              :      * @param a_min Min of search bounding rect
     109              :      * @param a_max Max of search bounding rect
     110              :      * @param a_searchResult Search result array.  Caller should set grow size. Function will reset, not append to array.
     111              :      * @param a_resultCallback Callback function to return result.  Callback should return 'true' to continue searching
     112              :      * @param a_context User context to pass as parameter to a_resultCallback
     113              :      * @return Returns the number of entries found
     114              :      * @see RTree::Search
     115              :      */
     116      1076992 :     virtual int Search(const float a_min[2], const float a_max[2], const GUIVisualizationSettings& c) const {
     117      1076992 :         FXMutexLock locker(myLock);
     118      2153980 :         return GUI_RTREE_QUAL::Search(a_min, a_max, c);
     119              :     }
     120              : 
     121              :     /** @brief Adds an additional object (detector/shape/trigger) for visualisation
     122              :      * @param[in] o The object to add
     123              :      */
     124        29012 :     void addAdditionalGLObject(GUIGlObject *o, const double exaggeration = 1) {
     125              :         // check if lock is locked before insert objects
     126        29012 :         if (myLock.locked()) {
     127            0 :             throw ProcessError("Mutex of SUMORTree is locked before object insertion");
     128              :         }
     129              :         // lock mutex
     130              :         FXMutexLock locker(myLock);
     131              :         // obtain boundary of object
     132        29012 :         Boundary b = o->getCenteringBoundary();
     133              :         // grow using exaggeration
     134        29012 :         if (exaggeration > 1) {
     135            0 :             b.scale(exaggeration);
     136              :         }
     137              :         // show information in gui testing debug gl mode
     138        29012 :         if (MsgHandler::writeDebugGLMessages()) {
     139            0 :             if (!b.isInitialised()) {
     140            0 :                 throw ProcessError(StringUtils::format("Boundary of GUIGlObject % is not initialised (insertion)", o->getMicrosimID()));
     141            0 :             } else if ((b.getWidth() == 0) || (b.getHeight() == 0)) {
     142            0 :                 throw ProcessError(StringUtils::format("Boundary of GUIGlObject % has an invalid size (insertion)", o->getMicrosimID()));
     143              :             } else if (myTreeDebug.count(o) > 0) {
     144            0 :                 throw ProcessError("GUIGlObject was already inserted");
     145              :             } else {
     146            0 :                 myTreeDebug[o] = b;
     147              :                 // write GL Debug
     148            0 :                 WRITE_GLDEBUG("\tInserted " + o->getFullName() + " into SUMORTree with boundary " + toString(b));
     149              :             }
     150              :         }
     151              :         // insert it in Tree
     152        29012 :         const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
     153        29012 :         const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
     154        29012 :         Insert(cmin, cmax, o);
     155              :         // update tree size
     156        29012 :         myTreeSize++;
     157        58024 :     }
     158              : 
     159              :     /** @brief Removes an additional object (detector/shape/trigger) from being visualised
     160              :      * @param[in] o The object to remove
     161              :      */
     162         2396 :     void removeAdditionalGLObject(GUIGlObject *o, const double exaggeration = 1) {
     163              :         // check if lock is locked remove insert objects
     164         2396 :         if (myLock.locked()) {
     165            0 :             throw ProcessError("Mutex of SUMORTree is locked before object remove");
     166              :         }
     167              :         // lock mutex
     168              :         FXMutexLock locker(myLock);
     169              :         // obtain boundary of object
     170         2396 :         Boundary b = o->getCenteringBoundary();
     171              :         // grow using exaggeration
     172         2396 :         if (exaggeration > 1) {
     173            0 :             b.scale(exaggeration);
     174              :         }
     175              :         // show information in gui testing debug gl mode
     176         2396 :         if (MsgHandler::writeDebugGLMessages()) {
     177            0 :             if (!b.isInitialised()) {
     178            0 :                 throw ProcessError(StringUtils::format("Boundary of GUIGlObject % is not initialised (deletion)", o->getMicrosimID()));
     179            0 :             } else if ((b.getWidth() == 0) || (b.getHeight() == 0)) {
     180            0 :                 throw ProcessError(StringUtils::format("Boundary of GUIGlObject % has an invalid size (deletion)", o->getMicrosimID()));
     181              :             } else if (myTreeDebug.count(o) == 0) {
     182            0 :                 throw ProcessError("GUIGlObject wasn't inserted");
     183            0 :             } else if (toString(b) != toString(myTreeDebug.at(o))) {
     184              :                 // show information in console before throwing exception
     185            0 :                 std::cout << "Tree: " << toString(myTreeDebug.at(o)) << " original: " << toString(b) << std::endl;
     186            0 :                 throw ProcessError("add boundary of GUIGlObject " + o->getMicrosimID() + " is different of removed boundary (" + toString(b) + " != " + toString(myTreeDebug.at(o)) + ")");
     187              :             } else {
     188              :                 myTreeDebug.erase(o);
     189            0 :                 WRITE_GLDEBUG("\tRemoved object " + o->getFullName() + " from SUMORTree with boundary " + toString(b));
     190              :             }
     191              :         }
     192              :         // remove it from Tree
     193         2396 :         const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
     194         2396 :         const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
     195         2396 :         Remove(cmin, cmax, o);
     196              :         // update tree size
     197         2396 :         myTreeSize--;
     198         4792 :     }
     199              : 
     200              :     /// @brief update boundaries
     201              :     void updateBoundaries(GUIGlObjectType type) {
     202              :         // declare vector with glObjects to update
     203              :         std::vector<GUIGlObject*> glObjects;
     204              :         glObjects.reserve(myTreeSize);
     205              :         // declare iterator
     206              :         GUI_RTREE_QUAL::Iterator it;
     207              :         GetFirst(it);
     208              :         // iterate over entire tree and keep glObject in glObjects
     209              :         while (!IsNull(it)) {
     210              :             const auto glType = (*it)->getType();
     211              :             if ((glType == type) ||
     212              :                 ((glType > GLO_ADDITIONALELEMENT) && (glType < GLO_SHAPE)) ||   // Additionals
     213              :                 ((glType >= GLO_TAZ) && (glType < GLO_LOCKICON))) {             // TAZ Elements
     214              :                 glObjects.push_back(*it);
     215              :             }
     216              :             GetNext(it);
     217              :         }
     218              :         // remove and insert all elements again with the new boundary
     219              :         for (const auto &glObject : glObjects) {
     220              :             removeAdditionalGLObject(glObject);
     221              :             removeObjectFromTreeDebug(glObject);
     222              :             addAdditionalGLObject(glObject);
     223              :         }
     224              :     }
     225              : 
     226              : protected:
     227              :     /// @brief A mutex avoiding parallel change and traversal of the tree
     228              :     mutable FXMutex myLock;
     229              : 
     230              :     /// @brief number of inserted elements
     231              :     int myTreeSize = 0;
     232              : 
     233              : private:
     234              :     /**@brief Map only used for check that SUMORTree works as expected, only is used if option "gui-testing-debug-gl" is enabled.
     235              :      * @note Warning: DO NOT USE in release mode and use it in debug mode carefully, due it produces a slowdown.
     236              :      */
     237              :     std::map<GUIGlObject*, Boundary> myTreeDebug;
     238              : 
     239              :     /// @brief remove object from TreeDebug
     240              :     bool removeObjectFromTreeDebug(const GUIGlObject* obj) {
     241              :         for (auto it = myTreeDebug.begin(); it != myTreeDebug.end(); it++) {
     242              :             if (it->first == obj) {
     243              :                 myTreeDebug.erase(it);
     244              :                 return true;
     245              :             }
     246              :         }
     247              :         return false;
     248              :     }
     249              : };
        

Generated by: LCOV version 2.0-1