Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 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 11327421 : const float extent0 = a_rect->m_max[0] - a_rect->m_min[0];
41 11327421 : const float extent1 = a_rect->m_max[1] - a_rect->m_min[1];
42 7784423 : 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 10986705 : newRect.m_min[0] = rtree_min(a_rectA->m_min[0], a_rectB->m_min[0]);
50 10986705 : newRect.m_max[0] = rtree_max(a_rectA->m_max[0], a_rectB->m_max[0]);
51 10986705 : newRect.m_min[1] = rtree_min(a_rectA->m_min[1], a_rectB->m_min[1]);
52 10986705 : 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 31884 : SUMORTree() :
70 : GUI_RTREE_QUAL(&GUIGlObject::drawGL),
71 31884 : myLock(true) {
72 31884 : }
73 :
74 : /// @brief Destructor
75 47730 : virtual ~SUMORTree() {
76 : // check if lock is locked before insert objects
77 31820 : 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 47730 : }
82 :
83 : /** @brief Insert entry
84 : * @param a_min Min of bounding rect
85 : * @param a_max Max of bounding rect
86 : * @param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
87 : * @see RTree::Insert
88 : */
89 444916 : virtual void Insert(const float a_min[2], const float a_max[2], GUIGlObject* const & a_dataId) {
90 444916 : FXMutexLock locker(myLock);
91 444916 : GUI_RTREE_QUAL::Insert(a_min, a_max, a_dataId);
92 444916 : }
93 :
94 : /** @brief Remove entry
95 : * @param a_min Min of bounding rect
96 : * @param a_max Max of bounding rect
97 : * @param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
98 : * @see RTree::Remove
99 : */
100 2387 : virtual void Remove(const float a_min[2], const float a_max[2], GUIGlObject* const & a_dataId) {
101 2387 : FXMutexLock locker(myLock);
102 2387 : GUI_RTREE_QUAL::Remove(a_min, a_max, a_dataId);
103 2387 : }
104 :
105 : /** @brief Find all within search rectangle
106 : * @param a_min Min of search bounding rect
107 : * @param a_max Max of search bounding rect
108 : * @param a_searchResult Search result array. Caller should set grow size. Function will reset, not append to array.
109 : * @param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
110 : * @param a_context User context to pass as parameter to a_resultCallback
111 : * @return Returns the number of entries found
112 : * @see RTree::Search
113 : */
114 1294149 : virtual int Search(const float a_min[2], const float a_max[2], const GUIVisualizationSettings& c) const {
115 1294149 : FXMutexLock locker(myLock);
116 2588297 : return GUI_RTREE_QUAL::Search(a_min, a_max, c);
117 : }
118 :
119 : /** @brief Adds an additional object (detector/shape/trigger) for visualisation
120 : * @param[in] o The object to add
121 : */
122 28843 : void addAdditionalGLObject(GUIGlObject *o, const double exaggeration = 1) {
123 : // check if lock is locked before insert objects
124 28843 : if (myLock.locked()) {
125 0 : throw ProcessError("Mutex of SUMORTree is locked before object insertion");
126 : }
127 : // lock mutex
128 : FXMutexLock locker(myLock);
129 : // obtain boundary of object
130 28843 : Boundary b = o->getCenteringBoundary();
131 : // grow using exaggeration
132 28843 : if (exaggeration > 1) {
133 0 : b.scale(exaggeration);
134 : }
135 : // show information in gui testing debug gl mode
136 28843 : if (MsgHandler::writeDebugGLMessages()) {
137 0 : if (!b.isInitialised()) {
138 0 : throw ProcessError(StringUtils::format("Boundary of GUIGlObject % is not initialised (insertion)", o->getMicrosimID()));
139 0 : } else if ((b.getWidth() == 0) || (b.getHeight() == 0)) {
140 0 : throw ProcessError(StringUtils::format("Boundary of GUIGlObject % has an invalid size (insertion)", o->getMicrosimID()));
141 : } else if (myTreeDebug.count(o) > 0) {
142 0 : throw ProcessError("GUIGlObject was already inserted");
143 : } else {
144 0 : myTreeDebug[o] = b;
145 : }
146 : }
147 : // insert it in Tree
148 28843 : const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
149 28843 : const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
150 28843 : Insert(cmin, cmax, o);
151 : // update tree size
152 28843 : myTreeSize++;
153 28843 : }
154 :
155 : /** @brief Removes an additional object (detector/shape/trigger) from being visualised
156 : * @param[in] o The object to remove
157 : */
158 2387 : void removeAdditionalGLObject(GUIGlObject *o, const double exaggeration = 1) {
159 : // check if lock is locked remove insert objects
160 2387 : if (myLock.locked()) {
161 0 : throw ProcessError("Mutex of SUMORTree is locked before object remove");
162 : }
163 : // lock mutex
164 : FXMutexLock locker(myLock);
165 : // obtain boundary of object
166 2387 : Boundary b = o->getCenteringBoundary();
167 : // grow using exaggeration
168 2387 : if (exaggeration > 1) {
169 0 : b.scale(exaggeration);
170 : }
171 : // show information in gui testing debug gl mode
172 2387 : if (MsgHandler::writeDebugGLMessages()) {
173 0 : if (!b.isInitialised()) {
174 0 : throw ProcessError(StringUtils::format("Boundary of GUIGlObject % is not initialised (deletion)", o->getMicrosimID()));
175 0 : } else if ((b.getWidth() == 0) || (b.getHeight() == 0)) {
176 0 : throw ProcessError(StringUtils::format("Boundary of GUIGlObject % has an invalid size (deletion)", o->getMicrosimID()));
177 : } else if (myTreeDebug.count(o) == 0) {
178 0 : throw ProcessError("GUIGlObject wasn't inserted");
179 0 : } else if (toString(b) != toString(myTreeDebug.at(o))) {
180 : // show information in console before throwing exception
181 0 : std::cout << "Tree: " << toString(myTreeDebug.at(o)) << " original: " << toString(b) << std::endl;
182 0 : throw ProcessError("add boundary of GUIGlObject " + o->getMicrosimID() + " is different of removed boundary (" + toString(b) + " != " + toString(myTreeDebug.at(o)) + ")");
183 : } else {
184 : myTreeDebug.erase(o);
185 : }
186 : }
187 : // remove it from Tree
188 2387 : const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
189 2387 : const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
190 2387 : Remove(cmin, cmax, o);
191 : // update tree size
192 2387 : myTreeSize--;
193 2387 : }
194 :
195 : /// @brief update boundaries
196 : void updateBoundaries(GUIGlObjectType type) {
197 : // declare vector with glObjects to update
198 : std::vector<GUIGlObject*> glObjects;
199 : glObjects.reserve(myTreeSize);
200 : // declare iterator
201 : GUI_RTREE_QUAL::Iterator it;
202 : GetFirst(it);
203 : // iterate over entire tree and keep glObject in glObjects
204 : while (!IsNull(it)) {
205 : const auto glType = (*it)->getType();
206 : if ((glType == type) ||
207 : ((glType > GLO_ADDITIONALELEMENT) && (glType < GLO_SHAPE)) || // Additionals
208 : ((glType >= GLO_TAZ) && (glType < GLO_LOCKICON))) { // TAZ Elements
209 : glObjects.push_back(*it);
210 : }
211 : GetNext(it);
212 : }
213 : // remove and insert all elements again with the new boundary
214 : for (const auto &glObject : glObjects) {
215 : removeAdditionalGLObject(glObject);
216 : removeObjectFromTreeDebug(glObject);
217 : addAdditionalGLObject(glObject);
218 : }
219 : }
220 :
221 : protected:
222 : /// @brief A mutex avoiding parallel change and traversal of the tree
223 : mutable FXMutex myLock;
224 :
225 : /// @brief number of inserted elements
226 : int myTreeSize = 0;
227 :
228 : private:
229 : /**@brief Map only used for check that SUMORTree works as expected, only is used if option "gui-testing-debug-gl" is enabled.
230 : * @note Warning: DO NOT USE in release mode and use it in debug mode carefully, due it produces a slowdown.
231 : */
232 : std::map<GUIGlObject*, Boundary> myTreeDebug;
233 :
234 : /// @brief remove object from TreeDebug
235 : bool removeObjectFromTreeDebug(const GUIGlObject* obj) {
236 : for (auto it = myTreeDebug.begin(); it != myTreeDebug.end(); it++) {
237 : if (it->first == obj) {
238 : myTreeDebug.erase(it);
239 : return true;
240 : }
241 : }
242 : return false;
243 : }
244 : };
|