Panda3D
mouseWatcherBase.cxx
1 // Filename: mouseWatcherBase.cxx
2 // Created by: rdb (13Jan14)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "mouseWatcherBase.h"
16 #include "lineSegs.h"
17 #include "indent.h"
18 #include "lightMutexHolder.h"
19 
20 TypeHandle MouseWatcherBase::_type_handle;
21 
22 ////////////////////////////////////////////////////////////////////
23 // Function: MouseWatcherBase::Constructor
24 // Access: Public
25 // Description:
26 ////////////////////////////////////////////////////////////////////
27 MouseWatcherBase::
28 MouseWatcherBase() :
29  _lock("MouseWatcherBase")
30 {
31  _sorted = true;
32 #ifndef NDEBUG
33  _show_regions = false;
34  _color.set(0.4, 0.6f, 1.0f, 1.0f);
35 #endif // NDEBUG
36 }
37 
38 ////////////////////////////////////////////////////////////////////
39 // Function: MouseWatcherBase::Destructor
40 // Access: Public, Virtual
41 // Description:
42 ////////////////////////////////////////////////////////////////////
43 MouseWatcherBase::
44 ~MouseWatcherBase() {
45 }
46 
47 ////////////////////////////////////////////////////////////////////
48 // Function: MouseWatcherBase::add_region
49 // Access: Published
50 // Description: Adds the indicated region to the set of regions in
51 // the group. It is an error to add the same region to
52 // the set more than once.
53 ////////////////////////////////////////////////////////////////////
56  PT(MouseWatcherRegion) pt = region;
57 
58  LightMutexHolder holder(_lock);
59 
60  // We will only bother to check for duplicates in the region list if
61  // we are building a development Panda. The overhead for doing this
62  // may be too high if we have many regions.
63 #ifdef _DEBUG
64  // See if the region is in the set/vector already
65  Regions::const_iterator ri =
66  find(_regions.begin(), _regions.end(), pt);
67  nassertv(ri == _regions.end());
68 #endif // _DEBUG
69 
70 #ifndef NDEBUG
71  // Also add it to the vizzes if we have them.
72  if (_show_regions) {
73  nassertv(_vizzes.size() == _regions.size());
74  _vizzes.push_back(make_viz_region(pt));
75  }
76 #endif // NDEBUG
77 
78  _regions.push_back(pt);
79  _sorted = false;
80 }
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: MouseWatcherBase::has_region
84 // Access: Published
85 // Description: Returns true if the indicated region has already been
86 // added to the MouseWatcherBase, false otherwise.
87 ////////////////////////////////////////////////////////////////////
90  LightMutexHolder holder(_lock);
91 
92  PT(MouseWatcherRegion) ptr = region;
93 
94  if (_sorted) {
95  // If the vector is already sorted, we can do this the quick way.
96  Regions::const_iterator ri = lower_bound(_regions.begin(), _regions.end(), ptr);
97  return (ri != _regions.end() && (*ri) == ptr);
98  }
99 
100  // If the vector isn't sorted, do a linear scan.
101  Regions::const_iterator ri = find(_regions.begin(), _regions.end(), ptr);
102  return (ri != _regions.end());
103 }
104 
105 ////////////////////////////////////////////////////////////////////
106 // Function: MouseWatcherBase::remove_region
107 // Access: Published
108 // Description: Removes the indicated region from the group.
109 // Returns true if it was successfully removed, or false
110 // if it wasn't there in the first place.
111 ////////////////////////////////////////////////////////////////////
114  LightMutexHolder holder(_lock);
115  return do_remove_region(region);
116 }
117 
118 ////////////////////////////////////////////////////////////////////
119 // Function: MouseWatcherBase::find_region
120 // Access: Published
121 // Description: Returns a pointer to the first region found with the
122 // indicated name. If multiple regions share the same
123 // name, the one that is returned is indeterminate.
124 ////////////////////////////////////////////////////////////////////
126 find_region(const string &name) const {
127  LightMutexHolder holder(_lock);
128 
129  Regions::const_iterator ri;
130  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
131  MouseWatcherRegion *region = (*ri);
132  if (region->get_name() == name) {
133  return region;
134  }
135  }
136 
137  return (MouseWatcherRegion *)NULL;
138 }
139 
140 ////////////////////////////////////////////////////////////////////
141 // Function: MouseWatcherBase::clear_regions
142 // Access: Published
143 // Description: Removes all the regions from the group.
144 ////////////////////////////////////////////////////////////////////
147  LightMutexHolder holder(_lock);
148 
149  _regions.clear();
150  _sorted = true;
151 
152 #ifndef NDEBUG
153  if (_show_regions) {
154  _show_regions_root.node()->remove_all_children();
155  _vizzes.clear();
156  }
157 #endif // NDEBUG
158 }
159 
160 ////////////////////////////////////////////////////////////////////
161 // Function: MouseWatcherBase::sort_regions
162 // Access: Published
163 // Description: Sorts all the regions in this group into pointer
164 // order.
165 ////////////////////////////////////////////////////////////////////
168  LightMutexHolder holder(_lock);
169  do_sort_regions();
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: MouseWatcherBase::is_sorted
174 // Access: Published
175 // Description: Returns true if the group has already been sorted,
176 // false otherwise.
177 ////////////////////////////////////////////////////////////////////
179 is_sorted() const {
180  LightMutexHolder holder(_lock);
181 
182  return _sorted;
183 }
184 
185 ////////////////////////////////////////////////////////////////////
186 // Function: MouseWatcherBase::get_num_regions
187 // Access: Published
188 // Description: Returns the number of regions in the group.
189 ////////////////////////////////////////////////////////////////////
192  LightMutexHolder holder(_lock);
193 
194  return _regions.size();
195 }
196 
197 ////////////////////////////////////////////////////////////////////
198 // Function: MouseWatcherBase::get_region
199 // Access: Published
200 // Description: Returns the nth region of the group; returns NULL if
201 // there is no nth region. Note that this is not
202 // thread-safe; another thread might have removed the
203 // nth region before you called this method.
204 ////////////////////////////////////////////////////////////////////
206 get_region(int n) const {
207  LightMutexHolder holder(_lock);
208  if (n >= 0 && n < (int)_regions.size()) {
209  return _regions[n];
210  }
211  return NULL;
212 }
213 
214 ////////////////////////////////////////////////////////////////////
215 // Function: MouseWatcherBase::output
216 // Access: Published
217 // Description:
218 ////////////////////////////////////////////////////////////////////
219 void MouseWatcherBase::
220 output(ostream &out) const {
221  out << "MouseWatcherGroup (" << _regions.size() << " regions)";
222 }
223 
224 ////////////////////////////////////////////////////////////////////
225 // Function: MouseWatcherBase::write
226 // Access: Published
227 // Description:
228 ////////////////////////////////////////////////////////////////////
229 void MouseWatcherBase::
230 write(ostream &out, int indent_level) const {
231  LightMutexHolder holder(_lock);
232 
233  Regions::const_iterator ri;
234  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
235  MouseWatcherRegion *region = (*ri);
236  region->write(out, indent_level);
237  }
238 }
239 
240 #ifndef NDEBUG
241 ////////////////////////////////////////////////////////////////////
242 // Function: MouseWatcherBase::show_regions
243 // Access: Published
244 // Description: Enables the visualization of all of the regions
245 // handled by this MouseWatcherBase. The supplied
246 // NodePath should be the root of the 2-d scene graph
247 // for the window.
248 ////////////////////////////////////////////////////////////////////
250 show_regions(const NodePath &render2d, const string &bin_name, int draw_order) {
251  LightMutexHolder holder(_lock);
252  do_show_regions(render2d, bin_name, draw_order);
253 }
254 #endif // NDEBUG
255 
256 #ifndef NDEBUG
257 ////////////////////////////////////////////////////////////////////
258 // Function: MouseWatcherBase::set_color
259 // Access: Published
260 // Description: Specifies the color used to draw the region
261 // rectangles for the regions visualized by
262 // show_regions().
263 ////////////////////////////////////////////////////////////////////
266  LightMutexHolder holder(_lock);
267 
268  _color = color;
269  do_update_regions();
270 }
271 #endif // NDEBUG
272 
273 #ifndef NDEBUG
274 ////////////////////////////////////////////////////////////////////
275 // Function: MouseWatcherBase::hide_regions
276 // Access: Published
277 // Description: Stops the visualization created by a previous call to
278 // show_regions().
279 ////////////////////////////////////////////////////////////////////
282  LightMutexHolder holder(_lock);
283  do_hide_regions();
284 }
285 #endif // NDEBUG
286 
287 #ifndef NDEBUG
288 ////////////////////////////////////////////////////////////////////
289 // Function: MouseWatcherBase::update_regions
290 // Access: Published
291 // Description: Refreshes the visualization created by
292 // show_regions().
293 ////////////////////////////////////////////////////////////////////
296  LightMutexHolder holder(_lock);
297  do_update_regions();
298 }
299 #endif // NDEBUG
300 
301 
302 ////////////////////////////////////////////////////////////////////
303 // Function: MouseWatcherBase::do_sort_regions
304 // Access: Protected
305 // Description: Sorts all the regions in this group into pointer
306 // order. Assumes the lock is already held.
307 ////////////////////////////////////////////////////////////////////
308 void MouseWatcherBase::
309 do_sort_regions() {
310  if (!_sorted) {
311  sort(_regions.begin(), _regions.end());
312  _sorted = true;
313  }
314 }
315 
316 ////////////////////////////////////////////////////////////////////
317 // Function: MouseWatcherBase::do_remove_region
318 // Access: Protected
319 // Description: The internal implementation of remove_region();
320 // assumes the lock is already held.
321 ////////////////////////////////////////////////////////////////////
322 bool MouseWatcherBase::
323 do_remove_region(MouseWatcherRegion *region) {
324  // See if the region is in the vector.
325  PT(MouseWatcherRegion) ptr = region;
326  Regions::iterator ri;
327 
328  if (_sorted) {
329  // Faster, binary search
330  ri = lower_bound(_regions.begin(), _regions.end(), ptr);
331  } else {
332  // Unsorted, so use slower linear scan
333  ri = find(_regions.begin(), _regions.end(), ptr);
334  }
335 
336  if (ri != _regions.end() && (*ri) == ptr) {
337  // Found it, now erase it
338 #ifndef NDEBUG
339  // Also remove it from the vizzes.
340  if (_show_regions) {
341  nassertr(_vizzes.size() == _regions.size(), false);
342  size_t index = ri - _regions.begin();
343  Vizzes::iterator vi = _vizzes.begin() + index;
344  _show_regions_root.node()->remove_child(*vi);
345  _vizzes.erase(vi);
346  }
347 #endif // NDEBUG
348 
349  _regions.erase(ri);
350  return true;
351  }
352 
353  // Did not find the region to erase
354  return false;
355 }
356 
357 #ifndef NDEBUG
358 ////////////////////////////////////////////////////////////////////
359 // Function: MouseWatcherBase::do_show_regions
360 // Access: Protected, Virtual
361 // Description: The protected implementation of show_regions(). This
362 // assumes the lock is already held.
363 ////////////////////////////////////////////////////////////////////
364 void MouseWatcherBase::
365 do_show_regions(const NodePath &render2d, const string &bin_name,
366  int draw_order) {
367  do_hide_regions();
368  _show_regions = true;
369  _show_regions_root = render2d.attach_new_node("show_regions");
370  _show_regions_root.set_bin(bin_name, draw_order);
371  do_update_regions();
372 }
373 #endif // NDEBUG
374 
375 #ifndef NDEBUG
376 ////////////////////////////////////////////////////////////////////
377 // Function: MouseWatcherBase::do_hide_regions
378 // Access: Protected, Virtual
379 // Description: The protected implementation of hide_regions(). This
380 // assumes the lock is already held.
381 ////////////////////////////////////////////////////////////////////
382 void MouseWatcherBase::
383 do_hide_regions() {
384  _show_regions_root.remove_node();
385  _show_regions = false;
386  _vizzes.clear();
387 }
388 #endif // NDEBUG
389 
390 #ifndef NDEBUG
391 ////////////////////////////////////////////////////////////////////
392 // Function: MouseWatcherBase::do_update_regions
393 // Access: Protected
394 // Description: Internally regenerates the show_regions()
395 // visualization. Assumes the lock is already held.
396 ////////////////////////////////////////////////////////////////////
397 void MouseWatcherBase::
398 do_update_regions() {
399  nassertv(_lock.debug_is_locked());
400 
401  if (_show_regions) {
402  _show_regions_root.node()->remove_all_children();
403  _vizzes.clear();
404  _vizzes.reserve(_regions.size());
405 
406  Regions::const_iterator ri;
407  for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
408  _vizzes.push_back(make_viz_region(*ri));
409  }
410  }
411 }
412 #endif // NDEBUG
413 
414 
415 #ifndef NDEBUG
416 ////////////////////////////////////////////////////////////////////
417 // Function: MouseWatcherBase::make_viz_region
418 // Access: Private
419 // Description: Creates a node to represent the indicated region, and
420 // attaches it to the _show_regions_root. Does not add
421 // it to _vizzes. Assumes the lock is already held.
422 ////////////////////////////////////////////////////////////////////
423 PandaNode *MouseWatcherBase::
424 make_viz_region(MouseWatcherRegion *region) {
425  nassertr(_lock.debug_is_locked(), NULL);
426 
427  LineSegs ls("show_regions");
428  ls.set_color(_color);
429 
430  const LVecBase4 &f = region->get_frame();
431 
432  ls.move_to(LVector3::rfu(f[0], 0.0f, f[2]));
433  ls.draw_to(LVector3::rfu(f[1], 0.0f, f[2]));
434  ls.draw_to(LVector3::rfu(f[1], 0.0f, f[3]));
435  ls.draw_to(LVector3::rfu(f[0], 0.0f, f[3]));
436  ls.draw_to(LVector3::rfu(f[0], 0.0f, f[2]));
437 
438  PT(PandaNode) node = ls.create();
439  _show_regions_root.attach_new_node(node);
440 
441  return node;
442 }
443 #endif // NDEBUG
bool is_sorted() const
Returns true if the group has already been sorted, false otherwise.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
void remove_child(int child_index, Thread *current_thread=Thread::get_current_thread())
Removes the nth child from the node.
Definition: pandaNode.cxx:697
void sort_regions()
Sorts all the regions in this group into pointer order.
bool debug_is_locked() const
Returns true if the current thread has locked the LightMutex, false otherwise.
MouseWatcherRegion * get_region(int n) const
Returns the nth region of the group; returns NULL if there is no nth region.
int get_num_regions() const
Returns the number of regions in the group.
bool remove_region(MouseWatcherRegion *region)
Removes the indicated region from the group.
void add_region(MouseWatcherRegion *region)
Adds the indicated region to the set of regions in the group.
bool has_region(MouseWatcherRegion *region) const
Returns true if the indicated region has already been added to the MouseWatcherBase, false otherwise.
static LVector3f rfu(float right, float fwd, float up, CoordinateSystem cs=CS_default)
Returns a vector that is described by its right, forward, and up components, in whatever way the coor...
Definition: lvector3.h:645
void clear_regions()
Removes all the regions from the group.
void show_regions(const NodePath &render2d, const string &bin_name, int draw_order)
Enables the visualization of all of the regions handled by this MouseWatcherBase. ...
void hide_regions()
Stops the visualization created by a previous call to show_regions().
Encapsulates creation of a series of connected or disconnected line segments or points, for drawing paths or rays.
Definition: lineSegs.h:36
void update_regions()
Refreshes the visualization created by show_regions().
void set_bin(const string &bin_name, int draw_order, int priority=0)
Assigns the geometry at this level and below to the named rendering bin.
Definition: nodePath.cxx:3263
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:723
void remove_all_children(Thread *current_thread=Thread::get_current_thread())
Removes all the children from the node at once, including stashed children.
Definition: pandaNode.cxx:990
Similar to MutexHolder, but for a light mutex.
This is the class that defines a rectangular region on the screen for the MouseWatcher.
void set_color(const LColor &color)
Specifies the color used to draw the region rectangles for the regions visualized by show_regions()...
MouseWatcherRegion * find_region(const string &name) const
Returns a pointer to the first region found with the indicated name.
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:757
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165