Panda3D

pgItem.cxx

00001 // Filename: pgItem.cxx
00002 // Created by:  drose (13Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pgItem.h"
00016 #include "pgMouseWatcherParameter.h"
00017 #include "pgCullTraverser.h"
00018 #include "config_pgui.h"
00019 #include "boundingVolume.h"
00020 #include "pandaNode.h"
00021 #include "sceneGraphReducer.h"
00022 #include "throw_event.h"
00023 #include "string_utils.h"
00024 #include "nodePath.h"
00025 #include "cullTraverser.h"
00026 #include "cullTraverserData.h"
00027 #include "cullBinManager.h"
00028 #include "clipPlaneAttrib.h"
00029 #include "scissorAttrib.h"
00030 #include "dcast.h"
00031 #include "boundingSphere.h"
00032 #include "boundingBox.h"
00033 #include "config_mathutil.h"
00034 
00035 #ifdef HAVE_AUDIO
00036 #include "audioSound.h"
00037 #endif
00038 
00039 TypeHandle PGItem::_type_handle;
00040 PT(TextNode) PGItem::_text_node;
00041 PGItem *PGItem::_focus_item = (PGItem *)NULL;
00042 PGItem::BackgroundFocus PGItem::_background_focus;
00043 
00044 
00045 ////////////////////////////////////////////////////////////////////
00046 //     Function: is_right
00047 //  Description: Returns true if the 2-d v1 is to the right of v2.
00048 ////////////////////////////////////////////////////////////////////
00049 INLINE bool
00050 is_right(const LVector2f &v1, const LVector2f &v2) {
00051   return (v1[0] * v2[1] - v1[1] * v2[0]) > 0;
00052 }
00053 
00054 ////////////////////////////////////////////////////////////////////
00055 //     Function: PGItem::Constructor
00056 //       Access: Published
00057 //  Description: 
00058 ////////////////////////////////////////////////////////////////////
00059 PGItem::
00060 PGItem(const string &name) : 
00061   PandaNode(name),
00062   _lock(name)
00063 {
00064   set_cull_callback();
00065 
00066   _notify = NULL;
00067   _has_frame = false;
00068   _frame.set(0, 0, 0, 0);
00069   _region = new PGMouseWatcherRegion(this);
00070   _state = 0;
00071   _flags = 0;
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: PGItem::Destructor
00076 //       Access: Public, Virtual
00077 //  Description: 
00078 ////////////////////////////////////////////////////////////////////
00079 PGItem::
00080 ~PGItem() {
00081   if (_notify != (PGItemNotify *)NULL) {
00082     _notify->remove_item(this);
00083     _notify = NULL;
00084   }
00085 
00086   nassertv(_region->_item == this);
00087   _region->_item = (PGItem *)NULL;
00088 
00089   set_background_focus(false);
00090   if (_focus_item == this) {
00091     _focus_item = (PGItem *)NULL;
00092   }
00093 }
00094 
00095 ////////////////////////////////////////////////////////////////////
00096 //     Function: PGItem::Copy Constructor
00097 //       Access: Public
00098 //  Description: 
00099 ////////////////////////////////////////////////////////////////////
00100 PGItem::
00101 PGItem(const PGItem &copy) :
00102   PandaNode(copy),
00103   _has_frame(copy._has_frame),
00104   _frame(copy._frame),
00105   _state(copy._state),
00106   _flags(copy._flags)
00107 #ifdef HAVE_AUDIO
00108   , _sounds(copy._sounds)
00109 #endif
00110 {
00111   _notify = NULL;
00112   _region = new PGMouseWatcherRegion(this);
00113   
00114   // We give our region the same name as the region for the PGItem
00115   // we're copying--so that this PGItem will generate the same event
00116   // names when the user interacts with it.
00117   _region->set_name(copy._region->get_name());
00118 
00119   // Make a deep copy of all of the original PGItem's StateDefs.
00120   size_t num_state_defs = copy._state_defs.size();
00121   _state_defs.reserve(num_state_defs);
00122   for (size_t i = 0; i < num_state_defs; ++i) {
00123     // We cheat and cast away the const, because the frame is just a
00124     // cache.  But we have to get the frame out of the source before we
00125     // can safely copy it.
00126     StateDef &old_sd = (StateDef &)(copy._state_defs[i]);
00127     old_sd._frame.remove_node();
00128     old_sd._frame_stale = true;
00129 
00130     StateDef new_sd;
00131     new_sd._root = old_sd._root.copy_to(NodePath());
00132     new_sd._frame_style = old_sd._frame_style;
00133 
00134     _state_defs.push_back(new_sd);
00135   }
00136 }
00137 
00138 ////////////////////////////////////////////////////////////////////
00139 //     Function: PGItem::make_copy
00140 //       Access: Protected, Virtual
00141 //  Description: Returns a newly-allocated Node that is a shallow copy
00142 //               of this one.  It will be a different Node pointer,
00143 //               but its internal data may or may not be shared with
00144 //               that of the original Node.
00145 ////////////////////////////////////////////////////////////////////
00146 PandaNode *PGItem::
00147 make_copy() const {
00148   LightReMutexHolder holder(_lock);
00149   return new PGItem(*this);
00150 }
00151 
00152 ////////////////////////////////////////////////////////////////////
00153 //     Function: PGItem::transform_changed
00154 //       Access: Protected, Virtual
00155 //  Description: Called after the node's transform has been changed
00156 //               for any reason, this just provides a hook so derived
00157 //               classes can do something special in this case.
00158 ////////////////////////////////////////////////////////////////////
00159 void PGItem::
00160 transform_changed() {
00161   LightReMutexHolder holder(_lock);
00162   PandaNode::transform_changed();
00163   if (has_notify()) {
00164     get_notify()->item_transform_changed(this);
00165   }
00166 }
00167 
00168 ////////////////////////////////////////////////////////////////////
00169 //     Function: PGItem::draw_mask_changed
00170 //       Access: Protected, Virtual
00171 //  Description: Called after the node's draw_mask has been changed
00172 //               for any reason, this just provides a hook so derived
00173 //               classes can do something special in this case.
00174 ////////////////////////////////////////////////////////////////////
00175 void PGItem::
00176 draw_mask_changed() {
00177   LightReMutexHolder holder(_lock);
00178   PandaNode::draw_mask_changed();
00179   if (has_notify()) {
00180     get_notify()->item_draw_mask_changed(this);
00181   }
00182 }
00183 
00184 ////////////////////////////////////////////////////////////////////
00185 //     Function: PGItem::cull_callback
00186 //       Access: Protected, Virtual
00187 //  Description: This function will be called during the cull
00188 //               traversal to perform any additional operations that
00189 //               should be performed at cull time.  This may include
00190 //               additional manipulation of render state or additional
00191 //               visible/invisible decisions, or any other arbitrary
00192 //               operation.
00193 //
00194 //               Note that this function will *not* be called unless
00195 //               set_cull_callback() is called in the constructor of
00196 //               the derived class.  It is necessary to call
00197 //               set_cull_callback() to indicated that we require
00198 //               cull_callback() to be called.
00199 //
00200 //               By the time this function is called, the node has
00201 //               already passed the bounding-volume test for the
00202 //               viewing frustum, and the node's transform and state
00203 //               have already been applied to the indicated
00204 //               CullTraverserData object.
00205 //
00206 //               The return value is true if this node should be
00207 //               visible, or false if it should be culled.
00208 ////////////////////////////////////////////////////////////////////
00209 bool PGItem::
00210 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00211   LightReMutexHolder holder(_lock);
00212   bool this_node_hidden = data.is_this_node_hidden(trav);
00213   if (!this_node_hidden && has_frame() && get_active()) {
00214     // The item has a frame, so we want to generate a region for it
00215     // and update the MouseWatcher.
00216 
00217     // We can only do this if our traverser is a PGCullTraverser
00218     // (which will be the case if this node was parented somewhere
00219     // under a PGTop node).
00220     if (trav->is_exact_type(PGCullTraverser::get_class_type())) {
00221       PGCullTraverser *pg_trav;
00222       DCAST_INTO_R(pg_trav, trav, true);
00223 
00224       CPT(TransformState) net_transform = data.get_net_transform(trav);
00225       const LMatrix4f &transform = net_transform->get_mat();
00226 
00227       // Consider the cull bin this object is in.  Since the binning
00228       // affects the render order, we want bins that render later to
00229       // get higher sort values.
00230       int bin_index = data._state->get_bin_index();
00231       int sort;
00232 
00233       CullBinManager *bin_manager = CullBinManager::get_global_ptr();
00234       CullBinManager::BinType bin_type = bin_manager->get_bin_type(bin_index);
00235       if (bin_type == CullBinManager::BT_fixed) {
00236         // If the bin is a "fixed" type bin, our local sort is based
00237         // on the fixed order.
00238         sort = data._state->get_draw_order();
00239 
00240       } else if (bin_type == CullBinManager::BT_unsorted) {
00241         // If the bin is an "unsorted" type bin, we base the local
00242         // sort on the scene graph order.
00243         sort = pg_trav->_sort_index;
00244         pg_trav->_sort_index++;
00245 
00246       } else {
00247         // Otherwise, the local sort is irrelevant.
00248         sort = 0;
00249       }
00250 
00251       // Now what order does this bin sort relative to the other bins?
00252       // This becomes the high-order part of the final sort count.
00253       int bin_sort = bin_manager->get_bin_sort(data._state->get_bin_index());
00254 
00255       // Combine the two sorts into a single int.  This assumes we
00256       // only need 16 bits for each sort number, possibly an erroneous
00257       // assumption.  We should really provide two separate sort
00258       // values, both ints, in the MouseWatcherRegion; but in the
00259       // interest of expediency we work within the existing interface
00260       // which only provides one.
00261       sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
00262 
00263       if (activate_region(transform, sort, 
00264                           DCAST(ClipPlaneAttrib, data._state->get_attrib(ClipPlaneAttrib::get_class_slot())),
00265                           DCAST(ScissorAttrib, data._state->get_attrib(ScissorAttrib::get_class_slot())))) {
00266         pg_trav->_top->add_region(get_region());
00267       }
00268     }
00269   }
00270 
00271   if (has_state_def(get_state())) {
00272     // This item has a current state definition that we should use
00273     // to render the item.
00274     NodePath &root = get_state_def(get_state());
00275     CullTraverserData next_data(data, root.node());
00276     trav->traverse(next_data);
00277   }
00278 
00279   // Now continue to render everything else below this node.
00280   return true;
00281 }
00282 
00283 ////////////////////////////////////////////////////////////////////
00284 //     Function: PGItem::is_renderable
00285 //       Access: Public, Virtual
00286 //  Description: Returns true if there is some value to visiting this
00287 //               particular node during the cull traversal for any
00288 //               camera, false otherwise.  This will be used to
00289 //               optimize the result of get_net_draw_show_mask(), so
00290 //               that any subtrees that contain only nodes for which
00291 //               is_renderable() is false need not be visited.
00292 ////////////////////////////////////////////////////////////////////
00293 bool PGItem::
00294 is_renderable() const {
00295   return true;
00296 }
00297 
00298 ////////////////////////////////////////////////////////////////////
00299 //     Function: PGItem::compute_internal_bounds
00300 //       Access: Protected, Virtual
00301 //  Description: Called when needed to recompute the node's
00302 //               _internal_bound object.  Nodes that contain anything
00303 //               of substance should redefine this to do the right
00304 //               thing.
00305 ////////////////////////////////////////////////////////////////////
00306 void PGItem::
00307 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
00308                         int &internal_vertices,
00309                         int pipeline_stage,
00310                         Thread *current_thread) const {
00311   LightReMutexHolder holder(_lock, current_thread);
00312   int num_vertices = 0;
00313 
00314   // First, get ourselves a fresh, empty bounding volume.
00315   PT(BoundingVolume) bound;
00316 
00317   BoundingVolume::BoundsType btype = get_bounds_type();
00318   if (btype == BoundingVolume::BT_default) {
00319     btype = bounds_type;
00320   }
00321 
00322   if (btype == BoundingVolume::BT_sphere) {
00323     bound = new BoundingSphere;
00324   } else {
00325     bound = new BoundingBox;
00326   }
00327 
00328   // Now actually compute the bounding volume by putting it around all
00329   // of our states' bounding volumes.
00330   pvector<const BoundingVolume *> child_volumes;
00331 
00332   // We walk through the list of state defs indirectly, calling
00333   // get_state_def() on each one, to ensure that the frames are
00334   // updated correctly before we measure their bounding volumes.
00335   for (int i = 0; i < (int)_state_defs.size(); i++) {
00336     NodePath &root = ((PGItem *)this)->get_state_def(i);
00337     if (!root.is_empty()) {
00338       PandaNode *node = root.node();
00339       child_volumes.push_back(node->get_bounds(current_thread));
00340       num_vertices += node->get_nested_vertices(current_thread);
00341     }
00342   }
00343 
00344   const BoundingVolume **child_begin = &child_volumes[0];
00345   const BoundingVolume **child_end = child_begin + child_volumes.size();
00346 
00347   bound->around(child_begin, child_end);
00348 
00349   internal_bounds = bound;
00350   internal_vertices = num_vertices;
00351 }
00352 
00353 ////////////////////////////////////////////////////////////////////
00354 //     Function: PGItem::r_prepare_scene
00355 //       Access: Protected, Virtual
00356 //  Description: The recursive implementation of prepare_scene().
00357 //               Don't call this directly; call
00358 //               PandaNode::prepare_scene() or
00359 //               NodePath::prepare_scene() instead.
00360 ////////////////////////////////////////////////////////////////////
00361 void PGItem::
00362 r_prepare_scene(const RenderState *state,
00363                 PreparedGraphicsObjects *prepared_objects,
00364                 Thread *current_thread) {
00365   LightReMutexHolder holder(_lock);
00366   StateDefs::iterator di;
00367   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
00368     NodePath &root = (*di)._root;
00369     if (!root.is_empty()) {
00370       PandaNode *child = root.node();
00371       CPT(RenderState) child_state = state->compose(child->get_state());
00372       child->r_prepare_scene(child_state, prepared_objects, current_thread);
00373     }
00374   }
00375   
00376   PandaNode::r_prepare_scene(state, prepared_objects, current_thread);
00377 }
00378 
00379 ////////////////////////////////////////////////////////////////////
00380 //     Function: PGItem::xform
00381 //       Access: Public, Virtual
00382 //  Description: Transforms the contents of this node by the indicated
00383 //               matrix, if it means anything to do so.  For most
00384 //               kinds of nodes, this does nothing.
00385 ////////////////////////////////////////////////////////////////////
00386 void PGItem::
00387 xform(const LMatrix4f &mat) {
00388   LightReMutexHolder holder(_lock);
00389   // Transform the frame.
00390   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
00391   LPoint3f ur(_frame[1], 0.0f, _frame[3]);
00392   ll = ll * mat;
00393   ur = ur * mat;
00394   _frame.set(ll[0], ur[0], ll[2], ur[2]);
00395 
00396   // Transform the individual states and their frame styles.
00397   StateDefs::iterator di;
00398   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
00399     NodePath &root = (*di)._root;
00400     // Apply the matrix to the previous transform.
00401     root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
00402 
00403     // Now flatten the transform into the subgraph.
00404     SceneGraphReducer gr;
00405     gr.apply_attribs(root.node());
00406 
00407     // Transform the frame style too.
00408     if ((*di)._frame_style.xform(mat)) {
00409       (*di)._frame_stale = true;
00410     }
00411   }
00412   mark_internal_bounds_stale();
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: PGItem::activate_region
00417 //       Access: Public
00418 //  Description: Applies the indicated scene graph transform and order
00419 //               as determined by the traversal from PGTop.
00420 //
00421 //               The return value is true if the region is valid, or
00422 //               false if it is empty or completely clipped.
00423 ////////////////////////////////////////////////////////////////////
00424 bool PGItem::
00425 activate_region(const LMatrix4f &transform, int sort,
00426                 const ClipPlaneAttrib *cpa,
00427                 const ScissorAttrib *sa) {
00428   LightReMutexHolder holder(_lock);
00429   // Transform all four vertices, and get the new bounding box.  This
00430   // way the region works (mostly) even if has been rotated.
00431   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
00432   LPoint3f lr(_frame[1], 0.0f, _frame[2]);
00433   LPoint3f ul(_frame[0], 0.0f, _frame[3]);
00434   LPoint3f ur(_frame[1], 0.0f, _frame[3]);
00435   ll = ll * transform;
00436   lr = lr * transform;
00437   ul = ul * transform;
00438   ur = ur * transform;
00439 
00440   LVecBase4f frame;
00441   if (cpa != (ClipPlaneAttrib *)NULL && cpa->get_num_on_planes() != 0) {
00442     // Apply the clip plane(s) and/or scissor region now that we are
00443     // here in world space.
00444     
00445     pvector<LPoint2f> points;
00446     points.reserve(4);
00447     points.push_back(LPoint2f(ll[0], ll[2]));
00448     points.push_back(LPoint2f(lr[0], lr[2]));
00449     points.push_back(LPoint2f(ur[0], ur[2]));
00450     points.push_back(LPoint2f(ul[0], ul[2]));
00451 
00452     int num_on_planes = cpa->get_num_on_planes();
00453     for (int i = 0; i < num_on_planes; ++i) {
00454       NodePath plane_path = cpa->get_on_plane(i);
00455       Planef plane = DCAST(PlaneNode, plane_path.node())->get_plane();
00456       plane.xform(plane_path.get_net_transform()->get_mat());
00457       
00458       // We ignore the y coordinate, assuming the frame is still in
00459       // the X-Z plane after being transformed.  Not sure if we really
00460       // need to support general 3-D transforms on 2-D objects.
00461       clip_frame(points, plane);
00462     }
00463     
00464     if (points.empty()) {
00465       // Turns out it's completely clipped after all.
00466       return false;
00467     }
00468 
00469     pvector<LPoint2f>::iterator pi;
00470     pi = points.begin();
00471     frame.set((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]);
00472     ++pi;
00473     while (pi != points.end()) {
00474       frame[0] = min(frame[0], (*pi)[0]);
00475       frame[1] = max(frame[1], (*pi)[0]);
00476       frame[2] = min(frame[2], (*pi)[1]);
00477       frame[3] = max(frame[3], (*pi)[1]);
00478       ++pi;
00479     }
00480   } else {
00481     // Since there are no clip planes involved, just set the frame.
00482     frame.set(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
00483               max(max(ll[0], lr[0]), max(ul[0], ur[0])),
00484               min(min(ll[2], lr[2]), min(ul[2], ur[2])),
00485               max(max(ll[2], lr[2]), max(ul[2], ur[2])));
00486   }
00487 
00488   if (sa != (ScissorAttrib *)NULL) {
00489     // Also restrict it to within the scissor region.
00490     const LVecBase4f &sf = sa->get_frame();
00491     // Expand sf from 0..1 to -1..1.
00492     frame.set(max(frame[0], sf[0] * 2.0f - 1.0f),
00493               min(frame[1], sf[1] * 2.0f - 1.0f),
00494               max(frame[2], sf[2] * 2.0f - 1.0f),
00495               min(frame[3], sf[3] * 2.0f - 1.0f));
00496     if (frame[1] <= frame[0] || frame[3] <= frame[2]) {
00497       // Completely outside the scissor region.
00498       return false;
00499     }
00500   }
00501 
00502   _region->set_frame(frame);
00503                      
00504   _region->set_sort(sort);
00505   _region->set_active(true);
00506 
00507   // calculate the inverse of this transform, which is needed to 
00508   // go back to the frame space.
00509   _frame_inv_xform.invert_from(transform);
00510 
00511   return true;
00512 }
00513 
00514 ////////////////////////////////////////////////////////////////////
00515 //     Function: PGItem::enter_region
00516 //       Access: Public, Virtual
00517 //  Description: This is a callback hook function, called whenever the
00518 //               mouse enters the region.  The mouse is only
00519 //               considered to be "entered" in one region at a time;
00520 //               in the case of nested regions, it exits the outer
00521 //               region before entering the inner one.
00522 ////////////////////////////////////////////////////////////////////
00523 void PGItem::
00524 enter_region(const MouseWatcherParameter &param) {
00525   LightReMutexHolder holder(_lock);
00526   if (pgui_cat.is_debug()) {
00527     pgui_cat.debug()
00528       << *this << "::enter_region(" << param << ")\n";
00529   }
00530 
00531   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00532   string event = get_enter_event();
00533   play_sound(event);
00534   throw_event(event, EventParameter(ep));
00535 
00536   if (has_notify()) {
00537     get_notify()->item_enter(this, param);
00538   }
00539 }
00540 
00541 ////////////////////////////////////////////////////////////////////
00542 //     Function: PGItem::exit_region
00543 //       Access: Public, Virtual
00544 //  Description: This is a callback hook function, called whenever the
00545 //               mouse exits the region.  The mouse is only considered
00546 //               to be "entered" in one region at a time; in the case
00547 //               of nested regions, it exits the outer region before
00548 //               entering the inner one.
00549 ////////////////////////////////////////////////////////////////////
00550 void PGItem::
00551 exit_region(const MouseWatcherParameter &param) {
00552   LightReMutexHolder holder(_lock);
00553   if (pgui_cat.is_debug()) {
00554     pgui_cat.debug()
00555       << *this << "::exit_region(" << param << ")\n";
00556   }
00557 
00558   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00559   string event = get_exit_event();
00560   play_sound(event);
00561   throw_event(event, EventParameter(ep));
00562 
00563   if (has_notify()) {
00564     get_notify()->item_exit(this, param);
00565   }
00566 
00567   //pgui_cat.info() << get_name() << "::exit()" << endl;
00568 }
00569 
00570 ////////////////////////////////////////////////////////////////////
00571 //     Function: PGItem::within_region
00572 //       Access: Public, Virtual
00573 //  Description: This is a callback hook function, called whenever the
00574 //               mouse moves within the boundaries of the region, even
00575 //               if it is also within the boundaries of a nested
00576 //               region.  This is different from "enter", which is
00577 //               only called whenever the mouse is within only that
00578 //               region.
00579 ////////////////////////////////////////////////////////////////////
00580 void PGItem::
00581 within_region(const MouseWatcherParameter &param) {
00582   LightReMutexHolder holder(_lock);
00583   if (pgui_cat.is_debug()) {
00584     pgui_cat.debug()
00585       << *this << "::within_region(" << param << ")\n";
00586   }
00587 
00588   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00589   string event = get_within_event();
00590   play_sound(event);
00591   throw_event(event, EventParameter(ep));
00592 
00593   if (has_notify()) {
00594     get_notify()->item_within(this, param);
00595   }
00596 }
00597 
00598 ////////////////////////////////////////////////////////////////////
00599 //     Function: PGItem::without_region
00600 //       Access: Public, Virtual
00601 //  Description: This is a callback hook function, called whenever the
00602 //               mouse moves completely outside the boundaries of the
00603 //               region.  See within().
00604 ////////////////////////////////////////////////////////////////////
00605 void PGItem::
00606 without_region(const MouseWatcherParameter &param) {
00607   LightReMutexHolder holder(_lock);
00608   if (pgui_cat.is_debug()) {
00609     pgui_cat.debug()
00610       << *this << "::without_region(" << param << ")\n";
00611   }
00612 
00613   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00614   string event = get_without_event();
00615   play_sound(event);
00616   throw_event(event, EventParameter(ep));
00617 
00618   if (has_notify()) {
00619     get_notify()->item_without(this, param);
00620   }
00621 }
00622 
00623 ////////////////////////////////////////////////////////////////////
00624 //     Function: PGItem::focus_in
00625 //       Access: Public, Virtual
00626 //  Description: This is a callback hook function, called whenever the
00627 //               widget gets the keyboard focus.
00628 ////////////////////////////////////////////////////////////////////
00629 void PGItem::
00630 focus_in() {
00631   LightReMutexHolder holder(_lock);
00632   if (pgui_cat.is_debug()) {
00633     pgui_cat.debug()
00634       << *this << "::focus_in()\n";
00635   }
00636 
00637   string event = get_focus_in_event();
00638   play_sound(event);
00639   throw_event(event);
00640 
00641   if (has_notify()) {
00642     get_notify()->item_focus_in(this);
00643   }
00644 }
00645 
00646 ////////////////////////////////////////////////////////////////////
00647 //     Function: PGItem::focus_out
00648 //       Access: Public, Virtual
00649 //  Description: This is a callback hook function, called whenever the
00650 //               widget loses the keyboard focus.
00651 ////////////////////////////////////////////////////////////////////
00652 void PGItem::
00653 focus_out() {
00654   LightReMutexHolder holder(_lock);
00655   if (pgui_cat.is_debug()) {
00656     pgui_cat.debug()
00657       << *this << "::focus_out()\n";
00658   }
00659 
00660   string event = get_focus_out_event();
00661   play_sound(event);
00662   throw_event(event);
00663 
00664   if (has_notify()) {
00665     get_notify()->item_focus_out(this);
00666   }
00667 }
00668 
00669 ////////////////////////////////////////////////////////////////////
00670 //     Function: PGItem::press
00671 //       Access: Public, Virtual
00672 //  Description: This is a callback hook function, called whenever a
00673 //               mouse or keyboard button is depressed while the mouse
00674 //               is within the region.
00675 ////////////////////////////////////////////////////////////////////
00676 void PGItem::
00677 press(const MouseWatcherParameter &param, bool background) {
00678   LightReMutexHolder holder(_lock);
00679   if (pgui_cat.is_debug()) {
00680     pgui_cat.debug()
00681       << *this << "::press(" << param << ", " << background << ")\n";
00682   }
00683 
00684   if (!background) {
00685     PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00686     string event;
00687     if (param.is_keyrepeat()) {
00688       event = get_repeat_event(param.get_button());
00689     } else {
00690       event = get_press_event(param.get_button());
00691     }
00692     play_sound(event);
00693     throw_event(event, EventParameter(ep));
00694   }
00695 
00696   if (has_notify()) {
00697     get_notify()->item_press(this, param);
00698   }
00699 }
00700 
00701 ////////////////////////////////////////////////////////////////////
00702 //     Function: PGItem::release
00703 //       Access: Public, Virtual
00704 //  Description: This is a callback hook function, called whenever a
00705 //               mouse or keyboard button previously depressed with
00706 //               press() is released.
00707 ////////////////////////////////////////////////////////////////////
00708 void PGItem::
00709 release(const MouseWatcherParameter &param, bool background) {
00710   LightReMutexHolder holder(_lock);
00711   if (pgui_cat.is_debug()) {
00712     pgui_cat.debug()
00713       << *this << "::release(" << param << ", " << background << ")\n";
00714   }
00715 
00716   if (!background) {
00717     PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00718     string event = get_release_event(param.get_button());
00719     play_sound(event);
00720     throw_event(event, EventParameter(ep));
00721   }
00722 
00723   if (has_notify()) {
00724     get_notify()->item_release(this, param);
00725   }
00726 }
00727 
00728 ////////////////////////////////////////////////////////////////////
00729 //     Function: PGItem::keystroke
00730 //       Access: Public, Virtual
00731 //  Description: This is a callback hook function, called whenever
00732 //               the user presses a key.
00733 ////////////////////////////////////////////////////////////////////
00734 void PGItem::
00735 keystroke(const MouseWatcherParameter &param, bool background) {
00736   LightReMutexHolder holder(_lock);
00737   if (pgui_cat.is_debug()) {
00738     pgui_cat.debug()
00739       << *this << "::keystroke(" << param << ", " << background << ")\n";
00740   }
00741 
00742   if (!background) {
00743     PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00744     string event = get_keystroke_event();
00745     play_sound(event);
00746     throw_event(event, EventParameter(ep));
00747 
00748     if (has_notify()) {
00749       get_notify()->item_keystroke(this, param);
00750     }
00751   }
00752 }
00753 
00754 ////////////////////////////////////////////////////////////////////
00755 //     Function: PGItem::candidate
00756 //       Access: Public, Virtual
00757 //  Description: This is a callback hook function, called whenever
00758 //               the user highlights an option in the IME window.
00759 ////////////////////////////////////////////////////////////////////
00760 void PGItem::
00761 candidate(const MouseWatcherParameter &param, bool background) {
00762   LightReMutexHolder holder(_lock);
00763   if (pgui_cat.is_debug()) {
00764     pgui_cat.debug()
00765       << *this << "::candidate(" << param << ", " << background << ")\n";
00766   }
00767 
00768   // We don't throw sound events for candidate selections for now.
00769   if (!background) {
00770     if (has_notify()) {
00771       get_notify()->item_candidate(this, param);
00772     }
00773   }
00774 }
00775 
00776 ////////////////////////////////////////////////////////////////////
00777 //     Function: PGItem::move
00778 //       Access: Public, Virtual
00779 //  Description: This is a callback hook function, called whenever a
00780 //               mouse is moved while within the region.
00781 ////////////////////////////////////////////////////////////////////
00782 void PGItem::
00783 move(const MouseWatcherParameter &param) {
00784   LightReMutexHolder holder(_lock);
00785   if (pgui_cat.is_debug()) {
00786     pgui_cat.debug()
00787       << *this << "::move(" << param << ")\n";
00788   }
00789 
00790   if (has_notify()) {
00791     get_notify()->item_move(this, param);
00792   }
00793 }
00794 
00795 ////////////////////////////////////////////////////////////////////
00796 //     Function: PGItem::background_press
00797 //       Access: Public, Static
00798 //  Description: Calls press() on all the PGItems with background
00799 //               focus.
00800 ////////////////////////////////////////////////////////////////////
00801 void PGItem::
00802 background_press(const MouseWatcherParameter &param) {
00803   BackgroundFocus::const_iterator fi;
00804   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00805     PGItem *item = *fi;
00806     if (!item->get_focus()) {
00807       item->press(param, true);
00808     }
00809   }
00810 }
00811 
00812 ////////////////////////////////////////////////////////////////////
00813 //     Function: PGItem::background_release
00814 //       Access: Public, Static
00815 //  Description: Calls release() on all the PGItems with background
00816 //               focus.
00817 ////////////////////////////////////////////////////////////////////
00818 void PGItem::
00819 background_release(const MouseWatcherParameter &param) {
00820   BackgroundFocus::const_iterator fi;
00821   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00822     PGItem *item = *fi;
00823     if (!item->get_focus()) {
00824       item->release(param, true);
00825     }
00826   }
00827 }
00828 
00829 ////////////////////////////////////////////////////////////////////
00830 //     Function: PGItem::background_keystroke
00831 //       Access: Public, Static
00832 //  Description: Calls keystroke() on all the PGItems with background
00833 //               focus.
00834 ////////////////////////////////////////////////////////////////////
00835 void PGItem::
00836 background_keystroke(const MouseWatcherParameter &param) {
00837   BackgroundFocus::const_iterator fi;
00838   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00839     PGItem *item = *fi;
00840     if (!item->get_focus()) {
00841       item->keystroke(param, true);
00842     }
00843   }
00844 }
00845 
00846 ////////////////////////////////////////////////////////////////////
00847 //     Function: PGItem::background_candidate
00848 //       Access: Public, Static
00849 //  Description: Calls candidate() on all the PGItems with background
00850 //               focus.
00851 ////////////////////////////////////////////////////////////////////
00852 void PGItem::
00853 background_candidate(const MouseWatcherParameter &param) {
00854   BackgroundFocus::const_iterator fi;
00855   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00856     PGItem *item = *fi;
00857     if (!item->get_focus()) {
00858       item->candidate(param, true);
00859     }
00860   }
00861 }
00862 
00863 ////////////////////////////////////////////////////////////////////
00864 //     Function: PGItem::set_active
00865 //       Access: Published, Virtual
00866 //  Description: Sets whether the PGItem is active for mouse watching.
00867 //               This is not necessarily related to the
00868 //               active/inactive appearance of the item, which is
00869 //               controlled by set_state(), but it does affect whether
00870 //               it responds to mouse events.
00871 ////////////////////////////////////////////////////////////////////
00872 void PGItem::
00873 set_active(bool active) {
00874   LightReMutexHolder holder(_lock);
00875   if (active) {
00876     _flags |= F_active;
00877   } else {
00878     _flags &= ~F_active;
00879     // Deactivating the item automatically defocuses it too.
00880     if (get_focus()) {
00881       set_focus(false);
00882     }
00883   }
00884 }
00885 
00886 ////////////////////////////////////////////////////////////////////
00887 //     Function: PGItem::set_focus
00888 //       Access: Published, Virtual
00889 //  Description: Sets whether the PGItem currently has keyboard focus.
00890 //               This simply means that the item may respond to
00891 //               keyboard events as well as to mouse events; precisely
00892 //               what this means is up to the individual item.  
00893 //
00894 //               Only one PGItem in the world is allowed to have focus
00895 //               at any given time.  Setting the focus on any other
00896 //               item automatically disables the focus from the
00897 //               previous item.
00898 ////////////////////////////////////////////////////////////////////
00899 void PGItem::
00900 set_focus(bool focus) {
00901   LightReMutexHolder holder(_lock);
00902   if (focus) {
00903     if (!get_active()) {
00904       // Cannot set focus on an inactive item.
00905       return;
00906     }
00907 
00908     // Set the keyboard focus to this item.
00909     if (_focus_item != this) {
00910       if (_focus_item != (PGItem *)NULL) {
00911         // Clear the focus from whatever item currently has it.
00912         _focus_item->set_focus(false);
00913       }
00914       _focus_item = this;
00915     }
00916     if (!get_focus()) {
00917       focus_in();
00918       _flags |= F_focus;
00919     }
00920 
00921   } else {
00922     if (_focus_item == this) {
00923       // Remove this item from the focus.
00924       _focus_item = (PGItem *)NULL;
00925     }
00926 
00927     if (get_focus()) {
00928       focus_out();
00929       _flags &= ~F_focus;
00930     }
00931   }
00932   _region->set_keyboard(focus);
00933 }
00934 
00935 ////////////////////////////////////////////////////////////////////
00936 //     Function: PGItem::set_background_focus
00937 //       Access: Published
00938 //  Description: Sets the background_focus flag for this item.  When
00939 //               background_focus is enabled, the item will receive
00940 //               keypress events even if it is not in focus; in fact,
00941 //               even if it is not onscreen.  Unlike normal focus,
00942 //               many items may have background_focus simultaneously.
00943 ////////////////////////////////////////////////////////////////////
00944 void PGItem::
00945 set_background_focus(bool focus) {
00946   LightReMutexHolder holder(_lock);
00947   if (focus != get_background_focus()) {
00948     if (focus) {
00949       // Activate background focus.
00950       _flags |= F_background_focus;
00951       bool inserted = _background_focus.insert(this).second;
00952       nassertv(inserted);
00953 
00954     } else {
00955       // Deactivate background focus.
00956       _flags &= ~F_background_focus;
00957       size_t num_erased = _background_focus.erase(this);
00958       nassertv(num_erased == 1);
00959     }
00960   }
00961 }
00962 
00963 ////////////////////////////////////////////////////////////////////
00964 //     Function: PGItem::get_num_state_defs
00965 //       Access: Published
00966 //  Description: Returns one more than the highest-numbered state def
00967 //               that was ever assigned to the PGItem.  The complete
00968 //               set of state defs assigned may then be retrieved by
00969 //               indexing from 0 to (get_num_state_defs() - 1).
00970 //
00971 //               This is only an upper limit on the actual number of
00972 //               state defs, since there may be holes in the list.
00973 ////////////////////////////////////////////////////////////////////
00974 int PGItem::
00975 get_num_state_defs() const {
00976   LightReMutexHolder holder(_lock);
00977   return _state_defs.size();
00978 }
00979 
00980 ////////////////////////////////////////////////////////////////////
00981 //     Function: PGItem::has_state_def
00982 //       Access: Published
00983 //  Description: Returns true if get_state_def() has ever been called
00984 //               for the indicated state (thus defining a render
00985 //               subgraph for this state index), false otherwise.
00986 ////////////////////////////////////////////////////////////////////
00987 bool PGItem::
00988 has_state_def(int state) const {
00989   LightReMutexHolder holder(_lock);
00990   if (state < 0 || state >= (int)_state_defs.size()) {
00991     return false;
00992   }
00993   return (!_state_defs[state]._root.is_empty());
00994 }
00995 
00996 ////////////////////////////////////////////////////////////////////
00997 //     Function: PGItem::clear_state_def
00998 //       Access: Published
00999 //  Description: Resets the NodePath assigned to the indicated state
01000 //               to its initial default, with only a frame
01001 //               representation if appropriate.
01002 ////////////////////////////////////////////////////////////////////
01003 void PGItem::
01004 clear_state_def(int state) {
01005   LightReMutexHolder holder(_lock);
01006   if (state < 0 || state >= (int)_state_defs.size()) {
01007     return;
01008   }
01009 
01010   _state_defs[state]._root = NodePath();
01011   _state_defs[state]._frame = NodePath();
01012   _state_defs[state]._frame_stale = true;
01013 
01014   mark_internal_bounds_stale();
01015 }
01016 
01017 ////////////////////////////////////////////////////////////////////
01018 //     Function: PGItem::get_state_def
01019 //       Access: Published
01020 //  Description: Returns the Node that is the root of the subgraph
01021 //               that will be drawn when the PGItem is in the
01022 //               indicated state.  The first time this is called for a
01023 //               particular state index, it may create the Node.
01024 ////////////////////////////////////////////////////////////////////
01025 NodePath &PGItem::
01026 get_state_def(int state) {
01027   LightReMutexHolder holder(_lock);
01028   nassertr(state >= 0 && state < 1000, get_state_def(0));  // Sanity check.
01029   slot_state_def(state);
01030 
01031   if (_state_defs[state]._root.is_empty()) {
01032     // Create a new node.
01033     _state_defs[state]._root = NodePath("state_" + format_string(state));
01034     _state_defs[state]._frame_stale = true;
01035   }
01036 
01037   if (_state_defs[state]._frame_stale) {
01038     update_frame(state);
01039   }
01040 
01041   return _state_defs[state]._root;
01042 }
01043 
01044 ////////////////////////////////////////////////////////////////////
01045 //     Function: PGItem::instance_to_state_def
01046 //       Access: Published
01047 //  Description: Parents an instance of the bottom node of the
01048 //               indicated NodePath to the indicated state index.
01049 ////////////////////////////////////////////////////////////////////
01050 NodePath PGItem::
01051 instance_to_state_def(int state, const NodePath &path) {
01052   LightReMutexHolder holder(_lock);
01053   if (path.is_empty()) {
01054     // If the source is empty, quietly do nothing.
01055     return NodePath();
01056   }
01057 
01058   mark_internal_bounds_stale();
01059 
01060   return path.instance_to(get_state_def(state));
01061 }
01062 
01063 ////////////////////////////////////////////////////////////////////
01064 //     Function: PGItem::get_frame_style
01065 //       Access: Published
01066 //  Description: Returns the kind of frame that will be drawn behind
01067 //               the item when it is in the indicated state.
01068 ////////////////////////////////////////////////////////////////////
01069 PGFrameStyle PGItem::
01070 get_frame_style(int state) {
01071   LightReMutexHolder holder(_lock);
01072   if (state < 0 || state >= (int)_state_defs.size()) {
01073     return PGFrameStyle();
01074   }
01075   return _state_defs[state]._frame_style;
01076 }
01077 
01078 ////////////////////////////////////////////////////////////////////
01079 //     Function: PGItem::set_frame_style
01080 //       Access: Published
01081 //  Description: Changes the kind of frame that will be drawn behind
01082 //               the item when it is in the indicated state.
01083 ////////////////////////////////////////////////////////////////////
01084 void PGItem::
01085 set_frame_style(int state, const PGFrameStyle &style) {
01086   LightReMutexHolder holder(_lock);
01087   // Get the state def node, mainly to ensure that this state is
01088   // slotted and listed as having been defined.
01089   NodePath &root = get_state_def(state);
01090   nassertv(!root.is_empty());
01091   
01092   _state_defs[state]._frame_style = style;
01093   _state_defs[state]._frame_stale = true;
01094     
01095   mark_internal_bounds_stale();
01096 }
01097 
01098 #ifdef HAVE_AUDIO
01099 ////////////////////////////////////////////////////////////////////
01100 //     Function: PGItem::set_sound
01101 //       Access: Published
01102 //  Description: Sets the sound that will be played whenever the
01103 //               indicated event occurs.
01104 ////////////////////////////////////////////////////////////////////
01105 void PGItem::
01106 set_sound(const string &event, AudioSound *sound) {
01107   LightReMutexHolder holder(_lock);
01108   _sounds[event] = sound;
01109 }
01110 
01111 ////////////////////////////////////////////////////////////////////
01112 //     Function: PGItem::clear_sound
01113 //       Access: Published
01114 //  Description: Removes the sound associated with the indicated
01115 //               event.
01116 ////////////////////////////////////////////////////////////////////
01117 void PGItem::
01118 clear_sound(const string &event) {
01119   LightReMutexHolder holder(_lock);
01120   _sounds.erase(event);
01121 }
01122 
01123 ////////////////////////////////////////////////////////////////////
01124 //     Function: PGItem::get_sound
01125 //       Access: Published
01126 //  Description: Returns the sound associated with the indicated
01127 //               event, or NULL if there is no associated sound.
01128 ////////////////////////////////////////////////////////////////////
01129 AudioSound *PGItem::
01130 get_sound(const string &event) const {
01131   LightReMutexHolder holder(_lock);
01132   Sounds::const_iterator si = _sounds.find(event);
01133   if (si != _sounds.end()) {
01134     return (*si).second;
01135   }
01136   return (AudioSound *)NULL;
01137 }
01138 
01139 ////////////////////////////////////////////////////////////////////
01140 //     Function: PGItem::has_sound
01141 //       Access: Published
01142 //  Description: Returns true if there is a sound associated with the
01143 //               indicated event, or false otherwise.
01144 ////////////////////////////////////////////////////////////////////
01145 bool PGItem::
01146 has_sound(const string &event) const {
01147   LightReMutexHolder holder(_lock);
01148   return (_sounds.count(event) != 0);
01149 }
01150 #endif  // HAVE_AUDIO
01151 
01152 ////////////////////////////////////////////////////////////////////
01153 //     Function: PGItem::get_text_node
01154 //       Access: Published, Static
01155 //  Description: Returns the TextNode object that will be used by all
01156 //               PGItems to generate default labels given a string.
01157 //               This can be loaded with the default font, etc.
01158 ////////////////////////////////////////////////////////////////////
01159 TextNode *PGItem::
01160 get_text_node() {
01161   if (_text_node == (TextNode *)NULL) {
01162     _text_node = new TextNode("pguiText");
01163     _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
01164 
01165     // The default TextNode is aligned to the left, for the
01166     // convenience of PGEntry.
01167     _text_node->set_align(TextNode::A_left);
01168   }
01169   return _text_node;
01170 }
01171 
01172 ////////////////////////////////////////////////////////////////////
01173 //     Function: PGItem::play_sound
01174 //       Access: Protected
01175 //  Description: Plays the sound associated with the indicated event,
01176 //               if there is one.
01177 ////////////////////////////////////////////////////////////////////
01178 void PGItem::
01179 play_sound(const string &event) {
01180 #ifdef HAVE_AUDIO
01181   LightReMutexHolder holder(_lock);
01182   Sounds::const_iterator si = _sounds.find(event);
01183   if (si != _sounds.end()) {
01184     AudioSound *sound = (*si).second;
01185     sound->play();
01186   }
01187 #endif  // HAVE_AUDIO
01188 }
01189 
01190 ////////////////////////////////////////////////////////////////////
01191 //     Function: PGItem::reduce_region
01192 //       Access: Protected
01193 //  Description: The frame parameter is an in/out parameter.  This
01194 //               function adjusts frame so that it represents the
01195 //               largest part of the rectangular region passed in,
01196 //               that does not overlap with the rectangular region of
01197 //               the indicated obscurer.  If the obscurer is NULL, or
01198 //               is a hidden node, it is not considered and the frame
01199 //               is left unchanged.
01200 //
01201 //               This is used by slider bars and scroll frames, which
01202 //               have to automatically figure out how much space they
01203 //               have to work with after allowing space for scroll
01204 //               bars and buttons.
01205 ////////////////////////////////////////////////////////////////////
01206 void PGItem::
01207 reduce_region(LVecBase4f &frame, PGItem *obscurer) const {
01208   if (obscurer != (PGItem *)NULL && !obscurer->is_overall_hidden()) {
01209     LVecBase4f oframe = get_relative_frame(obscurer);
01210 
01211     // Determine the four rectangular regions on the four sides of the
01212     // obscuring region.
01213     LVecBase4f right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
01214     LVecBase4f left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
01215     LVecBase4f above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
01216     LVecBase4f below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
01217 
01218     // Now choose the largest of those four.
01219     const LVecBase4f *largest = &right;
01220     float largest_area = compute_area(*largest);
01221     compare_largest(largest, largest_area, &left);
01222     compare_largest(largest, largest_area, &above);
01223     compare_largest(largest, largest_area, &below);
01224 
01225     frame = *largest;
01226   }
01227 }
01228 
01229 ////////////////////////////////////////////////////////////////////
01230 //     Function: PGItem::get_relative_frame
01231 //       Access: Protected
01232 //  Description: Returns the LVecBase4f frame of the indicated item,
01233 //               converted into this item's coordinate space.
01234 //               Presumably, item is a child of this node.
01235 ////////////////////////////////////////////////////////////////////
01236 LVecBase4f PGItem::
01237 get_relative_frame(PGItem *item) const {
01238   NodePath this_np = NodePath::any_path((PGItem *)this);
01239   NodePath item_np = this_np.find_path_to(item);
01240   if (item_np.is_empty()) { 
01241     item_np = NodePath::any_path(item);
01242   }
01243   const LVecBase4f &orig_frame = item->get_frame();
01244   LMatrix4f transform = item_np.get_mat(this_np);
01245   
01246   // Transform the item's frame into the PGScrollFrame's
01247   // coordinate space.  Transform all four vertices, and get the
01248   // new bounding box.  This way the region works (mostly) even if
01249   // has been rotated.
01250   LPoint3f ll(orig_frame[0], 0.0f, orig_frame[2]);
01251   LPoint3f lr(orig_frame[1], 0.0f, orig_frame[2]);
01252   LPoint3f ul(orig_frame[0], 0.0f, orig_frame[3]);
01253   LPoint3f ur(orig_frame[1], 0.0f, orig_frame[3]);
01254   ll = ll * transform;
01255   lr = lr * transform;
01256   ul = ul * transform;
01257   ur = ur * transform;
01258   
01259   return LVecBase4f(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
01260                     max(max(ll[0], lr[0]), max(ul[0], ur[0])),
01261                     min(min(ll[2], lr[2]), min(ul[2], ur[2])),
01262                     max(max(ll[2], lr[2]), max(ul[2], ur[2])));
01263 }
01264 
01265 ////////////////////////////////////////////////////////////////////
01266 //     Function: PGItem::mouse_to_local
01267 //       Access: Protected
01268 //  Description: Converts from the 2-d mouse coordinates into the
01269 //               coordinate space of the item.
01270 ////////////////////////////////////////////////////////////////////
01271 LPoint3f PGItem::
01272 mouse_to_local(const LPoint2f &mouse_point) const {
01273   // This is ambiguous if the PGItem has multiple instances.  Why
01274   // would you do that, anyway?
01275   NodePath this_np((PGItem *)this);
01276   CPT(TransformState) inv_transform = NodePath().get_transform(this_np);
01277   return inv_transform->get_mat().xform_point(LVector3f::rfu(mouse_point[0], 0, mouse_point[1]));
01278 }
01279 
01280 ////////////////////////////////////////////////////////////////////
01281 //     Function: PGItem::frame_changed
01282 //       Access: Protected, Virtual
01283 //  Description: Called when the user changes the frame size.
01284 ////////////////////////////////////////////////////////////////////
01285 void PGItem::
01286 frame_changed() {
01287   mark_frames_stale();
01288   if (has_notify()) {
01289     get_notify()->item_frame_changed(this);
01290   }
01291 }
01292 
01293 ////////////////////////////////////////////////////////////////////
01294 //     Function: PGItem::slot_state_def
01295 //       Access: Private
01296 //  Description: Ensures there is a slot in the array for the given
01297 //               state definition.
01298 ////////////////////////////////////////////////////////////////////
01299 void PGItem::
01300 slot_state_def(int state) {
01301   while (state >= (int)_state_defs.size()) {
01302     _state_defs.push_back(StateDef());
01303   }
01304 }
01305 
01306 ////////////////////////////////////////////////////////////////////
01307 //     Function: PGItem::update_frame
01308 //       Access: Private
01309 //  Description: Generates a new instance of the frame geometry for
01310 //               the indicated state.
01311 ////////////////////////////////////////////////////////////////////
01312 void PGItem::
01313 update_frame(int state) {
01314   // First, remove the old frame geometry, if any.
01315   if (state >= 0 && state < (int)_state_defs.size()) {
01316     _state_defs[state]._frame.remove_node();
01317   }
01318 
01319   // We must turn off the stale flag first, before we call
01320   // get_state_def(), to prevent get_state_def() from being a
01321   // recursive call.
01322   _state_defs[state]._frame_stale = false;
01323 
01324   // Now create new frame geometry.
01325   if (has_frame()) {
01326     NodePath &root = get_state_def(state);
01327     _state_defs[state]._frame = 
01328       _state_defs[state]._frame_style.generate_into(root, _frame);
01329   }
01330 }
01331 
01332 ////////////////////////////////////////////////////////////////////
01333 //     Function: PGItem::mark_frames_stale
01334 //       Access: Private
01335 //  Description: Marks all the frames in all states stale, so that
01336 //               they will be regenerated the next time each state is
01337 //               requested.
01338 ////////////////////////////////////////////////////////////////////
01339 void PGItem::
01340 mark_frames_stale() {
01341   StateDefs::iterator di;
01342   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
01343     // Remove the old frame, if any.
01344     (*di)._frame.remove_node();
01345     (*di)._frame_stale = true;
01346   }
01347   mark_internal_bounds_stale();
01348 }
01349 
01350 ////////////////////////////////////////////////////////////////////
01351 //     Function: PGItem::clip_frame
01352 //       Access: Private
01353 //  Description: Clips the four corners of the item's frame by the
01354 //               indicated clipping plane, and modifies the points to
01355 //               reflect the new set of clipped points.
01356 //
01357 //               The return value is true if the set of points is
01358 //               unmodified (all points are behind the clip plane), or
01359 //               false otherwise.
01360 ////////////////////////////////////////////////////////////////////
01361 bool PGItem::
01362 clip_frame(pvector<LPoint2f> &source_points, const Planef &plane) const {
01363   if (source_points.empty()) {
01364     return true;
01365   }
01366 
01367   LPoint3f from3d;
01368   LVector3f delta3d;
01369   if (!plane.intersects_plane(from3d, delta3d, Planef(LVector3f(0, 1, 0), LPoint3f::zero()))) {
01370     // The clipping plane is parallel to the polygon.  The polygon is
01371     // either all in or all out.
01372     if (plane.dist_to_plane(LPoint3f::zero()) < 0.0) {
01373       // A point within the polygon is behind the clipping plane: the
01374       // polygon is all in.
01375       return true;
01376     }
01377     return false;
01378   }
01379 
01380   // Project the line of intersection into the X-Z plane.  Now we have
01381   // a 2-d clipping line.
01382   LPoint2f from2d(from3d[0], from3d[2]);
01383   LVector2f delta2d(delta3d[0], delta3d[2]);
01384 
01385   float a = -delta2d[1];
01386   float b = delta2d[0];
01387   float c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
01388 
01389   // Now walk through the points.  Any point on the left of our line
01390   // gets removed, and the line segment clipped at the point of
01391   // intersection.
01392 
01393   // We might increase the number of vertices by as many as 1, if the
01394   // plane clips off exactly one corner.  (We might also decrease the
01395   // number of vertices, or keep them the same number.)
01396   pvector<LPoint2f> new_points;
01397   new_points.reserve(source_points.size() + 1);
01398 
01399   LPoint2f last_point = source_points.back();
01400   bool last_is_in = is_right(last_point - from2d, delta2d);
01401   bool all_in = last_is_in;
01402   pvector<LPoint2f>::const_iterator pi;
01403   for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
01404     const LPoint2f &this_point = (*pi);
01405     bool this_is_in = is_right(this_point - from2d, delta2d);
01406 
01407     // There appears to be a compiler bug in gcc 4.0: we need to
01408     // extract this comparison outside of the if statement.
01409     bool crossed_over = (this_is_in != last_is_in);
01410     if (crossed_over) {
01411       // We have just crossed over the clipping line.  Find the point
01412       // of intersection.
01413       LVector2f d = this_point - last_point;
01414       float denom = (a * d[0] + b * d[1]);
01415       if (denom != 0.0) {
01416         float t = -(a * last_point[0] + b * last_point[1] + c) / denom;
01417         LPoint2f p = last_point + t * d;
01418         
01419         new_points.push_back(p);
01420         last_is_in = this_is_in;
01421       }
01422     } 
01423 
01424     if (this_is_in) {
01425       // We are behind the clipping line.  Keep the point.
01426       new_points.push_back(this_point);
01427     } else {
01428       all_in = false;
01429     }
01430 
01431     last_point = this_point;
01432   }
01433 
01434   source_points.swap(new_points);
01435   return all_in;
01436 }
 All Classes Functions Variables Enumerations