Panda3D
pgItem.cxx
1 // Filename: pgItem.cxx
2 // Created by: drose (13Mar02)
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 "pgItem.h"
16 #include "pgMouseWatcherParameter.h"
17 #include "pgCullTraverser.h"
18 #include "config_pgui.h"
19 #include "boundingVolume.h"
20 #include "pandaNode.h"
21 #include "sceneGraphReducer.h"
22 #include "throw_event.h"
23 #include "string_utils.h"
24 #include "nodePath.h"
25 #include "cullTraverser.h"
26 #include "cullTraverserData.h"
27 #include "cullBinManager.h"
28 #include "clipPlaneAttrib.h"
29 #include "scissorAttrib.h"
30 #include "dcast.h"
31 #include "boundingSphere.h"
32 #include "boundingBox.h"
33 #include "config_mathutil.h"
34 
35 #ifdef HAVE_AUDIO
36 #include "audioSound.h"
37 #endif
38 
39 TypeHandle PGItem::_type_handle;
40 PT(TextNode) PGItem::_text_node;
41 PGItem *PGItem::_focus_item = (PGItem *)NULL;
42 PGItem::BackgroundFocus PGItem::_background_focus;
43 
44 
45 ////////////////////////////////////////////////////////////////////
46 // Function: is_right
47 // Description: Returns true if the 2-d v1 is to the right of v2.
48 ////////////////////////////////////////////////////////////////////
49 INLINE bool
50 is_right(const LVector2 &v1, const LVector2 &v2) {
51  return (v1[0] * v2[1] - v1[1] * v2[0]) > 0;
52 }
53 
54 ////////////////////////////////////////////////////////////////////
55 // Function: PGItem::Constructor
56 // Access: Published
57 // Description:
58 ////////////////////////////////////////////////////////////////////
59 PGItem::
60 PGItem(const string &name) :
61  PandaNode(name),
62  _lock(name)
63 {
64  set_cull_callback();
65 
66  _notify = NULL;
67  _has_frame = false;
68  _frame.set(0, 0, 0, 0);
69  _region = new PGMouseWatcherRegion(this);
70  _state = 0;
71  _flags = 0;
72 }
73 
74 ////////////////////////////////////////////////////////////////////
75 // Function: PGItem::Destructor
76 // Access: Public, Virtual
77 // Description:
78 ////////////////////////////////////////////////////////////////////
79 PGItem::
80 ~PGItem() {
81  if (_notify != (PGItemNotify *)NULL) {
82  _notify->remove_item(this);
83  _notify = NULL;
84  }
85 
86  nassertv(_region->_item == this);
87  _region->_item = (PGItem *)NULL;
88 
89  set_background_focus(false);
90  if (_focus_item == this) {
91  _focus_item = (PGItem *)NULL;
92  }
93 }
94 
95 ////////////////////////////////////////////////////////////////////
96 // Function: PGItem::Copy Constructor
97 // Access: Public
98 // Description:
99 ////////////////////////////////////////////////////////////////////
100 PGItem::
101 PGItem(const PGItem &copy) :
102  PandaNode(copy),
103  _has_frame(copy._has_frame),
104  _frame(copy._frame),
105  _state(copy._state),
106  _flags(copy._flags)
107 #ifdef HAVE_AUDIO
108  , _sounds(copy._sounds)
109 #endif
110 {
111  _notify = NULL;
112  _region = new PGMouseWatcherRegion(this);
113 
114  // We give our region the same name as the region for the PGItem
115  // we're copying--so that this PGItem will generate the same event
116  // names when the user interacts with it.
117  _region->set_name(copy._region->get_name());
118 
119  // Make a deep copy of all of the original PGItem's StateDefs.
120  size_t num_state_defs = copy._state_defs.size();
121  _state_defs.reserve(num_state_defs);
122  for (size_t i = 0; i < num_state_defs; ++i) {
123  // We cheat and cast away the const, because the frame is just a
124  // cache. But we have to get the frame out of the source before we
125  // can safely copy it.
126  StateDef &old_sd = (StateDef &)(copy._state_defs[i]);
127  old_sd._frame.remove_node();
128  old_sd._frame_stale = true;
129 
130  StateDef new_sd;
131  new_sd._root = old_sd._root.copy_to(NodePath());
132  new_sd._frame_style = old_sd._frame_style;
133 
134  _state_defs.push_back(new_sd);
135  }
136 }
137 
138 ////////////////////////////////////////////////////////////////////
139 // Function: PGItem::make_copy
140 // Access: Protected, Virtual
141 // Description: Returns a newly-allocated Node that is a shallow copy
142 // of this one. It will be a different Node pointer,
143 // but its internal data may or may not be shared with
144 // that of the original Node.
145 ////////////////////////////////////////////////////////////////////
146 PandaNode *PGItem::
147 make_copy() const {
148  LightReMutexHolder holder(_lock);
149  return new PGItem(*this);
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: PGItem::transform_changed
154 // Access: Protected, Virtual
155 // Description: Called after the node's transform has been changed
156 // for any reason, this just provides a hook so derived
157 // classes can do something special in this case.
158 ////////////////////////////////////////////////////////////////////
159 void PGItem::
160 transform_changed() {
161  LightReMutexHolder holder(_lock);
162  PandaNode::transform_changed();
163  if (has_notify()) {
164  get_notify()->item_transform_changed(this);
165  }
166 }
167 
168 ////////////////////////////////////////////////////////////////////
169 // Function: PGItem::draw_mask_changed
170 // Access: Protected, Virtual
171 // Description: Called after the node's draw_mask has been changed
172 // for any reason, this just provides a hook so derived
173 // classes can do something special in this case.
174 ////////////////////////////////////////////////////////////////////
175 void PGItem::
176 draw_mask_changed() {
177  LightReMutexHolder holder(_lock);
178  PandaNode::draw_mask_changed();
179  if (has_notify()) {
180  get_notify()->item_draw_mask_changed(this);
181  }
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: PGItem::cull_callback
186 // Access: Protected, Virtual
187 // Description: This function will be called during the cull
188 // traversal to perform any additional operations that
189 // should be performed at cull time. This may include
190 // additional manipulation of render state or additional
191 // visible/invisible decisions, or any other arbitrary
192 // operation.
193 //
194 // Note that this function will *not* be called unless
195 // set_cull_callback() is called in the constructor of
196 // the derived class. It is necessary to call
197 // set_cull_callback() to indicated that we require
198 // cull_callback() to be called.
199 //
200 // By the time this function is called, the node has
201 // already passed the bounding-volume test for the
202 // viewing frustum, and the node's transform and state
203 // have already been applied to the indicated
204 // CullTraverserData object.
205 //
206 // The return value is true if this node should be
207 // visible, or false if it should be culled.
208 ////////////////////////////////////////////////////////////////////
209 bool PGItem::
210 cull_callback(CullTraverser *trav, CullTraverserData &data) {
211  LightReMutexHolder holder(_lock);
212  bool this_node_hidden = data.is_this_node_hidden(trav->get_camera_mask());
213  if (!this_node_hidden && has_frame() && get_active()) {
214  // The item has a frame, so we want to generate a region for it
215  // and update the MouseWatcher.
216 
217  // We can only do this if our traverser is a PGCullTraverser
218  // (which will be the case if this node was parented somewhere
219  // under a PGTop node).
220  if (trav->is_exact_type(PGCullTraverser::get_class_type())) {
221  PGCullTraverser *pg_trav;
222  DCAST_INTO_R(pg_trav, trav, true);
223 
224  CPT(TransformState) net_transform = data.get_net_transform(trav);
225  const LMatrix4 &transform = net_transform->get_mat();
226 
227  // Consider the cull bin this object is in. Since the binning
228  // affects the render order, we want bins that render later to
229  // get higher sort values.
230  int bin_index = data._state->get_bin_index();
231  int sort;
232 
234  CullBinManager::BinType bin_type = bin_manager->get_bin_type(bin_index);
235  if (bin_type == CullBinManager::BT_fixed) {
236  // If the bin is a "fixed" type bin, our local sort is based
237  // on the fixed order.
238  sort = data._state->get_draw_order();
239 
240  } else if (bin_type == CullBinManager::BT_unsorted) {
241  // If the bin is an "unsorted" type bin, we base the local
242  // sort on the scene graph order.
243  sort = pg_trav->_sort_index;
244  pg_trav->_sort_index++;
245 
246  } else {
247  // Otherwise, the local sort is irrelevant.
248  sort = 0;
249  }
250 
251  // Now what order does this bin sort relative to the other bins?
252  // This becomes the high-order part of the final sort count.
253  int bin_sort = bin_manager->get_bin_sort(data._state->get_bin_index());
254 
255  // Combine the two sorts into a single int. This assumes we
256  // only need 16 bits for each sort number, possibly an erroneous
257  // assumption. We should really provide two separate sort
258  // values, both ints, in the MouseWatcherRegion; but in the
259  // interest of expediency we work within the existing interface
260  // which only provides one.
261  sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
262 
263  if (activate_region(transform, sort,
264  DCAST(ClipPlaneAttrib, data._state->get_attrib(ClipPlaneAttrib::get_class_slot())),
265  DCAST(ScissorAttrib, data._state->get_attrib(ScissorAttrib::get_class_slot())))) {
266  pg_trav->_top->add_region(get_region());
267  }
268  }
269  }
270 
271  if (has_state_def(get_state())) {
272  // This item has a current state definition that we should use
273  // to render the item.
274  NodePath &root = get_state_def(get_state());
275  CullTraverserData next_data(data, root.node());
276  trav->traverse(next_data);
277  }
278 
279  // Now continue to render everything else below this node.
280  return true;
281 }
282 
283 ////////////////////////////////////////////////////////////////////
284 // Function: PGItem::is_renderable
285 // Access: Public, Virtual
286 // Description: Returns true if there is some value to visiting this
287 // particular node during the cull traversal for any
288 // camera, false otherwise. This will be used to
289 // optimize the result of get_net_draw_show_mask(), so
290 // that any subtrees that contain only nodes for which
291 // is_renderable() is false need not be visited.
292 ////////////////////////////////////////////////////////////////////
293 bool PGItem::
294 is_renderable() const {
295  return true;
296 }
297 
298 ////////////////////////////////////////////////////////////////////
299 // Function: PGItem::compute_internal_bounds
300 // Access: Protected, Virtual
301 // Description: Called when needed to recompute the node's
302 // _internal_bound object. Nodes that contain anything
303 // of substance should redefine this to do the right
304 // thing.
305 ////////////////////////////////////////////////////////////////////
306 void PGItem::
307 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
308  int &internal_vertices,
309  int pipeline_stage,
310  Thread *current_thread) const {
311  LightReMutexHolder holder(_lock, current_thread);
312  int num_vertices = 0;
313 
314  // First, get ourselves a fresh, empty bounding volume.
315  PT(BoundingVolume) bound;
316 
317  BoundingVolume::BoundsType btype = get_bounds_type();
318  if (btype == BoundingVolume::BT_default) {
319  btype = bounds_type;
320  }
321 
322  if (btype == BoundingVolume::BT_sphere) {
323  bound = new BoundingSphere;
324  } else {
325  bound = new BoundingBox;
326  }
327 
328  // Now actually compute the bounding volume by putting it around all
329  // of our states' bounding volumes.
330  pvector<const BoundingVolume *> child_volumes;
331 
332  // We walk through the list of state defs indirectly, calling
333  // get_state_def() on each one, to ensure that the frames are
334  // updated correctly before we measure their bounding volumes.
335  for (int i = 0; i < (int)_state_defs.size(); i++) {
336  NodePath &root = ((PGItem *)this)->get_state_def(i);
337  if (!root.is_empty()) {
338  PandaNode *node = root.node();
339  child_volumes.push_back(node->get_bounds(current_thread));
340  num_vertices += node->get_nested_vertices(current_thread);
341  }
342  }
343 
344  const BoundingVolume **child_begin = &child_volumes[0];
345  const BoundingVolume **child_end = child_begin + child_volumes.size();
346 
347  bound->around(child_begin, child_end);
348 
349  internal_bounds = bound;
350  internal_vertices = num_vertices;
351 }
352 
353 ////////////////////////////////////////////////////////////////////
354 // Function: PGItem::r_prepare_scene
355 // Access: Protected, Virtual
356 // Description: The recursive implementation of prepare_scene().
357 // Don't call this directly; call
358 // PandaNode::prepare_scene() or
359 // NodePath::prepare_scene() instead.
360 ////////////////////////////////////////////////////////////////////
361 void PGItem::
362 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
363  GeomTransformer &transformer, Thread *current_thread) {
364  LightReMutexHolder holder(_lock);
365  StateDefs::iterator di;
366  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
367  NodePath &root = (*di)._root;
368  if (!root.is_empty()) {
369  PandaNode *child = root.node();
370  CPT(RenderState) child_state = node_state->compose(child->get_state());
371  child->r_prepare_scene(gsg, child_state, transformer, current_thread);
372  }
373  }
374 
375  PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread);
376 }
377 
378 ////////////////////////////////////////////////////////////////////
379 // Function: PGItem::xform
380 // Access: Public, Virtual
381 // Description: Transforms the contents of this node by the indicated
382 // matrix, if it means anything to do so. For most
383 // kinds of nodes, this does nothing.
384 ////////////////////////////////////////////////////////////////////
385 void PGItem::
386 xform(const LMatrix4 &mat) {
387  LightReMutexHolder holder(_lock);
388  // Transform the frame.
389  LPoint3 ll(_frame[0], 0.0f, _frame[2]);
390  LPoint3 ur(_frame[1], 0.0f, _frame[3]);
391  ll = ll * mat;
392  ur = ur * mat;
393  _frame.set(ll[0], ur[0], ll[2], ur[2]);
394 
395  // Transform the individual states and their frame styles.
396  StateDefs::iterator di;
397  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
398  NodePath &root = (*di)._root;
399  // Apply the matrix to the previous transform.
400  root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
401 
402  // Now flatten the transform into the subgraph.
404  gr.apply_attribs(root.node());
405 
406  // Transform the frame style too.
407  if ((*di)._frame_style.xform(mat)) {
408  (*di)._frame_stale = true;
409  }
410  }
411  mark_internal_bounds_stale();
412 }
413 
414 ////////////////////////////////////////////////////////////////////
415 // Function: PGItem::activate_region
416 // Access: Public
417 // Description: Applies the indicated scene graph transform and order
418 // as determined by the traversal from PGTop.
419 //
420 // The return value is true if the region is valid, or
421 // false if it is empty or completely clipped.
422 ////////////////////////////////////////////////////////////////////
423 bool PGItem::
424 activate_region(const LMatrix4 &transform, int sort,
425  const ClipPlaneAttrib *cpa,
426  const ScissorAttrib *sa) {
427  LightReMutexHolder holder(_lock);
428  // Transform all four vertices, and get the new bounding box. This
429  // way the region works (mostly) even if has been rotated.
430  LPoint3 ll = LPoint3::rfu(_frame[0], 0.0f, _frame[2]) * transform;
431  LPoint3 lr = LPoint3::rfu(_frame[1], 0.0f, _frame[2]) * transform;
432  LPoint3 ul = LPoint3::rfu(_frame[0], 0.0f, _frame[3]) * transform;
433  LPoint3 ur = LPoint3::rfu(_frame[1], 0.0f, _frame[3]) * transform;
434  LVector3 up = LVector3::up();
435  int up_axis;
436  if (up[1]) {
437  up_axis = 1;
438  }
439  else if (up[2]) {
440  up_axis = 2;
441  }
442  else {
443  up_axis = 0;
444  }
445  LVector3 right = LVector3::right();
446  int right_axis;
447  if (right[0]) {
448  right_axis = 0;
449  }
450  else if (right[2]) {
451  right_axis = 2;
452  }
453  else {
454  right_axis = 1;
455  }
456 
457  LVecBase4 frame;
458  if (cpa != (ClipPlaneAttrib *)NULL && cpa->get_num_on_planes() != 0) {
459  // Apply the clip plane(s) and/or scissor region now that we are
460  // here in world space.
461 
462  ClipPoints points;
463  points.reserve(4);
464  points.push_back(LPoint2(ll[right_axis], ll[up_axis]));
465  points.push_back(LPoint2(lr[right_axis], lr[up_axis]));
466  points.push_back(LPoint2(ur[right_axis], ur[up_axis]));
467  points.push_back(LPoint2(ul[right_axis], ul[up_axis]));
468 
469  int num_on_planes = cpa->get_num_on_planes();
470  for (int i = 0; i < num_on_planes; ++i) {
471  NodePath plane_path = cpa->get_on_plane(i);
472  LPlane plane = DCAST(PlaneNode, plane_path.node())->get_plane();
473  plane.xform(plane_path.get_net_transform()->get_mat());
474 
475  // We ignore the forward axis, assuming the frame is still in
476  // the right-up plane after being transformed. Not sure if we really
477  // need to support general 3-D transforms on 2-D objects.
478  clip_frame(points, plane);
479  }
480 
481  if (points.empty()) {
482  // Turns out it's completely clipped after all.
483  return false;
484  }
485 
486  ClipPoints::iterator pi;
487  pi = points.begin();
488  frame.set((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]);
489  ++pi;
490  while (pi != points.end()) {
491  frame[0] = min(frame[0], (*pi)[0]);
492  frame[1] = max(frame[1], (*pi)[0]);
493  frame[2] = min(frame[2], (*pi)[1]);
494  frame[3] = max(frame[3], (*pi)[1]);
495  ++pi;
496  }
497  } else {
498  // Since there are no clip planes involved, just set the frame.
499  frame.set(min(min(ll[right_axis], lr[right_axis]), min(ul[right_axis], ur[right_axis])),
500  max(max(ll[right_axis], lr[right_axis]), max(ul[right_axis], ur[right_axis])),
501  min(min(ll[up_axis], lr[up_axis]), min(ul[up_axis], ur[up_axis])),
502  max(max(ll[up_axis], lr[up_axis]), max(ul[up_axis], ur[up_axis])));
503  }
504 
505  if (sa != (ScissorAttrib *)NULL) {
506  // Also restrict it to within the scissor region.
507  const LVecBase4 &sf = sa->get_frame();
508  // Expand sf from 0..1 to -1..1.
509  frame.set(max(frame[0], sf[0] * 2.0f - 1.0f),
510  min(frame[1], sf[1] * 2.0f - 1.0f),
511  max(frame[2], sf[2] * 2.0f - 1.0f),
512  min(frame[3], sf[3] * 2.0f - 1.0f));
513  if (frame[1] <= frame[0] || frame[3] <= frame[2]) {
514  // Completely outside the scissor region.
515  return false;
516  }
517  }
518 
519  _region->set_frame(frame);
520 
521  _region->set_sort(sort);
522  _region->set_active(true);
523 
524  // calculate the inverse of this transform, which is needed to
525  // go back to the frame space.
526  _frame_inv_xform.invert_from(transform);
527 
528  return true;
529 }
530 
531 ////////////////////////////////////////////////////////////////////
532 // Function: PGItem::enter_region
533 // Access: Public, Virtual
534 // Description: This is a callback hook function, called whenever the
535 // mouse enters the region. The mouse is only
536 // considered to be "entered" in one region at a time;
537 // in the case of nested regions, it exits the outer
538 // region before entering the inner one.
539 ////////////////////////////////////////////////////////////////////
540 void PGItem::
542  LightReMutexHolder holder(_lock);
543  if (pgui_cat.is_debug()) {
544  pgui_cat.debug()
545  << *this << "::enter_region(" << param << ")\n";
546  }
547 
549  string event = get_enter_event();
550  play_sound(event);
551  throw_event(event, EventParameter(ep));
552 
553  if (has_notify()) {
554  get_notify()->item_enter(this, param);
555  }
556 }
557 
558 ////////////////////////////////////////////////////////////////////
559 // Function: PGItem::exit_region
560 // Access: Public, Virtual
561 // Description: This is a callback hook function, called whenever the
562 // mouse exits the region. The mouse is only considered
563 // to be "entered" in one region at a time; in the case
564 // of nested regions, it exits the outer region before
565 // entering the inner one.
566 ////////////////////////////////////////////////////////////////////
567 void PGItem::
569  LightReMutexHolder holder(_lock);
570  if (pgui_cat.is_debug()) {
571  pgui_cat.debug()
572  << *this << "::exit_region(" << param << ")\n";
573  }
574 
576  string event = get_exit_event();
577  play_sound(event);
578  throw_event(event, EventParameter(ep));
579 
580  if (has_notify()) {
581  get_notify()->item_exit(this, param);
582  }
583 
584  //pgui_cat.info() << get_name() << "::exit()" << endl;
585 }
586 
587 ////////////////////////////////////////////////////////////////////
588 // Function: PGItem::within_region
589 // Access: Public, Virtual
590 // Description: This is a callback hook function, called whenever the
591 // mouse moves within the boundaries of the region, even
592 // if it is also within the boundaries of a nested
593 // region. This is different from "enter", which is
594 // only called whenever the mouse is within only that
595 // region.
596 ////////////////////////////////////////////////////////////////////
597 void PGItem::
599  LightReMutexHolder holder(_lock);
600  if (pgui_cat.is_debug()) {
601  pgui_cat.debug()
602  << *this << "::within_region(" << param << ")\n";
603  }
604 
606  string event = get_within_event();
607  play_sound(event);
608  throw_event(event, EventParameter(ep));
609 
610  if (has_notify()) {
611  get_notify()->item_within(this, param);
612  }
613 }
614 
615 ////////////////////////////////////////////////////////////////////
616 // Function: PGItem::without_region
617 // Access: Public, Virtual
618 // Description: This is a callback hook function, called whenever the
619 // mouse moves completely outside the boundaries of the
620 // region. See within().
621 ////////////////////////////////////////////////////////////////////
622 void PGItem::
624  LightReMutexHolder holder(_lock);
625  if (pgui_cat.is_debug()) {
626  pgui_cat.debug()
627  << *this << "::without_region(" << param << ")\n";
628  }
629 
631  string event = get_without_event();
632  play_sound(event);
633  throw_event(event, EventParameter(ep));
634 
635  if (has_notify()) {
636  get_notify()->item_without(this, param);
637  }
638 }
639 
640 ////////////////////////////////////////////////////////////////////
641 // Function: PGItem::focus_in
642 // Access: Public, Virtual
643 // Description: This is a callback hook function, called whenever the
644 // widget gets the keyboard focus.
645 ////////////////////////////////////////////////////////////////////
646 void PGItem::
648  LightReMutexHolder holder(_lock);
649  if (pgui_cat.is_debug()) {
650  pgui_cat.debug()
651  << *this << "::focus_in()\n";
652  }
653 
654  string event = get_focus_in_event();
655  play_sound(event);
656  throw_event(event);
657 
658  if (has_notify()) {
659  get_notify()->item_focus_in(this);
660  }
661 }
662 
663 ////////////////////////////////////////////////////////////////////
664 // Function: PGItem::focus_out
665 // Access: Public, Virtual
666 // Description: This is a callback hook function, called whenever the
667 // widget loses the keyboard focus.
668 ////////////////////////////////////////////////////////////////////
669 void PGItem::
671  LightReMutexHolder holder(_lock);
672  if (pgui_cat.is_debug()) {
673  pgui_cat.debug()
674  << *this << "::focus_out()\n";
675  }
676 
677  string event = get_focus_out_event();
678  play_sound(event);
679  throw_event(event);
680 
681  if (has_notify()) {
682  get_notify()->item_focus_out(this);
683  }
684 }
685 
686 ////////////////////////////////////////////////////////////////////
687 // Function: PGItem::press
688 // Access: Public, Virtual
689 // Description: This is a callback hook function, called whenever a
690 // mouse or keyboard button is depressed while the mouse
691 // is within the region.
692 ////////////////////////////////////////////////////////////////////
693 void PGItem::
694 press(const MouseWatcherParameter &param, bool background) {
695  LightReMutexHolder holder(_lock);
696  if (pgui_cat.is_debug()) {
697  pgui_cat.debug()
698  << *this << "::press(" << param << ", " << background << ")\n";
699  }
700 
701  if (!background) {
703  string event;
704  if (param.is_keyrepeat()) {
705  event = get_repeat_event(param.get_button());
706  } else {
707  event = get_press_event(param.get_button());
708  }
709  play_sound(event);
710  throw_event(event, EventParameter(ep));
711  }
712 
713  if (has_notify()) {
714  get_notify()->item_press(this, param);
715  }
716 }
717 
718 ////////////////////////////////////////////////////////////////////
719 // Function: PGItem::release
720 // Access: Public, Virtual
721 // Description: This is a callback hook function, called whenever a
722 // mouse or keyboard button previously depressed with
723 // press() is released.
724 ////////////////////////////////////////////////////////////////////
725 void PGItem::
726 release(const MouseWatcherParameter &param, bool background) {
727  LightReMutexHolder holder(_lock);
728  if (pgui_cat.is_debug()) {
729  pgui_cat.debug()
730  << *this << "::release(" << param << ", " << background << ")\n";
731  }
732 
733  if (!background) {
735  string event = get_release_event(param.get_button());
736  play_sound(event);
737  throw_event(event, EventParameter(ep));
738  }
739 
740  if (has_notify()) {
741  get_notify()->item_release(this, param);
742  }
743 }
744 
745 ////////////////////////////////////////////////////////////////////
746 // Function: PGItem::keystroke
747 // Access: Public, Virtual
748 // Description: This is a callback hook function, called whenever
749 // the user presses a key.
750 ////////////////////////////////////////////////////////////////////
751 void PGItem::
752 keystroke(const MouseWatcherParameter &param, bool background) {
753  LightReMutexHolder holder(_lock);
754  if (pgui_cat.is_debug()) {
755  pgui_cat.debug()
756  << *this << "::keystroke(" << param << ", " << background << ")\n";
757  }
758 
759  if (!background) {
761  string event = get_keystroke_event();
762  play_sound(event);
763  throw_event(event, EventParameter(ep));
764 
765  if (has_notify()) {
766  get_notify()->item_keystroke(this, param);
767  }
768  }
769 }
770 
771 ////////////////////////////////////////////////////////////////////
772 // Function: PGItem::candidate
773 // Access: Public, Virtual
774 // Description: This is a callback hook function, called whenever
775 // the user highlights an option in the IME window.
776 ////////////////////////////////////////////////////////////////////
777 void PGItem::
778 candidate(const MouseWatcherParameter &param, bool background) {
779  LightReMutexHolder holder(_lock);
780  if (pgui_cat.is_debug()) {
781  pgui_cat.debug()
782  << *this << "::candidate(" << param << ", " << background << ")\n";
783  }
784 
785  // We don't throw sound events for candidate selections for now.
786  if (!background) {
787  if (has_notify()) {
788  get_notify()->item_candidate(this, param);
789  }
790  }
791 }
792 
793 ////////////////////////////////////////////////////////////////////
794 // Function: PGItem::move
795 // Access: Public, Virtual
796 // Description: This is a callback hook function, called whenever a
797 // mouse is moved while within the region.
798 ////////////////////////////////////////////////////////////////////
799 void PGItem::
801  LightReMutexHolder holder(_lock);
802  if (pgui_cat.is_debug()) {
803  pgui_cat.debug()
804  << *this << "::move(" << param << ")\n";
805  }
806 
807  if (has_notify()) {
808  get_notify()->item_move(this, param);
809  }
810 }
811 
812 ////////////////////////////////////////////////////////////////////
813 // Function: PGItem::background_press
814 // Access: Public, Static
815 // Description: Calls press() on all the PGItems with background
816 // focus.
817 ////////////////////////////////////////////////////////////////////
818 void PGItem::
820  BackgroundFocus::const_iterator fi;
821  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
822  PGItem *item = *fi;
823  if (!item->get_focus()) {
824  item->press(param, true);
825  }
826  }
827 }
828 
829 ////////////////////////////////////////////////////////////////////
830 // Function: PGItem::background_release
831 // Access: Public, Static
832 // Description: Calls release() on all the PGItems with background
833 // focus.
834 ////////////////////////////////////////////////////////////////////
835 void PGItem::
837  BackgroundFocus::const_iterator fi;
838  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
839  PGItem *item = *fi;
840  if (!item->get_focus()) {
841  item->release(param, true);
842  }
843  }
844 }
845 
846 ////////////////////////////////////////////////////////////////////
847 // Function: PGItem::background_keystroke
848 // Access: Public, Static
849 // Description: Calls keystroke() on all the PGItems with background
850 // focus.
851 ////////////////////////////////////////////////////////////////////
852 void PGItem::
854  BackgroundFocus::const_iterator fi;
855  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
856  PGItem *item = *fi;
857  if (!item->get_focus()) {
858  item->keystroke(param, true);
859  }
860  }
861 }
862 
863 ////////////////////////////////////////////////////////////////////
864 // Function: PGItem::background_candidate
865 // Access: Public, Static
866 // Description: Calls candidate() on all the PGItems with background
867 // focus.
868 ////////////////////////////////////////////////////////////////////
869 void PGItem::
871  BackgroundFocus::const_iterator fi;
872  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
873  PGItem *item = *fi;
874  if (!item->get_focus()) {
875  item->candidate(param, true);
876  }
877  }
878 }
879 
880 ////////////////////////////////////////////////////////////////////
881 // Function: PGItem::set_active
882 // Access: Published, Virtual
883 // Description: Sets whether the PGItem is active for mouse watching.
884 // This is not necessarily related to the
885 // active/inactive appearance of the item, which is
886 // controlled by set_state(), but it does affect whether
887 // it responds to mouse events.
888 ////////////////////////////////////////////////////////////////////
889 void PGItem::
890 set_active(bool active) {
891  LightReMutexHolder holder(_lock);
892  if (active) {
893  _flags |= F_active;
894  } else {
895  _flags &= ~F_active;
896  // Deactivating the item automatically defocuses it too.
897  if (get_focus()) {
898  set_focus(false);
899  }
900  }
901 }
902 
903 ////////////////////////////////////////////////////////////////////
904 // Function: PGItem::set_focus
905 // Access: Published, Virtual
906 // Description: Sets whether the PGItem currently has keyboard focus.
907 // This simply means that the item may respond to
908 // keyboard events as well as to mouse events; precisely
909 // what this means is up to the individual item.
910 //
911 // Only one PGItem in the world is allowed to have focus
912 // at any given time. Setting the focus on any other
913 // item automatically disables the focus from the
914 // previous item.
915 ////////////////////////////////////////////////////////////////////
916 void PGItem::
917 set_focus(bool focus) {
918  LightReMutexHolder holder(_lock);
919  if (focus) {
920  if (!get_active()) {
921  // Cannot set focus on an inactive item.
922  return;
923  }
924 
925  // Set the keyboard focus to this item.
926  if (_focus_item != this) {
927  if (_focus_item != (PGItem *)NULL) {
928  // Clear the focus from whatever item currently has it.
929  _focus_item->set_focus(false);
930  }
931  _focus_item = this;
932  }
933  if (!get_focus()) {
934  focus_in();
935  _flags |= F_focus;
936  }
937 
938  } else {
939  if (_focus_item == this) {
940  // Remove this item from the focus.
941  _focus_item = (PGItem *)NULL;
942  }
943 
944  if (get_focus()) {
945  focus_out();
946  _flags &= ~F_focus;
947  }
948  }
949  _region->set_keyboard(focus);
950 }
951 
952 ////////////////////////////////////////////////////////////////////
953 // Function: PGItem::set_background_focus
954 // Access: Published
955 // Description: Sets the background_focus flag for this item. When
956 // background_focus is enabled, the item will receive
957 // keypress events even if it is not in focus; in fact,
958 // even if it is not onscreen. Unlike normal focus,
959 // many items may have background_focus simultaneously.
960 ////////////////////////////////////////////////////////////////////
961 void PGItem::
962 set_background_focus(bool focus) {
963  LightReMutexHolder holder(_lock);
964  if (focus != get_background_focus()) {
965  if (focus) {
966  // Activate background focus.
967  _flags |= F_background_focus;
968  bool inserted = _background_focus.insert(this).second;
969  nassertv(inserted);
970 
971  } else {
972  // Deactivate background focus.
973  _flags &= ~F_background_focus;
974  size_t num_erased = _background_focus.erase(this);
975  nassertv(num_erased == 1);
976  }
977  }
978 }
979 
980 ////////////////////////////////////////////////////////////////////
981 // Function: PGItem::get_num_state_defs
982 // Access: Published
983 // Description: Returns one more than the highest-numbered state def
984 // that was ever assigned to the PGItem. The complete
985 // set of state defs assigned may then be retrieved by
986 // indexing from 0 to (get_num_state_defs() - 1).
987 //
988 // This is only an upper limit on the actual number of
989 // state defs, since there may be holes in the list.
990 ////////////////////////////////////////////////////////////////////
991 int PGItem::
993  LightReMutexHolder holder(_lock);
994  return _state_defs.size();
995 }
996 
997 ////////////////////////////////////////////////////////////////////
998 // Function: PGItem::has_state_def
999 // Access: Published
1000 // Description: Returns true if get_state_def() has ever been called
1001 // for the indicated state (thus defining a render
1002 // subgraph for this state index), false otherwise.
1003 ////////////////////////////////////////////////////////////////////
1004 bool PGItem::
1005 has_state_def(int state) const {
1006  LightReMutexHolder holder(_lock);
1007  if (state < 0 || state >= (int)_state_defs.size()) {
1008  return false;
1009  }
1010  return (!_state_defs[state]._root.is_empty());
1011 }
1012 
1013 ////////////////////////////////////////////////////////////////////
1014 // Function: PGItem::clear_state_def
1015 // Access: Published
1016 // Description: Resets the NodePath assigned to the indicated state
1017 // to its initial default, with only a frame
1018 // representation if appropriate.
1019 ////////////////////////////////////////////////////////////////////
1020 void PGItem::
1021 clear_state_def(int state) {
1022  LightReMutexHolder holder(_lock);
1023  if (state < 0 || state >= (int)_state_defs.size()) {
1024  return;
1025  }
1026 
1027  _state_defs[state]._root = NodePath();
1028  _state_defs[state]._frame = NodePath();
1029  _state_defs[state]._frame_stale = true;
1030 
1031  mark_internal_bounds_stale();
1032 }
1033 
1034 ////////////////////////////////////////////////////////////////////
1035 // Function: PGItem::get_state_def
1036 // Access: Published
1037 // Description: Returns the Node that is the root of the subgraph
1038 // that will be drawn when the PGItem is in the
1039 // indicated state. The first time this is called for a
1040 // particular state index, it may create the Node.
1041 ////////////////////////////////////////////////////////////////////
1043 get_state_def(int state) {
1044  LightReMutexHolder holder(_lock);
1045  nassertr(state >= 0 && state < 1000, get_state_def(0)); // Sanity check.
1046  slot_state_def(state);
1047 
1048  if (_state_defs[state]._root.is_empty()) {
1049  // Create a new node.
1050  _state_defs[state]._root = NodePath("state_" + format_string(state));
1051  _state_defs[state]._frame_stale = true;
1052  }
1053 
1054  if (_state_defs[state]._frame_stale) {
1055  update_frame(state);
1056  }
1057 
1058  return _state_defs[state]._root;
1059 }
1060 
1061 ////////////////////////////////////////////////////////////////////
1062 // Function: PGItem::instance_to_state_def
1063 // Access: Published
1064 // Description: Parents an instance of the bottom node of the
1065 // indicated NodePath to the indicated state index.
1066 ////////////////////////////////////////////////////////////////////
1068 instance_to_state_def(int state, const NodePath &path) {
1069  LightReMutexHolder holder(_lock);
1070  if (path.is_empty()) {
1071  // If the source is empty, quietly do nothing.
1072  return NodePath();
1073  }
1074 
1075  mark_internal_bounds_stale();
1076 
1077  return path.instance_to(get_state_def(state));
1078 }
1079 
1080 ////////////////////////////////////////////////////////////////////
1081 // Function: PGItem::get_frame_style
1082 // Access: Published
1083 // Description: Returns the kind of frame that will be drawn behind
1084 // the item when it is in the indicated state.
1085 ////////////////////////////////////////////////////////////////////
1087 get_frame_style(int state) {
1088  LightReMutexHolder holder(_lock);
1089  if (state < 0 || state >= (int)_state_defs.size()) {
1090  return PGFrameStyle();
1091  }
1092  return _state_defs[state]._frame_style;
1093 }
1094 
1095 ////////////////////////////////////////////////////////////////////
1096 // Function: PGItem::set_frame_style
1097 // Access: Published
1098 // Description: Changes the kind of frame that will be drawn behind
1099 // the item when it is in the indicated state.
1100 ////////////////////////////////////////////////////////////////////
1101 void PGItem::
1102 set_frame_style(int state, const PGFrameStyle &style) {
1103  LightReMutexHolder holder(_lock);
1104  // Get the state def node, mainly to ensure that this state is
1105  // slotted and listed as having been defined.
1106  NodePath &root = get_state_def(state);
1107  nassertv(!root.is_empty());
1108 
1109  _state_defs[state]._frame_style = style;
1110  _state_defs[state]._frame_stale = true;
1111 
1112  mark_internal_bounds_stale();
1113 }
1114 
1115 #ifdef HAVE_AUDIO
1116 ////////////////////////////////////////////////////////////////////
1117 // Function: PGItem::set_sound
1118 // Access: Published
1119 // Description: Sets the sound that will be played whenever the
1120 // indicated event occurs.
1121 ////////////////////////////////////////////////////////////////////
1122 void PGItem::
1123 set_sound(const string &event, AudioSound *sound) {
1124  LightReMutexHolder holder(_lock);
1125  _sounds[event] = sound;
1126 }
1127 
1128 ////////////////////////////////////////////////////////////////////
1129 // Function: PGItem::clear_sound
1130 // Access: Published
1131 // Description: Removes the sound associated with the indicated
1132 // event.
1133 ////////////////////////////////////////////////////////////////////
1134 void PGItem::
1135 clear_sound(const string &event) {
1136  LightReMutexHolder holder(_lock);
1137  _sounds.erase(event);
1138 }
1139 
1140 ////////////////////////////////////////////////////////////////////
1141 // Function: PGItem::get_sound
1142 // Access: Published
1143 // Description: Returns the sound associated with the indicated
1144 // event, or NULL if there is no associated sound.
1145 ////////////////////////////////////////////////////////////////////
1146 AudioSound *PGItem::
1147 get_sound(const string &event) const {
1148  LightReMutexHolder holder(_lock);
1149  Sounds::const_iterator si = _sounds.find(event);
1150  if (si != _sounds.end()) {
1151  return (*si).second;
1152  }
1153  return (AudioSound *)NULL;
1154 }
1155 
1156 ////////////////////////////////////////////////////////////////////
1157 // Function: PGItem::has_sound
1158 // Access: Published
1159 // Description: Returns true if there is a sound associated with the
1160 // indicated event, or false otherwise.
1161 ////////////////////////////////////////////////////////////////////
1162 bool PGItem::
1163 has_sound(const string &event) const {
1164  LightReMutexHolder holder(_lock);
1165  return (_sounds.count(event) != 0);
1166 }
1167 #endif // HAVE_AUDIO
1168 
1169 ////////////////////////////////////////////////////////////////////
1170 // Function: PGItem::get_text_node
1171 // Access: Published, Static
1172 // Description: Returns the TextNode object that will be used by all
1173 // PGItems to generate default labels given a string.
1174 // This can be loaded with the default font, etc.
1175 ////////////////////////////////////////////////////////////////////
1178  if (_text_node == (TextNode *)NULL) {
1179  _text_node = new TextNode("pguiText");
1180  _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
1181 
1182  // The default TextNode is aligned to the left, for the
1183  // convenience of PGEntry.
1184  _text_node->set_align(TextNode::A_left);
1185  }
1186  return _text_node;
1187 }
1188 
1189 ////////////////////////////////////////////////////////////////////
1190 // Function: PGItem::play_sound
1191 // Access: Protected
1192 // Description: Plays the sound associated with the indicated event,
1193 // if there is one.
1194 ////////////////////////////////////////////////////////////////////
1195 void PGItem::
1196 play_sound(const string &event) {
1197 #ifdef HAVE_AUDIO
1198  LightReMutexHolder holder(_lock);
1199  Sounds::const_iterator si = _sounds.find(event);
1200  if (si != _sounds.end()) {
1201  AudioSound *sound = (*si).second;
1202  sound->play();
1203  }
1204 #endif // HAVE_AUDIO
1205 }
1206 
1207 ////////////////////////////////////////////////////////////////////
1208 // Function: PGItem::reduce_region
1209 // Access: Protected
1210 // Description: The frame parameter is an in/out parameter. This
1211 // function adjusts frame so that it represents the
1212 // largest part of the rectangular region passed in,
1213 // that does not overlap with the rectangular region of
1214 // the indicated obscurer. If the obscurer is NULL, or
1215 // is a hidden node, it is not considered and the frame
1216 // is left unchanged.
1217 //
1218 // This is used by slider bars and scroll frames, which
1219 // have to automatically figure out how much space they
1220 // have to work with after allowing space for scroll
1221 // bars and buttons.
1222 ////////////////////////////////////////////////////////////////////
1223 void PGItem::
1224 reduce_region(LVecBase4 &frame, PGItem *obscurer) const {
1225  if (obscurer != (PGItem *)NULL && !obscurer->is_overall_hidden()) {
1226  LVecBase4 oframe = get_relative_frame(obscurer);
1227 
1228  // Determine the four rectangular regions on the four sides of the
1229  // obscuring region.
1230  LVecBase4 right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
1231  LVecBase4 left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
1232  LVecBase4 above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
1233  LVecBase4 below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
1234 
1235  // Now choose the largest of those four.
1236  const LVecBase4 *largest = &right;
1237  PN_stdfloat largest_area = compute_area(*largest);
1238  compare_largest(largest, largest_area, &left);
1239  compare_largest(largest, largest_area, &above);
1240  compare_largest(largest, largest_area, &below);
1241 
1242  frame = *largest;
1243  }
1244 }
1245 
1246 ////////////////////////////////////////////////////////////////////
1247 // Function: PGItem::get_relative_frame
1248 // Access: Protected
1249 // Description: Returns the LVecBase4 frame of the indicated item,
1250 // converted into this item's coordinate space.
1251 // Presumably, item is a child of this node.
1252 ////////////////////////////////////////////////////////////////////
1253 LVecBase4 PGItem::
1254 get_relative_frame(PGItem *item) const {
1255  NodePath this_np = NodePath::any_path((PGItem *)this);
1256  NodePath item_np = this_np.find_path_to(item);
1257  if (item_np.is_empty()) {
1258  item_np = NodePath::any_path(item);
1259  }
1260  const LVecBase4 &orig_frame = item->get_frame();
1261  LMatrix4 transform = item_np.get_mat(this_np);
1262 
1263  // Transform the item's frame into the PGScrollFrame's
1264  // coordinate space. Transform all four vertices, and get the
1265  // new bounding box. This way the region works (mostly) even if
1266  // has been rotated.
1267  LPoint3 ll(orig_frame[0], 0.0f, orig_frame[2]);
1268  LPoint3 lr(orig_frame[1], 0.0f, orig_frame[2]);
1269  LPoint3 ul(orig_frame[0], 0.0f, orig_frame[3]);
1270  LPoint3 ur(orig_frame[1], 0.0f, orig_frame[3]);
1271  ll = ll * transform;
1272  lr = lr * transform;
1273  ul = ul * transform;
1274  ur = ur * transform;
1275 
1276  return LVecBase4(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
1277  max(max(ll[0], lr[0]), max(ul[0], ur[0])),
1278  min(min(ll[2], lr[2]), min(ul[2], ur[2])),
1279  max(max(ll[2], lr[2]), max(ul[2], ur[2])));
1280 }
1281 
1282 ////////////////////////////////////////////////////////////////////
1283 // Function: PGItem::mouse_to_local
1284 // Access: Protected
1285 // Description: Converts from the 2-d mouse coordinates into the
1286 // coordinate space of the item.
1287 ////////////////////////////////////////////////////////////////////
1288 LPoint3 PGItem::
1289 mouse_to_local(const LPoint2 &mouse_point) const {
1290  // This is ambiguous if the PGItem has multiple instances. Why
1291  // would you do that, anyway?
1292  NodePath this_np((PGItem *)this);
1293  CPT(TransformState) inv_transform = NodePath().get_transform(this_np);
1294  return inv_transform->get_mat().xform_point(LVector3::rfu(mouse_point[0], 0, mouse_point[1]));
1295 }
1296 
1297 ////////////////////////////////////////////////////////////////////
1298 // Function: PGItem::frame_changed
1299 // Access: Protected, Virtual
1300 // Description: Called when the user changes the frame size.
1301 ////////////////////////////////////////////////////////////////////
1302 void PGItem::
1303 frame_changed() {
1304  mark_frames_stale();
1305  if (has_notify()) {
1306  get_notify()->item_frame_changed(this);
1307  }
1308 }
1309 
1310 ////////////////////////////////////////////////////////////////////
1311 // Function: PGItem::slot_state_def
1312 // Access: Private
1313 // Description: Ensures there is a slot in the array for the given
1314 // state definition.
1315 ////////////////////////////////////////////////////////////////////
1316 void PGItem::
1317 slot_state_def(int state) {
1318  while (state >= (int)_state_defs.size()) {
1319  _state_defs.push_back(StateDef());
1320  }
1321 }
1322 
1323 ////////////////////////////////////////////////////////////////////
1324 // Function: PGItem::update_frame
1325 // Access: Private
1326 // Description: Generates a new instance of the frame geometry for
1327 // the indicated state.
1328 ////////////////////////////////////////////////////////////////////
1329 void PGItem::
1330 update_frame(int state) {
1331  // First, remove the old frame geometry, if any.
1332  if (state >= 0 && state < (int)_state_defs.size()) {
1333  _state_defs[state]._frame.remove_node();
1334  }
1335 
1336  // We must turn off the stale flag first, before we call
1337  // get_state_def(), to prevent get_state_def() from being a
1338  // recursive call.
1339  _state_defs[state]._frame_stale = false;
1340 
1341  // Now create new frame geometry.
1342  if (has_frame()) {
1343  NodePath &root = get_state_def(state);
1344  _state_defs[state]._frame =
1345  _state_defs[state]._frame_style.generate_into(root, _frame);
1346  }
1347 }
1348 
1349 ////////////////////////////////////////////////////////////////////
1350 // Function: PGItem::mark_frames_stale
1351 // Access: Private
1352 // Description: Marks all the frames in all states stale, so that
1353 // they will be regenerated the next time each state is
1354 // requested.
1355 ////////////////////////////////////////////////////////////////////
1356 void PGItem::
1357 mark_frames_stale() {
1358  StateDefs::iterator di;
1359  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
1360  // Remove the old frame, if any.
1361  (*di)._frame.remove_node();
1362  (*di)._frame_stale = true;
1363  }
1364  mark_internal_bounds_stale();
1365 }
1366 
1367 ////////////////////////////////////////////////////////////////////
1368 // Function: PGItem::clip_frame
1369 // Access: Private
1370 // Description: Clips the four corners of the item's frame by the
1371 // indicated clipping plane, and modifies the points to
1372 // reflect the new set of clipped points.
1373 //
1374 // The return value is true if the set of points is
1375 // unmodified (all points are behind the clip plane), or
1376 // false otherwise.
1377 ////////////////////////////////////////////////////////////////////
1378 bool PGItem::
1379 clip_frame(ClipPoints &source_points, const LPlane &plane) const {
1380  if (source_points.empty()) {
1381  return true;
1382  }
1383 
1384  LPoint3 from3d;
1385  LVector3 delta3d;
1386  if (!plane.intersects_plane(from3d, delta3d, LPlane(LVector3(0, 1, 0), LPoint3::zero()))) {
1387  // The clipping plane is parallel to the polygon. The polygon is
1388  // either all in or all out.
1389  if (plane.dist_to_plane(LPoint3::zero()) < 0.0) {
1390  // A point within the polygon is behind the clipping plane: the
1391  // polygon is all in.
1392  return true;
1393  }
1394  return false;
1395  }
1396 
1397  // Project the line of intersection into the X-Z plane. Now we have
1398  // a 2-d clipping line.
1399  LPoint2 from2d(from3d[0], from3d[2]);
1400  LVector2 delta2d(delta3d[0], delta3d[2]);
1401 
1402  PN_stdfloat a = -delta2d[1];
1403  PN_stdfloat b = delta2d[0];
1404  PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
1405 
1406  // Now walk through the points. Any point on the left of our line
1407  // gets removed, and the line segment clipped at the point of
1408  // intersection.
1409 
1410  // We might increase the number of vertices by as many as 1, if the
1411  // plane clips off exactly one corner. (We might also decrease the
1412  // number of vertices, or keep them the same number.)
1413  ClipPoints new_points;
1414  new_points.reserve(source_points.size() + 1);
1415 
1416  LPoint2 last_point(source_points.back());
1417  bool last_is_in = is_right(last_point - from2d, delta2d);
1418  bool all_in = last_is_in;
1419  ClipPoints::const_iterator pi;
1420  for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
1421  LPoint2 this_point(*pi);
1422  bool this_is_in = is_right(this_point - from2d, delta2d);
1423 
1424  // There appears to be a compiler bug in gcc 4.0: we need to
1425  // extract this comparison outside of the if statement.
1426  bool crossed_over = (this_is_in != last_is_in);
1427  if (crossed_over) {
1428  // We have just crossed over the clipping line. Find the point
1429  // of intersection.
1430  LVector2 d = this_point - last_point;
1431  PN_stdfloat denom = (a * d[0] + b * d[1]);
1432  if (denom != 0.0) {
1433  PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
1434  LPoint2 p = last_point + t * d;
1435 
1436  new_points.push_back(p);
1437  last_is_in = this_is_in;
1438  }
1439  }
1440 
1441  if (this_is_in) {
1442  // We are behind the clipping line. Keep the point.
1443  new_points.push_back(this_point);
1444  } else {
1445  all_in = false;
1446  }
1447 
1448  last_point = this_point;
1449  }
1450 
1451  source_points.swap(new_points);
1452  return all_in;
1453 }
string get_focus_in_event() const
Returns the event name that will be thrown when the item gets the keyboard focus. ...
Definition: pgItem.I:481
bool get_background_focus() const
Returns whether background_focus is currently enabled.
Definition: pgItem.I:223
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
This specialization on MouseWatcherParameter allows us to tag on additional elements to events for th...
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition: boundingBox.h:31
PGFrameStyle get_frame_style(int state)
Returns the kind of frame that will be drawn behind the item when it is in the indicated state...
Definition: pgItem.cxx:1087
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:74
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this node by the indicated matrix, if it means anything to do so...
Definition: pgItem.cxx:386
This is the base class for all the various kinds of gui widget objects.
Definition: pgItem.h:58
An optional parameter associated with an event.
NodePath & get_state_def(int state)
Returns the Node that is the root of the subgraph that will be drawn when the PGItem is in the indica...
Definition: pgItem.cxx:1043
int get_state() const
Returns the "state" of this particular PGItem.
Definition: pgItem.I:187
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
static void background_release(const MouseWatcherParameter &param)
Calls release() on all the PGItems with background focus.
Definition: pgItem.cxx:836
bool get_active() const
Returns whether the PGItem is currently active for mouse events.
Definition: pgItem.I:199
NodePath get_on_plane(int n) const
Returns the nth plane enabled by the attribute, sorted in render order.
int get_bin_sort(int bin_index) const
Returns the sort order of the bin with the indicated bin_index (where bin_index was retrieved by get_...
bool has_state_def(int state) const
Returns true if get_state_def() has ever been called for the indicated state (thus defining a render ...
Definition: pgItem.cxx:1005
virtual void without_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse moves completely outside the boundaries o...
Definition: pgItem.cxx:623
This defines a bounding sphere, consisting of a center and a radius.
string get_repeat_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the item is active and the indicated mouse or keyboar...
Definition: pgItem.I:521
static const LPoint3f & zero()
Returns a zero-length point.
Definition: lpoint3.h:259
An interface for simplifying ("flattening") scene graphs by eliminating unneeded nodes and collapsing...
bool is_this_node_hidden(const DrawMask &camera_mask) const
Returns true if this particular node is hidden, even though we might be traversing past this node to ...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
virtual void enter_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse enters the region.
Definition: pgItem.cxx:541
string get_press_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the item is active and the indicated mouse or keyboar...
Definition: pgItem.I:507
virtual void focus_out()
This is a callback hook function, called whenever the widget loses the keyboard focus.
Definition: pgItem.cxx:670
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This functions similarly to a LightAttrib.
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
NodePath instance_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Adds the referenced node of the NodePath as a child of the referenced node of the indicated other Nod...
Definition: nodePath.cxx:618
int get_num_state_defs() const
Returns one more than the highest-numbered state def that was ever assigned to the PGItem...
Definition: pgItem.cxx:992
bool is_overall_hidden() const
Returns true if the node has been hidden to all cameras by clearing its overall bit.
Definition: pandaNode.I:524
string get_enter_event() const
Returns the event name that will be thrown when the item is active and the mouse enters its frame...
Definition: pgItem.I:425
string get_focus_out_event() const
Returns the event name that will be thrown when the item loses the keyboard focus.
Definition: pgItem.I:493
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
Definition: pandaNode.cxx:2925
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 traverse(const NodePath &root)
Begins the traversal from the indicated node.
void set_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Changes the complete transform object on this node.
Definition: nodePath.I:718
PGMouseWatcherRegion * get_region() const
Returns the MouseWatcherRegion associated with this item.
Definition: pgItem.I:39
string get_without_event() const
Returns the event name that will be thrown when the item is active and the mouse moves completely out...
Definition: pgItem.I:469
static CullBinManager * get_global_ptr()
Returns the pointer to the global CullBinManager object.
static NodePath any_path(PandaNode *node, Thread *current_thread=Thread::get_current_thread())
Returns a new NodePath that represents any arbitrary path from the root to the indicated node...
Definition: nodePath.I:77
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
bool has_notify() const
Returns true if there is an object configured to be notified when the PGItem changes, false otherwise.
Definition: pgItem.I:72
string get_keystroke_event() const
Returns the event name that will be thrown when the item is active and any key is pressed by the user...
Definition: pgItem.I:547
int get_num_on_planes() const
Returns the number of planes that are enabled by the attribute.
bool is_keyrepeat() const
Returns true if the button-down even was generated due to keyrepeat, or false if it was an original b...
virtual void focus_in()
This is a callback hook function, called whenever the widget gets the keyboard focus.
Definition: pgItem.cxx:647
void set_frame_style(int state, const PGFrameStyle &style)
Changes the kind of frame that will be drawn behind the item when it is in the indicated state...
Definition: pgItem.cxx:1102
ButtonHandle get_button() const
Returns the mouse or keyboard button associated with this event.
const LVecBase4 & get_frame() const
Returns the left, right, bottom, top coordinates of the scissor frame.
Definition: scissorAttrib.I:51
virtual void set_focus(bool focus)
Sets whether the PGItem currently has keyboard focus.
Definition: pgItem.cxx:917
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
NodePath instance_to_state_def(int state, const NodePath &path)
Parents an instance of the bottom node of the indicated NodePath to the indicated state index...
Definition: pgItem.cxx:1068
string get_within_event() const
Returns the event name that will be thrown when the item is active and the mouse moves within the bou...
Definition: pgItem.I:453
This is a specialization of CullTraverser for use within the pgui system.
static void background_press(const MouseWatcherParameter &param)
Calls press() on all the PGItems with background focus.
Definition: pgItem.cxx:819
bool activate_region(const LMatrix4 &transform, int sort, const ClipPlaneAttrib *cpa, const ScissorAttrib *sa)
Applies the indicated scene graph transform and order as determined by the traversal from PGTop...
Definition: pgItem.cxx:424
PGItemNotify * get_notify() const
Returns the object which will be notified when the PGItem changes, if any.
Definition: pgItem.I:85
virtual void release(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever a mouse or keyboard button previously depressed wit...
Definition: pgItem.cxx:726
This is a specialization on MouseWatcherRegion, to add a bit more fields that are relevant to the PG ...
bool invert_from(const LMatrix4f &other)
Computes the inverse of the other matrix, and stores the result in this matrix.
Definition: lmatrix.h:2173
static TextNode * get_text_node()
Returns the TextNode object that will be used by all PGItems to generate default labels given a strin...
Definition: pgItem.cxx:1177
bool get_focus() const
Returns whether the PGItem currently has focus for keyboard events.
Definition: pgItem.I:211
virtual void within_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse moves within the boundaries of the region...
Definition: pgItem.cxx:598
static LVector3f right(CoordinateSystem cs=CS_default)
Returns the right vector for the given coordinate system.
Definition: lvector3.h:568
BoundingVolume::BoundsType get_bounds_type() const
Returns the bounding volume type set with set_bounds_type().
Definition: pandaNode.cxx:2284
Similar to MutexHolder, but for a light reentrant mutex.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
string get_release_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the item is active and the indicated mouse or keyboar...
Definition: pgItem.I:535
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void apply_attribs(PandaNode *node, int attrib_types=~(TT_clip_plane|TT_cull_face|TT_apply_texture_color))
Walks the scene graph, accumulating attribs of the indicated types, applying them to the vertices...
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
This is a two-component vector offset.
Definition: lvector2.h:91
virtual void move(const MouseWatcherParameter &param)
This is a callback hook function, called whenever a mouse is moved while within the region...
Definition: pgItem.cxx:800
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
A thread; that is, a lightweight process.
Definition: thread.h:51
static LPoint3f rfu(float right, float fwd, float up, CoordinateSystem cs=CS_default)
Returns a point described by right, forward, up displacements from the origin, wherever that maps to ...
Definition: lpoint3.h:463
void set_background_focus(bool focus)
Sets the background_focus flag for this item.
Definition: pgItem.cxx:962
The primary interface to this module.
Definition: textNode.h:52
void clear_state_def(int state)
Resets the NodePath assigned to the indicated state to its initial default, with only a frame represe...
Definition: pgItem.cxx:1021
virtual void candidate(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever the user highlights an option in the IME window...
Definition: pgItem.cxx:778
bool has_frame() const
Returns true if the item has a bounding rectangle; see set_frame().
Definition: pgItem.I:144
virtual void keystroke(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever the user presses a key.
Definition: pgItem.cxx:752
string get_exit_event() const
Returns the event name that will be thrown when the item is active and the mouse exits its frame...
Definition: pgItem.I:438
NodePath find_path_to(PandaNode *node) const
Searches for the indicated node below this node and returns the shortest NodePath that connects them...
Definition: nodePath.cxx:454
const LMatrix4 & get_mat() const
Returns the transform matrix that has been applied to the referenced node, or the identity matrix if ...
Definition: nodePath.I:965
void add_region(MouseWatcherRegion *region)
Adds the indicated region to the set of regions in the group.
Definition: pgTop.I:97
const DrawMask & get_camera_mask() const
Returns the visibility mask from the camera viewing the scene.
This is our own Panda specialization on the default STL set.
Definition: pset.h:52
This restricts rendering to within a rectangular region of the scene, without otherwise affecting the...
Definition: scissorAttrib.h:41
Objects that inherit from this class can receive specialized messages when PGItems change in certain ...
Definition: pgItemNotify.h:30
This is a two-component point in space.
Definition: lpoint2.h:92
const LVecBase4 & get_frame() const
Returns the bounding rectangle of the item.
Definition: pgItem.I:131
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
This is sent along as a parameter to most events generated for a region to indicate the mouse and but...
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:925
This is a global object that maintains the collection of named CullBins in the world.
virtual void exit_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse exits the region.
Definition: pgItem.cxx:568
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:541
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
virtual void set_active(bool active)
Sets whether the PGItem is active for mouse watching.
Definition: pgItem.cxx:890
A node that contains a plane.
Definition: planeNode.h:39
static void background_candidate(const MouseWatcherParameter &param)
Calls candidate() on all the PGItems with background focus.
Definition: pgItem.cxx:870
virtual void press(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever a mouse or keyboard button is depressed while the m...
Definition: pgItem.cxx:694
static void background_keystroke(const MouseWatcherParameter &param)
Calls keystroke() on all the PGItems with background focus.
Definition: pgItem.cxx:853
BinType get_bin_type(int bin_index) const
Returns the type of the bin with the indicated bin_index (where bin_index was retrieved by get_bin() ...
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...