Panda3D
 All Classes Functions Variables Enumerations
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 
233  CullBinManager *bin_manager = CullBinManager::get_global_ptr();
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  }
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 
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 
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 
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  }
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 }
ButtonHandle get_button() const
Returns the mouse or keyboard button associated with this event.
const LVecBase4 & get_frame() const
Returns the bounding rectangle of the item.
Definition: pgItem.I:131
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
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
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
bool get_background_focus() const
Returns whether background_focus is currently enabled.
Definition: pgItem.I:223
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
static void background_release(const MouseWatcherParameter &param)
Calls release() on all the PGItems with background focus.
Definition: pgItem.cxx:836
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
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.
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
static const LPoint3f & zero()
Returns a zero-length point.
Definition: lpoint3.h:258
An interface for simplifying (&quot;flattening&quot;) scene graphs by eliminating unneeded nodes and collapsing...
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
int get_nested_vertices(Thread *current_thread=Thread::get_current_thread()) const
Returns the total number of vertices that will be rendered by this node and all of its descendents...
Definition: pandaNode.cxx:2407
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
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:74
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
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
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
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
bool has_frame() const
Returns true if the item has a bounding rectangle; see set_frame().
Definition: pgItem.I:144
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...
BoundingVolume::BoundsType get_bounds_type() const
Returns the bounding volume type set with set_bounds_type().
Definition: pandaNode.cxx:2284
PGItemNotify * get_notify() const
Returns the object which will be notified when the PGItem changes, if any.
Definition: pgItem.I:85
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
void mark_internal_bounds_stale(Thread *current_thread=Thread::get_current_thread())
Should be called by a derived class to mark the internal bounding volume stale, so that compute_inter...
Definition: pandaNode.cxx:2467
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
virtual void set_focus(bool focus)
Sets whether the PGItem currently has keyboard focus.
Definition: pgItem.cxx:917
int get_state() const
Returns the &quot;state&quot; of this particular PGItem.
Definition: pgItem.I:187
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
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
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
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
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
bool get_active() const
Returns whether the PGItem is currently active for mouse events.
Definition: pgItem.I:199
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
NodePath get_on_plane(int n) const
Returns the nth plane enabled by the attribute, sorted in render order.
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
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:554
int get_num_on_planes() const
Returns the number of planes that are enabled by the attribute.
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
const DrawMask & get_camera_mask() const
Returns the visibility mask from the camera viewing the scene.
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
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
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
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:450
void set_background_focus(bool focus)
Sets the background_focus flag for this item.
Definition: pgItem.cxx:962
bool is_keyrepeat() const
Returns true if the button-down even was generated due to keyrepeat, or false if it was an original b...
The primary interface to this module.
Definition: textNode.h:52
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
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
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_...
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
void add_region(MouseWatcherRegion *region)
Adds the indicated region to the set of regions in the group.
Definition: pgTop.I:97
This restricts rendering to within a rectangular region of the scene, without otherwise affecting the...
Definition: scissorAttrib.h:41
PGMouseWatcherRegion * get_region() const
Returns the MouseWatcherRegion associated with this item.
Definition: pgItem.I:39
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
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...
This is a global object that maintains the collection of named CullBins in the world.
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
const LVecBase4 & get_frame() const
Returns the left, right, bottom, top coordinates of the scissor frame.
Definition: scissorAttrib.I:51
virtual void exit_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse exits the region.
Definition: pgItem.cxx:568
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
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 ...
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
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
bool get_focus() const
Returns whether the PGItem currently has focus for keyboard events.
Definition: pgItem.I:211
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:527
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
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
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
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
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