Panda3D
 All Classes Functions Variables Enumerations
cMetaInterval.cxx
00001 // Filename: cMetaInterval.cxx
00002 // Created by:  drose (27Aug02)
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 "cMetaInterval.h"
00016 #include "waitInterval.h"
00017 #include "config_interval.h"
00018 #include "indirectLess.h"
00019 #include "indent.h"
00020 
00021 #include <algorithm>
00022 #include <math.h>   // for log10()
00023 #include <stdio.h>  // for sprintf()
00024 
00025 TypeHandle CMetaInterval::_type_handle;
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: CMetaInterval::Constructor
00029 //       Access: Published
00030 //  Description: 
00031 ////////////////////////////////////////////////////////////////////
00032 CMetaInterval::
00033 CMetaInterval(const string &name) :
00034   CInterval(name, 0.0, true)
00035 {
00036   _precision = interval_precision;
00037   _current_nesting_level = 0;
00038   _next_event_index = 0;
00039   _processing_events = false;
00040 }
00041 
00042 ////////////////////////////////////////////////////////////////////
00043 //     Function: CMetaInterval::Destructor
00044 //       Access: Published, Virtual
00045 //  Description: 
00046 ////////////////////////////////////////////////////////////////////
00047 CMetaInterval::
00048 ~CMetaInterval() {
00049   clear_intervals();
00050 }
00051 
00052 ////////////////////////////////////////////////////////////////////
00053 //     Function: CMetaInterval::clear_intervals
00054 //       Access: Published
00055 //  Description: Resets the list of intervals and prepares for
00056 //               receiving a new list.
00057 ////////////////////////////////////////////////////////////////////
00058 void CMetaInterval::
00059 clear_intervals() {
00060   // Better not do this unless you have serviced all of the
00061   // outstanding events!
00062   bool lost_events = false;
00063   if (!_event_queue.empty()) {
00064     interval_cat.warning()
00065       << "Losing outstanding events for " << *this << "\n";
00066     _event_queue.clear();
00067     lost_events = true;
00068   }
00069 
00070   clear_events();
00071 
00072   // Go through all of our nested intervals and remove ourselves as
00073   // their parent.
00074   Defs::iterator di;
00075   for (di = _defs.begin(); di != _defs.end(); ++di) {
00076     IntervalDef &def = (*di);
00077     if (def._c_interval != (CInterval *)NULL) {
00078       CInterval::Parents::iterator pi = 
00079         find(def._c_interval->_parents.begin(),
00080              def._c_interval->_parents.end(),
00081              this);
00082       nassertv(pi != def._c_interval->_parents.end());
00083       def._c_interval->_parents.erase(pi);
00084     }
00085   }
00086   _defs.clear();
00087 
00088   _current_nesting_level = 0;
00089   _next_event_index = 0;
00090 
00091 #ifndef NDEBUG
00092   if (verify_intervals) {
00093     nassertv(!lost_events);
00094   }
00095 #endif
00096 }
00097 
00098 ////////////////////////////////////////////////////////////////////
00099 //     Function: CMetaInterval::push_level
00100 //       Access: Published
00101 //  Description: Marks the beginning of a nested level of child
00102 //               intervals.  Within the nested level, a RelativeStart
00103 //               time of RS_level_begin refers to the start of the
00104 //               level, and the first interval added within the level
00105 //               is always relative to the start of the level.
00106 //
00107 //               The return value is the index of the def entry
00108 //               created by this push.
00109 ////////////////////////////////////////////////////////////////////
00110 int CMetaInterval::
00111 push_level(const string &name, double rel_time, RelativeStart rel_to) {
00112   nassertr(_event_queue.empty() && !_processing_events, -1);
00113 
00114   _defs.push_back(IntervalDef());
00115   IntervalDef &def = _defs.back();
00116   def._type = DT_push_level;
00117   def._ext_name = name;
00118   def._rel_time = rel_time;
00119   def._rel_to = rel_to;
00120   _current_nesting_level++;
00121   mark_dirty();
00122 
00123   return (int)_defs.size() - 1;
00124 }
00125 
00126 ////////////////////////////////////////////////////////////////////
00127 //     Function: CMetaInterval::add_c_interval
00128 //       Access: Published
00129 //  Description: Adds a new CInterval to the list.  The interval will
00130 //               be played when the indicated time (relative to the
00131 //               given point) has been reached.
00132 //
00133 //               The return value is the index of the def entry
00134 //               representing the new interval.
00135 ////////////////////////////////////////////////////////////////////
00136 int CMetaInterval::
00137 add_c_interval(CInterval *c_interval, 
00138                double rel_time, RelativeStart rel_to) {
00139   nassertr(_event_queue.empty() && !_processing_events, -1);
00140   nassertr(c_interval != (CInterval *)NULL, -1);
00141 
00142   c_interval->_parents.push_back(this);
00143   c_interval->_ival_pcollector = PStatCollector(_ival_pcollector, c_interval->_pname);
00144   _defs.push_back(IntervalDef());
00145   IntervalDef &def = _defs.back();
00146   def._type = DT_c_interval;
00147   def._c_interval = c_interval;
00148   def._rel_time = rel_time;
00149   def._rel_to = rel_to;
00150   mark_dirty();
00151 
00152   return (int)_defs.size() - 1;
00153 }
00154 
00155 ////////////////////////////////////////////////////////////////////
00156 //     Function: CMetaInterval::add_ext_index
00157 //       Access: Published
00158 //  Description: Adds a new external interval to the list.  This
00159 //               represents some object in the external scripting
00160 //               language that has properties similar to a CInterval
00161 //               (for instance, a Python Interval object).
00162 //
00163 //               The CMetaInterval object cannot play this external
00164 //               interval directly, but it records a placeholder for
00165 //               it and will ask the scripting language to play it
00166 //               when it is time, via is_event_ready() and related
00167 //               methods.
00168 //
00169 //               The ext_index number itself is simply a handle that
00170 //               the scripting language makes up and associates with
00171 //               its interval object somehow.  The CMetaInterval
00172 //               object does not attempt to interpret this value.
00173 //
00174 //               The return value is the index of the def entry
00175 //               representing the new interval.
00176 ////////////////////////////////////////////////////////////////////
00177 int CMetaInterval::
00178 add_ext_index(int ext_index, const string &name, double duration,
00179               bool open_ended,
00180               double rel_time, RelativeStart rel_to) {
00181   nassertr(_event_queue.empty() && !_processing_events, -1);
00182 
00183   _defs.push_back(IntervalDef());
00184   IntervalDef &def = _defs.back();
00185   def._type = DT_ext_index;
00186   def._ext_index = ext_index;
00187   def._ext_name = name;
00188   def._ext_duration = duration;
00189   def._ext_open_ended = open_ended;
00190   def._rel_time = rel_time;
00191   def._rel_to = rel_to;
00192   mark_dirty();
00193 
00194   return (int)_defs.size() - 1;
00195 }
00196 
00197 ////////////////////////////////////////////////////////////////////
00198 //     Function: CMetaInterval::pop_level
00199 //       Access: Published
00200 //  Description: Finishes a level marked by a previous call to
00201 //               push_level(), and returns to the previous level.
00202 //
00203 //               If the duration is not negative, it represents a
00204 //               phony duration to assign to the level, for the
00205 //               purposes of sequencing later intervals.  Otherwise,
00206 //               the level's duration is computed based on the
00207 //               intervals within the level.
00208 ////////////////////////////////////////////////////////////////////
00209 int CMetaInterval::
00210 pop_level(double duration) {
00211   nassertr(_event_queue.empty() && !_processing_events, -1);
00212   nassertr(_current_nesting_level > 0, -1);
00213 
00214   _defs.push_back(IntervalDef());
00215   IntervalDef &def = _defs.back();
00216   def._type = DT_pop_level;
00217   def._ext_duration = duration;
00218   _current_nesting_level--;
00219   mark_dirty();
00220 
00221   return (int)_defs.size() - 1;
00222 }
00223 
00224 ////////////////////////////////////////////////////////////////////
00225 //     Function: CMetaInterval::set_interval_start_time
00226 //       Access: Published
00227 //  Description: Adjusts the start time of the child interval with the
00228 //               given name, if found.  This may be either a C++
00229 //               interval added via add_c_interval(), or an external
00230 //               interval added via add_ext_index(); the name must
00231 //               match exactly.
00232 //
00233 //               If the interval is found, its start time is adjusted,
00234 //               and all subsequent intervals are adjusting
00235 //               accordingly, and true is returned.  If a matching
00236 //               interval is not found, nothing is changed and false
00237 //               is returned.
00238 ////////////////////////////////////////////////////////////////////
00239 bool CMetaInterval::
00240 set_interval_start_time(const string &name, double rel_time,
00241                         CMetaInterval::RelativeStart rel_to) {
00242   nassertr(_event_queue.empty() && !_processing_events, false);
00243   Defs::iterator di;
00244   for (di = _defs.begin(); di != _defs.end(); ++di) {
00245     IntervalDef &def = (*di);
00246 
00247     bool match = false;
00248     switch (def._type) {
00249     case DT_c_interval:
00250       match = (def._c_interval->get_name() == name);
00251       break;
00252 
00253     case DT_ext_index:
00254       match = (def._ext_name == name);
00255       break;
00256 
00257     default:
00258       break;
00259     }
00260     if (match) {
00261       // Here's the interval.
00262       def._rel_time = rel_time;
00263       def._rel_to = rel_to;
00264       mark_dirty();
00265       return true;
00266     }
00267   }
00268 
00269   return false;
00270 }
00271 
00272 ////////////////////////////////////////////////////////////////////
00273 //     Function: CMetaInterval::get_interval_start_time
00274 //       Access: Published
00275 //  Description: Returns the actual start time, relative to the
00276 //               beginning of the interval, of the child interval with
00277 //               the given name, if found, or -1 if the interval is
00278 //               not found.
00279 ////////////////////////////////////////////////////////////////////
00280 double CMetaInterval::
00281 get_interval_start_time(const string &name) const {
00282   recompute();
00283   Defs::const_iterator di;
00284   for (di = _defs.begin(); di != _defs.end(); ++di) {
00285     const IntervalDef &def = (*di);
00286 
00287     bool match = false;
00288     switch (def._type) {
00289     case DT_c_interval:
00290       match = (def._c_interval->get_name() == name);
00291       break;
00292 
00293     case DT_ext_index:
00294       match = (def._ext_name == name);
00295       break;
00296 
00297     default:
00298       break;
00299     }
00300     if (match) {
00301       // Here's the interval.
00302       return int_to_double_time(def._actual_begin_time);
00303     }
00304   }
00305 
00306   return -1.0;
00307 }
00308 
00309 ////////////////////////////////////////////////////////////////////
00310 //     Function: CMetaInterval::get_interval_end_time
00311 //       Access: Published
00312 //  Description: Returns the actual end time, relative to the
00313 //               beginning of the interval, of the child interval with
00314 //               the given name, if found, or -1 if the interval is
00315 //               not found.
00316 ////////////////////////////////////////////////////////////////////
00317 double CMetaInterval::
00318 get_interval_end_time(const string &name) const {
00319   recompute();
00320   Defs::const_iterator di;
00321   for (di = _defs.begin(); di != _defs.end(); ++di) {
00322     const IntervalDef &def = (*di);
00323 
00324     bool match = false;
00325     double duration = 0.0;
00326     switch (def._type) {
00327     case DT_c_interval:
00328       duration = def._c_interval->get_duration();
00329       match = (def._c_interval->get_name() == name);
00330       break;
00331 
00332     case DT_ext_index:
00333       duration = def._ext_duration;
00334       match = (def._ext_name == name);
00335       break;
00336 
00337     default:
00338       break;
00339     }
00340     if (match) {
00341       // Here's the interval.
00342       return int_to_double_time(def._actual_begin_time) + duration;
00343     }
00344   }
00345 
00346   return -1.0;
00347 }
00348 
00349 ////////////////////////////////////////////////////////////////////
00350 //     Function: CMetaInterval::initialize
00351 //       Access: Published, Virtual
00352 //  Description: This replaces the first call to priv_step(), and indicates
00353 //               that the interval has just begun.  This may be
00354 //               overridden by derived classes that need to do some
00355 //               explicit initialization on the first call.
00356 ////////////////////////////////////////////////////////////////////
00357 void CMetaInterval::
00358 priv_initialize(double t) {
00359   if (_processing_events) {
00360     enqueue_self_event(ET_initialize, t);
00361     return;
00362   }
00363 
00364   check_stopped(get_class_type(), "priv_initialize");
00365   // It may be tempting to flush the event_queue here, but don't do
00366   // it.  Those are events that must still be serviced from some
00367   // previous interval operation.  Throwing them away would be a
00368   // mistake.
00369 
00370   recompute();
00371   _next_event_index = 0;
00372   _active.clear();
00373 
00374   int now = double_to_int_time(t);
00375 
00376   /*
00377   // One special case: if we step to t == 0.0, it really means to the
00378   // very beginning of the interval, *before* any events that occurred
00379   // at time 0.  (Most of the time, stepping to a particular time
00380   // means *after* any events that occurred at that time.)
00381   if (t == 0.0) {
00382     now = -1;
00383   }
00384   */
00385 
00386   // Now look for events from the beginning up to the current time.
00387   _processing_events = true;
00388   ActiveEvents new_active;
00389   while (_next_event_index < _events.size() &&
00390          _events[_next_event_index]->_time <= now) {
00391     PlaybackEvent *event = _events[_next_event_index];
00392     _next_event_index++;
00393     
00394     // Do the indicated event.
00395     do_event_forward(event, new_active, true);
00396   }
00397   finish_events_forward(now, new_active);
00398   _processing_events = false;
00399 
00400   _curr_t = t;
00401   _state = S_started;
00402 }
00403 
00404 ////////////////////////////////////////////////////////////////////
00405 //     Function: CMetaInterval::instant
00406 //       Access: Published, Virtual
00407 //  Description: This is called in lieu of priv_initialize() .. priv_step()
00408 //               .. priv_finalize(), when everything is to happen within
00409 //               one frame.  The interval should initialize itself,
00410 //               then leave itself in the final state.
00411 ////////////////////////////////////////////////////////////////////
00412 void CMetaInterval::
00413 priv_instant() {
00414   if (_processing_events) {
00415     enqueue_self_event(ET_instant);
00416     return;
00417   }
00418 
00419   check_stopped(get_class_type(), "priv_instant");
00420   recompute();
00421   _active.clear();
00422 
00423   // Apply all of the events.  This just means we invoke "instant" for
00424   // any end or instant event, ignoring the begin events.
00425   _processing_events = true;
00426   PlaybackEvents::iterator ei;
00427   for (ei = _events.begin(); ei != _events.end(); ++ei) {
00428     PlaybackEvent *event = (*ei);
00429     if (event->_type != PET_begin) {
00430       enqueue_event(event->_n, ET_instant, true, 0);
00431     }
00432   }
00433   _processing_events = false;
00434 
00435   _next_event_index = _events.size();
00436   _curr_t = get_duration();
00437   _state = S_final;
00438 
00439   if (_event_queue.empty()) {
00440     interval_done();
00441   } else {
00442     enqueue_done_event();
00443   }
00444 }
00445 
00446 ////////////////////////////////////////////////////////////////////
00447 //     Function: CMetaInterval::step
00448 //       Access: Published, Virtual
00449 //  Description: Advances the time on the interval.  The time may
00450 //               either increase (the normal case) or decrease
00451 //               (e.g. if the interval is being played by a slider).
00452 ////////////////////////////////////////////////////////////////////
00453 void CMetaInterval::
00454 priv_step(double t) {
00455   if (_processing_events) {
00456     enqueue_self_event(ET_step, t);
00457     return;
00458   }
00459 
00460   check_started(get_class_type(), "priv_step");
00461   int now = double_to_int_time(t);
00462 
00463   /*
00464   // One special case: if we step to t == 0.0, it really means to the
00465   // very beginning of the interval, *before* any events that occurred
00466   // at time 0.  (Most of the time, stepping to a particular time
00467   // means *after* any events that occurred at that time.)
00468   if (t == 0.0) {
00469     now = -1;
00470   }
00471   */
00472 
00473   // Now look for events between the last time we ran and the current
00474   // time.
00475 
00476   _processing_events = true;
00477   if (_next_event_index < _events.size() &&
00478       _events[_next_event_index]->_time <= now) {
00479     // The normal case: time is increasing.
00480     ActiveEvents new_active;
00481     while (_next_event_index < _events.size() &&
00482            _events[_next_event_index]->_time <= now) {
00483       PlaybackEvent *event = _events[_next_event_index];
00484       _next_event_index++;
00485 
00486       // Do the indicated event.
00487       do_event_forward(event, new_active, false);
00488     }
00489 
00490     finish_events_forward(now, new_active);
00491 
00492   } else {
00493     // A less usual case: time is decreasing.
00494     ActiveEvents new_active;
00495     while (_next_event_index > 0 && 
00496            _events[_next_event_index - 1]->_time > now) {
00497       _next_event_index--;
00498       PlaybackEvent *event = _events[_next_event_index];
00499 
00500       do_event_reverse(event, new_active, false);
00501     }
00502 
00503     finish_events_reverse(now, new_active);
00504   }
00505   _processing_events = false;
00506 
00507   _curr_t = t;
00508   _state = S_started;
00509 }
00510 
00511 ////////////////////////////////////////////////////////////////////
00512 //     Function: CMetaInterval::finalize
00513 //       Access: Published, Virtual
00514 //  Description: This is called when an interval is interrupted.  It
00515 //               should advance the time as if priv_step() were called, and
00516 //               also perform whatever cleanup might be required.
00517 ////////////////////////////////////////////////////////////////////
00518 void CMetaInterval::
00519 priv_finalize() {
00520   if (_processing_events) {
00521     enqueue_self_event(ET_finalize);
00522     return;
00523   }
00524 
00525   double duration = get_duration();
00526   if (_state == S_initial) {
00527     priv_initialize(duration);
00528   }
00529 
00530   // Do all remaining events.
00531   _processing_events = true;
00532   ActiveEvents new_active;
00533   while (_next_event_index < _events.size()) {
00534     PlaybackEvent *event = _events[_next_event_index];
00535     _next_event_index++;
00536 
00537     // Do the indicated event.
00538     do_event_forward(event, new_active, true);
00539   }
00540   finish_events_forward(double_to_int_time(duration), new_active);
00541   _processing_events = false;
00542 
00543   _curr_t = duration;
00544   _state = S_final;
00545 
00546   if (_event_queue.empty()) {
00547     interval_done();
00548   } else {
00549     enqueue_done_event();
00550   }
00551 }
00552 
00553 ////////////////////////////////////////////////////////////////////
00554 //     Function: CMetaInterval::reverse_initialize
00555 //       Access: Published, Virtual
00556 //  Description: Similar to priv_initialize(), but this is called when the
00557 //               interval is being played backwards; it indicates that
00558 //               the interval should start at the finishing state and
00559 //               undo any intervening intervals.
00560 ////////////////////////////////////////////////////////////////////
00561 void CMetaInterval::
00562 priv_reverse_initialize(double t) {
00563   if (_processing_events) {
00564     enqueue_self_event(ET_reverse_initialize, t);
00565     return;
00566   }
00567 
00568   check_stopped(get_class_type(), "priv_reverse_initialize");
00569   // It may be tempting to flush the event_queue here, but don't do
00570   // it.  Those are events that must still be serviced from some
00571   // previous interval operation.  Throwing them away would be a
00572   // mistake.
00573 
00574   recompute();
00575   _next_event_index = _events.size();
00576   _active.clear();
00577 
00578   int now = double_to_int_time(t);
00579 
00580   /*
00581   // One special case: if we step to t == 0.0, it really means to the
00582   // very beginning of the interval, *before* any events that occurred
00583   // at time 0.  (Most of the time, stepping to a particular time
00584   // means *after* any events that occurred at that time.)
00585   if (t == 0.0) {
00586     now = -1;
00587   }
00588   */
00589 
00590   // Now look for events from the end down to the current time.
00591   _processing_events = true;
00592   ActiveEvents new_active;
00593   while (_next_event_index > 0 && 
00594          _events[_next_event_index - 1]->_time > now) {
00595     _next_event_index--;
00596     PlaybackEvent *event = _events[_next_event_index];
00597     
00598     // Do the indicated event.
00599     do_event_reverse(event, new_active, true);
00600   }
00601   finish_events_reverse(now, new_active);
00602   _processing_events = false;
00603 
00604   _curr_t = t;
00605   _state = S_started;
00606 }
00607 
00608 ////////////////////////////////////////////////////////////////////
00609 //     Function: CMetaInterval::reverse_instant
00610 //       Access: Published, Virtual
00611 //  Description: This is called in lieu of priv_reverse_initialize()
00612 //               .. priv_step() .. priv_reverse_finalize(), when everything is
00613 //               to happen within one frame.  The interval should
00614 //               initialize itself, then leave itself in the initial
00615 //               state.
00616 ////////////////////////////////////////////////////////////////////
00617 void CMetaInterval::
00618 priv_reverse_instant() {
00619   if (_processing_events) {
00620     enqueue_self_event(ET_reverse_instant);
00621     return;
00622   }
00623 
00624   check_stopped(get_class_type(), "priv_reverse_instant");
00625   recompute();
00626   _active.clear();
00627 
00628   // Apply all of the events.  This just means we invoke "instant" for
00629   // any end or instant event, ignoring the begin events.
00630   _processing_events = true;
00631   PlaybackEvents::reverse_iterator ei;
00632   for (ei = _events.rbegin(); ei != _events.rend(); ++ei) {
00633     PlaybackEvent *event = (*ei);
00634     if (event->_type != PET_begin) {
00635       enqueue_event(event->_n, ET_reverse_instant, true, 0);
00636     }
00637   }
00638   _processing_events = false;
00639 
00640   _next_event_index = 0;
00641   _curr_t = 0.0;
00642   _state = S_initial;
00643 }
00644 
00645 ////////////////////////////////////////////////////////////////////
00646 //     Function: CMetaInterval::reverse_finalize
00647 //       Access: Published, Virtual
00648 //  Description: Called generally following a priv_reverse_initialize(),
00649 //               this indicates the interval should set itself to the
00650 //               initial state.
00651 ////////////////////////////////////////////////////////////////////
00652 void CMetaInterval::
00653 priv_reverse_finalize() {
00654   if (_processing_events) {
00655     enqueue_self_event(ET_reverse_finalize);
00656     return;
00657   }
00658 
00659   if (_state == S_initial) {
00660     priv_initialize(0.0);
00661   }
00662 
00663   // Do all remaining events at the beginning.
00664   _processing_events = true;
00665   ActiveEvents new_active;
00666 
00667   while (_next_event_index > 0) {
00668     _next_event_index--;
00669     PlaybackEvent *event = _events[_next_event_index];
00670 
00671     do_event_reverse(event, new_active, true);
00672   }
00673   finish_events_reverse(0, new_active);
00674   _processing_events = false;
00675 
00676   _curr_t = 0.0;
00677   _state = S_initial;
00678 }
00679 
00680 ////////////////////////////////////////////////////////////////////
00681 //     Function: CMetaInterval::interrupt
00682 //       Access: Published, Virtual
00683 //  Description: This is called while the interval is playing to
00684 //               indicate that it is about to be interrupted; that is,
00685 //               priv_step() will not be called for a length of time.  But
00686 //               the interval should remain in its current state in
00687 //               anticipation of being eventually restarted when the
00688 //               calls to priv_step() eventually resume.
00689 //
00690 //               The purpose of this function is to allow self-running
00691 //               intervals like sound intervals to stop the actual
00692 //               sound playback during the pause.
00693 ////////////////////////////////////////////////////////////////////
00694 void CMetaInterval::
00695 priv_interrupt() {
00696   if (_processing_events) {
00697     enqueue_self_event(ET_interrupt);
00698     return;
00699   }
00700 
00701   _processing_events = true;
00702   ActiveEvents::iterator ai;
00703   for (ai = _active.begin(); ai != _active.end(); ++ai) {
00704     PlaybackEvent *event = (*ai);
00705     enqueue_event(event->_n, ET_interrupt, false);
00706   }
00707   _processing_events = false;
00708 
00709   if (_state == S_started) {
00710     _state = S_paused;
00711   }
00712 }
00713 
00714 ////////////////////////////////////////////////////////////////////
00715 //     Function: CMetaInterval::pop_event
00716 //       Access: Published
00717 //  Description: Acknowledges that the external interval on the top of
00718 //               the queue has been extracted, and is about to be
00719 //               serviced by the scripting language.  This prepares
00720 //               the interval so the next call to is_event_ready()
00721 //               will return information about the next external
00722 //               interval on the queue, if any.
00723 ////////////////////////////////////////////////////////////////////
00724 void CMetaInterval::
00725 pop_event() {
00726 #ifndef NDEBUG
00727   nassertv(!_event_queue.empty());
00728   const EventQueueEntry &entry = _event_queue.front();
00729   const IntervalDef &def = _defs[entry._n];
00730   nassertv(def._type == DT_ext_index);
00731 #endif
00732   _event_queue.pop_front();
00733 }
00734 
00735 ////////////////////////////////////////////////////////////////////
00736 //     Function: CMetaInterval::write
00737 //       Access: Published, Virtual
00738 //  Description: 
00739 ////////////////////////////////////////////////////////////////////
00740 void CMetaInterval::
00741 write(ostream &out, int indent_level) const {
00742   recompute();
00743 
00744   // How many digits of precision should we output for time?
00745   int num_decimals = (int)ceil(log10(_precision));
00746   int total_digits = num_decimals + 4;
00747   static const int max_digits = 32;  // totally arbitrary
00748   nassertv(total_digits <= max_digits);
00749   char format_str[12];
00750   sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
00751 
00752   indent(out, indent_level) << get_name() << ":\n";
00753 
00754   int extra_indent_level = 1;
00755   Defs::const_iterator di;
00756   for (di = _defs.begin(); di != _defs.end(); ++di) {
00757     const IntervalDef &def = (*di);
00758     char time_str[max_digits + 1];
00759     sprintf(time_str, format_str, int_to_double_time(def._actual_begin_time));
00760     indent(out, indent_level) << time_str;
00761 
00762     write_event_desc(out, def, extra_indent_level);
00763   }
00764 }
00765 
00766 ////////////////////////////////////////////////////////////////////
00767 //     Function: CMetaInterval::timeline
00768 //       Access: Published
00769 //  Description: Outputs a list of all events in the order in which
00770 //               they occur.
00771 ////////////////////////////////////////////////////////////////////
00772 void CMetaInterval::
00773 timeline(ostream &out) const {
00774   recompute();
00775 
00776   // How many digits of precision should we output for time?
00777   int num_decimals = (int)ceil(log10(_precision));
00778   int total_digits = num_decimals + 4;
00779   static const int max_digits = 32;  // totally arbitrary
00780   nassertv(total_digits <= max_digits);
00781   char format_str[12];
00782   sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
00783 
00784   int extra_indent_level = 0;
00785   PlaybackEvents::const_iterator ei;
00786   for (ei = _events.begin(); ei != _events.end(); ++ei) {
00787     const PlaybackEvent *event = (*ei);
00788 
00789     char time_str[max_digits + 1];
00790     sprintf(time_str, format_str, int_to_double_time(event->_time));
00791     out << time_str;
00792 
00793     switch (event->_type) {
00794     case PET_begin:
00795       out << " begin   ";
00796       break;
00797     case PET_end:
00798       out << " end     ";
00799       break;
00800     case PET_instant:
00801       out << " instant ";
00802       break;
00803     }
00804 
00805     int n = event->_n;
00806     nassertv(n >= 0 && n < (int)_defs.size());
00807     const IntervalDef &def = _defs[n];
00808 
00809     write_event_desc(out, def, extra_indent_level);
00810   }
00811 }
00812 
00813 ////////////////////////////////////////////////////////////////////
00814 //     Function: CMetaInterval::do_recompute
00815 //       Access: Protected, Virtual
00816 //  Description: Recomputes all of the events (and the duration)
00817 //               according to the set of interval defs.
00818 ////////////////////////////////////////////////////////////////////
00819 void CMetaInterval::
00820 do_recompute() {
00821   _dirty = false;
00822   clear_events();
00823 
00824   int n = recompute_level(0, 0, _end_time);
00825 
00826   if (n != (int)_defs.size()) {
00827     interval_cat.warning()
00828       << "CMetaInterval pushes don't match pops.\n";
00829   }
00830 
00831   // We do a stable_sort() to guarantee ordering of events that have
00832   // the same start time.  These must be invoked in the order in which
00833   // they appear.
00834   stable_sort(_events.begin(), _events.end(), IndirectLess<PlaybackEvent>());
00835   _duration = int_to_double_time(_end_time);
00836 }
00837 
00838 ////////////////////////////////////////////////////////////////////
00839 //     Function: CMetaInterval::clear_events
00840 //       Access: Private
00841 //  Description: Removes all entries from the _events list.
00842 ////////////////////////////////////////////////////////////////////
00843 void CMetaInterval::
00844 clear_events() {
00845   PlaybackEvents::iterator ei;
00846   for (ei = _events.begin(); ei != _events.end(); ++ei) {
00847     PlaybackEvent *event = (*ei);
00848     delete event;
00849   }
00850   _events.clear();
00851   _active.clear();
00852 }
00853 
00854 ////////////////////////////////////////////////////////////////////
00855 //     Function: CMetaInterval::do_event_forward
00856 //       Access: Private
00857 //  Description: Process a single event in the interval, moving
00858 //               forwards in time.  If the event represents a new
00859 //               begin, adds it to the new_active list; if it is an
00860 //               end, finalizes it.
00861 //
00862 //               If is_initial is true, it is as if we are in
00863 //               initialize or finalize: instant events will be
00864 //               invoked only if they are marked open_ended.
00865 ////////////////////////////////////////////////////////////////////
00866 void CMetaInterval::
00867 do_event_forward(CMetaInterval::PlaybackEvent *event, 
00868                  CMetaInterval::ActiveEvents &new_active, bool is_initial) {
00869   switch (event->_type) {
00870   case PET_begin:
00871     nassertv(event->_begin_event == event);
00872     new_active.push_back(event);
00873     break;
00874     
00875   case PET_end:
00876     {
00877       // Erase the event from either the new active or the current
00878       // active lists.
00879       ActiveEvents::iterator ai;
00880       ai = find(new_active.begin(), new_active.end(), event->_begin_event);
00881       if (ai != new_active.end()) {
00882         new_active.erase(ai);
00883         // This interval was new this frame; we must invoke it as
00884         // an instant event.
00885         enqueue_event(event->_n, ET_instant, is_initial);
00886 
00887       } else {
00888         ai = find(_active.begin(), _active.end(), event->_begin_event);
00889         if (ai != _active.end()) {
00890           _active.erase(ai);
00891           enqueue_event(event->_n, ET_finalize, is_initial);
00892 
00893         } else {
00894           // Hmm, this event wasn't on either list.  Maybe there was a
00895           // start event on the list whose time was less than 0.
00896           interval_cat.error()
00897             << "Event " << event->_begin_event->_n << " not on active list.\n";
00898           nassertv(false);
00899         }
00900       }
00901     }
00902     break;
00903     
00904   case PET_instant:
00905     nassertv(event->_begin_event == event);
00906     enqueue_event(event->_n, ET_instant, is_initial);
00907     break;
00908   }
00909 }
00910 
00911 ////////////////////////////////////////////////////////////////////
00912 //     Function: CMetaInterval::finish_events_forward
00913 //       Access: Private
00914 //  Description: After walking through the event list and adding a
00915 //               bunch of new events to new_active, finished up by
00916 //               calling priv_step() on all of the events still in _active
00917 //               and priv_initialize() on all the events in new_active,
00918 //               then copying the events from new_active to active.
00919 ////////////////////////////////////////////////////////////////////
00920 void CMetaInterval::
00921 finish_events_forward(int now, CMetaInterval::ActiveEvents &new_active) {
00922   // Do whatever's still active.
00923   ActiveEvents::iterator ai;
00924   for (ai = _active.begin(); ai != _active.end(); ++ai) {
00925     PlaybackEvent *event = (*ai);
00926     enqueue_event(event->_n, ET_step, false, now - event->_time);
00927   }
00928   
00929   // Initialize whatever new intervals we came across.
00930   for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
00931     PlaybackEvent *event = (*ai);
00932     enqueue_event(event->_n, ET_initialize, false, now - event->_time);
00933     _active.push_back(event);
00934   }
00935 }
00936 
00937 ////////////////////////////////////////////////////////////////////
00938 //     Function: CMetaInterval::do_event_reverse
00939 //       Access: Private
00940 //  Description: Process a single event in the interval, moving
00941 //               backwards in time.  This undoes the indicated event.
00942 //               If the event represents a new begin, adds it to the
00943 //               new_active list; if it is an end, finalizes it.
00944 //
00945 //               If is_initial is true, it is as if we are in
00946 //               reverse_initialize or reverse_finalize: instant
00947 //               events will be invoked only if they are marked
00948 //               open_ended.
00949 ////////////////////////////////////////////////////////////////////
00950 void CMetaInterval::
00951 do_event_reverse(CMetaInterval::PlaybackEvent *event, 
00952                  CMetaInterval::ActiveEvents &new_active, bool is_initial) {
00953   // Undo the indicated event.
00954   switch (event->_type) {
00955   case PET_begin:
00956     {
00957       nassertv(event->_begin_event == event);
00958       // Erase the event from either the new active or the current
00959       // active lists.
00960       ActiveEvents::iterator ai;
00961       ai = find(new_active.begin(), new_active.end(), event);
00962       if (ai != new_active.end()) {
00963         new_active.erase(ai);
00964         // This interval was new this frame; we invoke it as an
00965         // instant event.
00966         enqueue_event(event->_n, ET_reverse_instant, is_initial);
00967 
00968       } else {
00969         ai = find(_active.begin(), _active.end(), event);
00970         if (ai != _active.end()) {
00971           _active.erase(ai);
00972           enqueue_event(event->_n, ET_reverse_finalize, is_initial);
00973 
00974         } else {
00975           // Hmm, this event wasn't on either list.  Maybe there was a
00976           // stop event on the list whose time was greater than the
00977           // total, somehow. 
00978           interval_cat.error()
00979             << "Event " << event->_n << " not on active list.\n";
00980           nassertv(false);
00981         }
00982       }
00983     }
00984     break;
00985     
00986   case PET_end:
00987     new_active.push_front(event->_begin_event);
00988     break;
00989     
00990   case PET_instant:
00991     nassertv(event->_begin_event == event);
00992     enqueue_event(event->_n, ET_reverse_instant, is_initial);
00993     break;
00994   }
00995 }
00996 
00997 ////////////////////////////////////////////////////////////////////
00998 //     Function: CMetaInterval::finish_events_reverse
00999 //       Access: Private
01000 //  Description: After walking through the event list and adding a
01001 //               bunch of new events to new_active, finishes up by
01002 //               calling priv_step() on all of the events still in _active
01003 //               and priv_reverse_initialize() on all the events in
01004 //               new_active, then copying the events from new_active
01005 //               to active.
01006 ////////////////////////////////////////////////////////////////////
01007 void CMetaInterval::
01008 finish_events_reverse(int now, CMetaInterval::ActiveEvents &new_active) {
01009   // Do whatever's still active.
01010   ActiveEvents::iterator ai;
01011   for (ai = _active.begin(); ai != _active.end(); ++ai) {
01012     PlaybackEvent *event = (*ai);
01013     enqueue_event(event->_n, ET_step, false, now - event->_time);
01014   }
01015   
01016   // Initialize whatever new intervals we came across.
01017   for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
01018     PlaybackEvent *event = (*ai);
01019     enqueue_event(event->_n, ET_reverse_initialize, false, now - event->_time);
01020     _active.push_front(event);
01021   }
01022 }
01023   
01024 ////////////////////////////////////////////////////////////////////
01025 //     Function: CMetaInterval::enqueue_event
01026 //       Access: Private
01027 //  Description: Enqueues the indicated interval for invocation after
01028 //               we have finished scanning for events that need
01029 //               processing this frame.
01030 //
01031 //               is_initial is only relevant for event types
01032 //               ET_instant or ET_reverse_instant, and indicates
01033 //               whether we are in the priv_initialize() (or
01034 //               priv_reverse_initialize()) call, and should therefore only
01035 //               invoke open-ended intervals.
01036 //
01037 //               time is only relevant for ET_initialize,
01038 //               ET_reverse_initialize, and ET_step.
01039 ////////////////////////////////////////////////////////////////////
01040 void CMetaInterval::
01041 enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time) {
01042   nassertv(n >= 0 && n < (int)_defs.size());
01043   const IntervalDef &def = _defs[n];
01044   switch (def._type) {
01045   case DT_c_interval:
01046     if (is_initial &&
01047         (event_type == ET_instant || event_type == ET_reverse_instant) &&
01048         !def._c_interval->get_open_ended()) {
01049       // Ignore a non-open-ended interval that we skipped completely
01050       // past on priv_initialize().
01051       return;
01052     } else {
01053       if (_event_queue.empty()) {
01054         // if the event queue is empty, we can process this C++
01055         // interval immediately.  We only need to defer it if there
01056         // are external (e.g. Python) intervals in the queue that need
01057         // to be processed first.
01058         def._c_interval->priv_do_event(int_to_double_time(time), event_type);
01059         return;
01060       }
01061     }
01062     break;
01063 
01064   case DT_ext_index:
01065     if (is_initial &&
01066         (event_type == ET_instant || event_type == ET_reverse_instant) &&
01067         !def._ext_open_ended) {
01068       // Ignore a non-open-ended interval that we skipped completely
01069       // past on priv_initialize().
01070       return;
01071     }
01072     break;
01073 
01074   default:
01075     nassertv(false);
01076     return;
01077   }
01078 
01079   _event_queue.push_back(EventQueueEntry(n, event_type, time));
01080 }
01081 
01082 ////////////////////////////////////////////////////////////////////
01083 //     Function: CMetaInterval::enqueue_self_event
01084 //       Access: Private
01085 //  Description: Enqueues a reference to *this* interval.  This is
01086 //               called only when the interval is recursively
01087 //               re-entered; the request will be serviced when the
01088 //               current request is done processing.
01089 //
01090 //               time is only relevant for ET_initialize,
01091 //               ET_reverse_initialize, and ET_step.
01092 ////////////////////////////////////////////////////////////////////
01093 void CMetaInterval::
01094 enqueue_self_event(CInterval::EventType event_type, double t) {
01095   interval_cat.info()
01096     << "Recursive reentry detected into " << *this << "\n";
01097   int time = double_to_int_time(t);
01098   _event_queue.push_back(EventQueueEntry(-1, event_type, time));
01099 }
01100 
01101 ////////////////////////////////////////////////////////////////////
01102 //     Function: CMetaInterval::enqueue_done_event
01103 //       Access: Private
01104 //  Description: Enqueues a special "event" that simply marks the end
01105 //               of processing of the interval; the interval's done
01106 //               event should be thrown now, if it is defined.
01107 ////////////////////////////////////////////////////////////////////
01108 void CMetaInterval::
01109 enqueue_done_event() {
01110   _event_queue.push_back(EventQueueEntry(-2, ET_finalize, 0));
01111 }
01112   
01113 ////////////////////////////////////////////////////////////////////
01114 //     Function: CMetaInterval::service_event_queue
01115 //       Access: Private
01116 //  Description: Invokes whatever C++ intervals might be at the head
01117 //               of the queue, and prepares for passing an external
01118 //               interval to the scripting language.
01119 //
01120 //               The return value is true if there remains at least
01121 //               one external event to be serviced, false if all
01122 //               events are handled.
01123 ////////////////////////////////////////////////////////////////////
01124 bool CMetaInterval::
01125 service_event_queue() {
01126   while (!_event_queue.empty()) {
01127     nassertr(!_processing_events, true);
01128     const EventQueueEntry &entry = _event_queue.front();
01129     if (entry._n == -1) {
01130       // Index -1 is a special code for *this* interval.
01131       priv_do_event(int_to_double_time(entry._time), entry._event_type);
01132 
01133     } else if (entry._n == -2) {
01134       // Index -2 is a special code to indicate the interval is now
01135       // done, and its done event should be thrown.
01136       interval_done();
01137 
01138     } else {
01139       nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false);
01140       const IntervalDef &def = _defs[entry._n];
01141       switch (def._type) {
01142       case DT_c_interval:
01143         // Handle the C++ event.
01144         def._c_interval->priv_do_event(int_to_double_time(entry._time), entry._event_type);
01145         break;
01146         
01147       case DT_ext_index:
01148         // Here's an external event; leave it there and return.
01149         return true;
01150         
01151       default:
01152         nassertr(false, false);
01153         return false;
01154       }
01155     }
01156     _event_queue.pop_front();
01157   }
01158 
01159   // No more events on the queue.
01160   nassertr(!_processing_events, false);
01161   return false;
01162 }
01163 
01164 ////////////////////////////////////////////////////////////////////
01165 //     Function: CMetaInterval::recompute_level
01166 //       Access: Private
01167 //  Description: Recursively recomputes a complete level (delimited by
01168 //               push/pop definitions).
01169 //
01170 //               The value n on entry refers to the first entry after
01171 //               the push; the return value will reference the
01172 //               matching pop, or an index greater than the last
01173 //               element in the array if there was no matching pop.
01174 //
01175 //               The level_begin value indicates the begin time of
01176 //               this level.  On return, level_end is filled with the
01177 //               end time of this level.
01178 ////////////////////////////////////////////////////////////////////
01179 int CMetaInterval::
01180 recompute_level(int n, int level_begin, int &level_end) {
01181   level_end = level_begin;
01182   int previous_begin = level_begin;
01183   int previous_end = level_begin;
01184 
01185   while (n < (int)_defs.size() && _defs[n]._type != DT_pop_level) {
01186     IntervalDef &def = _defs[n];
01187     int begin_time = previous_begin;
01188     int end_time = previous_end;
01189     switch (def._type) {
01190     case DT_c_interval:
01191       begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
01192       def._actual_begin_time = begin_time;
01193       end_time = begin_time + double_to_int_time(def._c_interval->get_duration());
01194 
01195       if (def._c_interval->is_exact_type(WaitInterval::get_class_type())) {
01196         // Don't bother enqueuing events for WaitIntervals; they're
01197         // just there to fill up time.
01198 
01199       } else {
01200         if (begin_time == end_time) {
01201           _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
01202         } else {
01203           PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
01204           PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
01205           end->_begin_event = begin;
01206           _events.push_back(begin);
01207           _events.push_back(end);
01208         }
01209       }
01210       break;
01211 
01212     case DT_ext_index:
01213       begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
01214       def._actual_begin_time = begin_time;
01215       end_time = begin_time + double_to_int_time(def._ext_duration);
01216       if (begin_time == end_time) {
01217         _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
01218       } else {
01219         PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
01220         PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
01221         end->_begin_event = begin;
01222         _events.push_back(begin);
01223         _events.push_back(end);
01224       }
01225       break;
01226 
01227     case DT_push_level:
01228       begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
01229       def._actual_begin_time = begin_time;
01230       n = recompute_level(n + 1, begin_time, end_time);
01231       break;
01232 
01233     case DT_pop_level:
01234       nassertr(false, _defs.size());
01235       break;
01236     }
01237 
01238     previous_begin = begin_time;
01239     previous_end = end_time;
01240     level_end = max(level_end, end_time);
01241     n++;
01242   }
01243 
01244   if (n < (int)_defs.size()) {
01245     IntervalDef &def = _defs[n];
01246     // If we have a pop record, check it for a phony duration.
01247     if (def._ext_duration >= 0.0) {
01248       level_end = level_begin + double_to_int_time(def._ext_duration);
01249     }
01250 
01251     // The final pop "begins" at the level end time, just for clarity
01252     // on output.
01253     def._actual_begin_time = level_end;
01254   }
01255 
01256   return n;
01257 }
01258 
01259 ////////////////////////////////////////////////////////////////////
01260 //     Function: CMetaInterval::get_begin_time
01261 //       Access: Private
01262 //  Description: Returns the integer begin time indicated by the given
01263 //               IntervalDef, given the indicated level begin,
01264 //               previous begin, and previous end times.
01265 ////////////////////////////////////////////////////////////////////
01266 int CMetaInterval::
01267 get_begin_time(const CMetaInterval::IntervalDef &def, int level_begin,
01268                int previous_begin, int previous_end) {
01269   switch (def._rel_to) {
01270   case RS_previous_end:
01271     return previous_end + double_to_int_time(def._rel_time);
01272 
01273   case RS_previous_begin:
01274     return previous_begin + double_to_int_time(def._rel_time);
01275 
01276   case RS_level_begin:
01277     return level_begin + double_to_int_time(def._rel_time);
01278   }
01279 
01280   nassertr(false, previous_end);
01281   return previous_end;
01282 }
01283 
01284 ////////////////////////////////////////////////////////////////////
01285 //     Function: CMetaInterval::write_event_desc
01286 //       Access: Private
01287 //  Description: Formats an event for output, for write() or
01288 //               timeline().
01289 ////////////////////////////////////////////////////////////////////
01290 void CMetaInterval::
01291 write_event_desc(ostream &out, const CMetaInterval::IntervalDef &def, 
01292                  int &extra_indent_level) const {
01293   switch (def._type) {
01294   case DT_c_interval:
01295     indent(out, extra_indent_level)
01296       << *def._c_interval;
01297     if (!def._c_interval->get_open_ended()) {
01298       out << " (!oe)";
01299     }
01300     out << "\n";
01301     break;
01302     
01303   case DT_ext_index:
01304     indent(out, extra_indent_level)
01305       << "*" << def._ext_name;
01306     if (def._ext_duration != 0.0) {
01307       out << " dur " << def._ext_duration;
01308     }
01309     if (!def._ext_open_ended) {
01310       out << " (!oe)";
01311     }
01312     out<< "\n";
01313     break;
01314     
01315   case DT_push_level:
01316     indent(out, extra_indent_level)
01317       << def._ext_name << " {\n";
01318     extra_indent_level += 2;
01319     break;
01320     
01321   case DT_pop_level:
01322     extra_indent_level -= 2;
01323     indent(out, extra_indent_level)
01324       << "}\n";
01325     break;
01326   }
01327 }
 All Classes Functions Variables Enumerations