Panda3D
cMetaInterval.cxx
1 // Filename: cMetaInterval.cxx
2 // Created by: drose (27Aug02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "cMetaInterval.h"
16 #include "waitInterval.h"
17 #include "config_interval.h"
18 #include "indirectLess.h"
19 #include "indent.h"
20 
21 #include <algorithm>
22 #include <math.h> // for log10()
23 #include <stdio.h> // for sprintf()
24 
25 TypeHandle CMetaInterval::_type_handle;
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: CMetaInterval::Constructor
29 // Access: Published
30 // Description:
31 ////////////////////////////////////////////////////////////////////
32 CMetaInterval::
33 CMetaInterval(const string &name) :
34  CInterval(name, 0.0, true)
35 {
36  _precision = interval_precision;
37  _current_nesting_level = 0;
38  _next_event_index = 0;
39  _processing_events = false;
40 }
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: CMetaInterval::Destructor
44 // Access: Published, Virtual
45 // Description:
46 ////////////////////////////////////////////////////////////////////
47 CMetaInterval::
48 ~CMetaInterval() {
50 }
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: CMetaInterval::clear_intervals
54 // Access: Published
55 // Description: Resets the list of intervals and prepares for
56 // receiving a new list.
57 ////////////////////////////////////////////////////////////////////
58 void CMetaInterval::
60  // Better not do this unless you have serviced all of the
61  // outstanding events!
62  bool lost_events = false;
63  if (!_event_queue.empty()) {
64  interval_cat.warning()
65  << "Losing outstanding events for " << *this << "\n";
66  _event_queue.clear();
67  lost_events = true;
68  }
69 
70  clear_events();
71 
72  // Go through all of our nested intervals and remove ourselves as
73  // their parent.
74  Defs::iterator di;
75  for (di = _defs.begin(); di != _defs.end(); ++di) {
76  IntervalDef &def = (*di);
77  if (def._c_interval != (CInterval *)NULL) {
78  CInterval::Parents::iterator pi =
79  find(def._c_interval->_parents.begin(),
80  def._c_interval->_parents.end(),
81  this);
82  nassertv(pi != def._c_interval->_parents.end());
83  def._c_interval->_parents.erase(pi);
84  }
85  }
86  _defs.clear();
87 
88  _current_nesting_level = 0;
89  _next_event_index = 0;
90 
91 #ifndef NDEBUG
92  if (verify_intervals) {
93  nassertv(!lost_events);
94  }
95 #endif
96 }
97 
98 ////////////////////////////////////////////////////////////////////
99 // Function: CMetaInterval::push_level
100 // Access: Published
101 // Description: Marks the beginning of a nested level of child
102 // intervals. Within the nested level, a RelativeStart
103 // time of RS_level_begin refers to the start of the
104 // level, and the first interval added within the level
105 // is always relative to the start of the level.
106 //
107 // The return value is the index of the def entry
108 // created by this push.
109 ////////////////////////////////////////////////////////////////////
110 int CMetaInterval::
111 push_level(const string &name, double rel_time, RelativeStart rel_to) {
112  nassertr(_event_queue.empty() && !_processing_events, -1);
113 
114  _defs.push_back(IntervalDef());
115  IntervalDef &def = _defs.back();
116  def._type = DT_push_level;
117  def._ext_name = name;
118  def._rel_time = rel_time;
119  def._rel_to = rel_to;
120  _current_nesting_level++;
121  mark_dirty();
122 
123  return (int)_defs.size() - 1;
124 }
125 
126 ////////////////////////////////////////////////////////////////////
127 // Function: CMetaInterval::add_c_interval
128 // Access: Published
129 // Description: Adds a new CInterval to the list. The interval will
130 // be played when the indicated time (relative to the
131 // given point) has been reached.
132 //
133 // The return value is the index of the def entry
134 // representing the new interval.
135 ////////////////////////////////////////////////////////////////////
136 int CMetaInterval::
138  double rel_time, RelativeStart rel_to) {
139  nassertr(_event_queue.empty() && !_processing_events, -1);
140  nassertr(c_interval != (CInterval *)NULL, -1);
141 
142  c_interval->_parents.push_back(this);
143  c_interval->_ival_pcollector = PStatCollector(_ival_pcollector, c_interval->_pname);
144  _defs.push_back(IntervalDef());
145  IntervalDef &def = _defs.back();
146  def._type = DT_c_interval;
147  def._c_interval = c_interval;
148  def._rel_time = rel_time;
149  def._rel_to = rel_to;
150  mark_dirty();
151 
152  return (int)_defs.size() - 1;
153 }
154 
155 ////////////////////////////////////////////////////////////////////
156 // Function: CMetaInterval::add_ext_index
157 // Access: Published
158 // Description: Adds a new external interval to the list. This
159 // represents some object in the external scripting
160 // language that has properties similar to a CInterval
161 // (for instance, a Python Interval object).
162 //
163 // The CMetaInterval object cannot play this external
164 // interval directly, but it records a placeholder for
165 // it and will ask the scripting language to play it
166 // when it is time, via is_event_ready() and related
167 // methods.
168 //
169 // The ext_index number itself is simply a handle that
170 // the scripting language makes up and associates with
171 // its interval object somehow. The CMetaInterval
172 // object does not attempt to interpret this value.
173 //
174 // The return value is the index of the def entry
175 // representing the new interval.
176 ////////////////////////////////////////////////////////////////////
177 int CMetaInterval::
178 add_ext_index(int ext_index, const string &name, double duration,
179  bool open_ended,
180  double rel_time, RelativeStart rel_to) {
181  nassertr(_event_queue.empty() && !_processing_events, -1);
182 
183  _defs.push_back(IntervalDef());
184  IntervalDef &def = _defs.back();
185  def._type = DT_ext_index;
186  def._ext_index = ext_index;
187  def._ext_name = name;
188  def._ext_duration = duration;
189  def._ext_open_ended = open_ended;
190  def._rel_time = rel_time;
191  def._rel_to = rel_to;
192  mark_dirty();
193 
194  return (int)_defs.size() - 1;
195 }
196 
197 ////////////////////////////////////////////////////////////////////
198 // Function: CMetaInterval::pop_level
199 // Access: Published
200 // Description: Finishes a level marked by a previous call to
201 // push_level(), and returns to the previous level.
202 //
203 // If the duration is not negative, it represents a
204 // phony duration to assign to the level, for the
205 // purposes of sequencing later intervals. Otherwise,
206 // the level's duration is computed based on the
207 // intervals within the level.
208 ////////////////////////////////////////////////////////////////////
209 int CMetaInterval::
210 pop_level(double duration) {
211  nassertr(_event_queue.empty() && !_processing_events, -1);
212  nassertr(_current_nesting_level > 0, -1);
213 
214  _defs.push_back(IntervalDef());
215  IntervalDef &def = _defs.back();
216  def._type = DT_pop_level;
217  def._ext_duration = duration;
218  _current_nesting_level--;
219  mark_dirty();
220 
221  return (int)_defs.size() - 1;
222 }
223 
224 ////////////////////////////////////////////////////////////////////
225 // Function: CMetaInterval::set_interval_start_time
226 // Access: Published
227 // Description: Adjusts the start time of the child interval with the
228 // given name, if found. This may be either a C++
229 // interval added via add_c_interval(), or an external
230 // interval added via add_ext_index(); the name must
231 // match exactly.
232 //
233 // If the interval is found, its start time is adjusted,
234 // and all subsequent intervals are adjusting
235 // accordingly, and true is returned. If a matching
236 // interval is not found, nothing is changed and false
237 // is returned.
238 ////////////////////////////////////////////////////////////////////
239 bool CMetaInterval::
240 set_interval_start_time(const string &name, double rel_time,
241  CMetaInterval::RelativeStart rel_to) {
242  nassertr(_event_queue.empty() && !_processing_events, false);
243  Defs::iterator di;
244  for (di = _defs.begin(); di != _defs.end(); ++di) {
245  IntervalDef &def = (*di);
246 
247  bool match = false;
248  switch (def._type) {
249  case DT_c_interval:
250  match = (def._c_interval->get_name() == name);
251  break;
252 
253  case DT_ext_index:
254  match = (def._ext_name == name);
255  break;
256 
257  default:
258  break;
259  }
260  if (match) {
261  // Here's the interval.
262  def._rel_time = rel_time;
263  def._rel_to = rel_to;
264  mark_dirty();
265  return true;
266  }
267  }
268 
269  return false;
270 }
271 
272 ////////////////////////////////////////////////////////////////////
273 // Function: CMetaInterval::get_interval_start_time
274 // Access: Published
275 // Description: Returns the actual start time, relative to the
276 // beginning of the interval, of the child interval with
277 // the given name, if found, or -1 if the interval is
278 // not found.
279 ////////////////////////////////////////////////////////////////////
280 double CMetaInterval::
281 get_interval_start_time(const string &name) const {
282  recompute();
283  Defs::const_iterator di;
284  for (di = _defs.begin(); di != _defs.end(); ++di) {
285  const IntervalDef &def = (*di);
286 
287  bool match = false;
288  switch (def._type) {
289  case DT_c_interval:
290  match = (def._c_interval->get_name() == name);
291  break;
292 
293  case DT_ext_index:
294  match = (def._ext_name == name);
295  break;
296 
297  default:
298  break;
299  }
300  if (match) {
301  // Here's the interval.
302  return int_to_double_time(def._actual_begin_time);
303  }
304  }
305 
306  return -1.0;
307 }
308 
309 ////////////////////////////////////////////////////////////////////
310 // Function: CMetaInterval::get_interval_end_time
311 // Access: Published
312 // Description: Returns the actual end time, relative to the
313 // beginning of the interval, of the child interval with
314 // the given name, if found, or -1 if the interval is
315 // not found.
316 ////////////////////////////////////////////////////////////////////
317 double CMetaInterval::
318 get_interval_end_time(const string &name) const {
319  recompute();
320  Defs::const_iterator di;
321  for (di = _defs.begin(); di != _defs.end(); ++di) {
322  const IntervalDef &def = (*di);
323 
324  bool match = false;
325  double duration = 0.0;
326  switch (def._type) {
327  case DT_c_interval:
328  duration = def._c_interval->get_duration();
329  match = (def._c_interval->get_name() == name);
330  break;
331 
332  case DT_ext_index:
333  duration = def._ext_duration;
334  match = (def._ext_name == name);
335  break;
336 
337  default:
338  break;
339  }
340  if (match) {
341  // Here's the interval.
342  return int_to_double_time(def._actual_begin_time) + duration;
343  }
344  }
345 
346  return -1.0;
347 }
348 
349 ////////////////////////////////////////////////////////////////////
350 // Function: CMetaInterval::initialize
351 // Access: Published, Virtual
352 // Description: This replaces the first call to priv_step(), and indicates
353 // that the interval has just begun. This may be
354 // overridden by derived classes that need to do some
355 // explicit initialization on the first call.
356 ////////////////////////////////////////////////////////////////////
357 void CMetaInterval::
358 priv_initialize(double t) {
359  if (_processing_events) {
360  enqueue_self_event(ET_initialize, t);
361  return;
362  }
363 
364  check_stopped(get_class_type(), "priv_initialize");
365  // It may be tempting to flush the event_queue here, but don't do
366  // it. Those are events that must still be serviced from some
367  // previous interval operation. Throwing them away would be a
368  // mistake.
369 
370  recompute();
371  _next_event_index = 0;
372  _active.clear();
373 
374  int now = double_to_int_time(t);
375 
376  /*
377  // One special case: if we step to t == 0.0, it really means to the
378  // very beginning of the interval, *before* any events that occurred
379  // at time 0. (Most of the time, stepping to a particular time
380  // means *after* any events that occurred at that time.)
381  if (t == 0.0) {
382  now = -1;
383  }
384  */
385 
386  // Now look for events from the beginning up to the current time.
387  _processing_events = true;
388  ActiveEvents new_active;
389  while (_next_event_index < _events.size() &&
390  _events[_next_event_index]->_time <= now) {
391  PlaybackEvent *event = _events[_next_event_index];
392  _next_event_index++;
393 
394  // Do the indicated event.
395  do_event_forward(event, new_active, true);
396  }
397  finish_events_forward(now, new_active);
398  _processing_events = false;
399 
400  _curr_t = t;
401  _state = S_started;
402 }
403 
404 ////////////////////////////////////////////////////////////////////
405 // Function: CMetaInterval::instant
406 // Access: Published, Virtual
407 // Description: This is called in lieu of priv_initialize() .. priv_step()
408 // .. priv_finalize(), when everything is to happen within
409 // one frame. The interval should initialize itself,
410 // then leave itself in the final state.
411 ////////////////////////////////////////////////////////////////////
412 void CMetaInterval::
414  if (_processing_events) {
415  enqueue_self_event(ET_instant);
416  return;
417  }
418 
419  check_stopped(get_class_type(), "priv_instant");
420  recompute();
421  _active.clear();
422 
423  // Apply all of the events. This just means we invoke "instant" for
424  // any end or instant event, ignoring the begin events.
425  _processing_events = true;
426  PlaybackEvents::iterator ei;
427  for (ei = _events.begin(); ei != _events.end(); ++ei) {
428  PlaybackEvent *event = (*ei);
429  if (event->_type != PET_begin) {
430  enqueue_event(event->_n, ET_instant, true, 0);
431  }
432  }
433  _processing_events = false;
434 
435  _next_event_index = _events.size();
436  _curr_t = get_duration();
437  _state = S_final;
438 
439  if (_event_queue.empty()) {
440  interval_done();
441  } else {
442  enqueue_done_event();
443  }
444 }
445 
446 ////////////////////////////////////////////////////////////////////
447 // Function: CMetaInterval::step
448 // Access: Published, Virtual
449 // Description: Advances the time on the interval. The time may
450 // either increase (the normal case) or decrease
451 // (e.g. if the interval is being played by a slider).
452 ////////////////////////////////////////////////////////////////////
453 void CMetaInterval::
454 priv_step(double t) {
455  if (_processing_events) {
456  enqueue_self_event(ET_step, t);
457  return;
458  }
459 
460  check_started(get_class_type(), "priv_step");
461  int now = double_to_int_time(t);
462 
463  /*
464  // One special case: if we step to t == 0.0, it really means to the
465  // very beginning of the interval, *before* any events that occurred
466  // at time 0. (Most of the time, stepping to a particular time
467  // means *after* any events that occurred at that time.)
468  if (t == 0.0) {
469  now = -1;
470  }
471  */
472 
473  // Now look for events between the last time we ran and the current
474  // time.
475 
476  _processing_events = true;
477  if (_next_event_index < _events.size() &&
478  _events[_next_event_index]->_time <= now) {
479  // The normal case: time is increasing.
480  ActiveEvents new_active;
481  while (_next_event_index < _events.size() &&
482  _events[_next_event_index]->_time <= now) {
483  PlaybackEvent *event = _events[_next_event_index];
484  _next_event_index++;
485 
486  // Do the indicated event.
487  do_event_forward(event, new_active, false);
488  }
489 
490  finish_events_forward(now, new_active);
491 
492  } else {
493  // A less usual case: time is decreasing.
494  ActiveEvents new_active;
495  while (_next_event_index > 0 &&
496  _events[_next_event_index - 1]->_time > now) {
497  _next_event_index--;
498  PlaybackEvent *event = _events[_next_event_index];
499 
500  do_event_reverse(event, new_active, false);
501  }
502 
503  finish_events_reverse(now, new_active);
504  }
505  _processing_events = false;
506 
507  _curr_t = t;
508  _state = S_started;
509 }
510 
511 ////////////////////////////////////////////////////////////////////
512 // Function: CMetaInterval::finalize
513 // Access: Published, Virtual
514 // Description: This is called when an interval is interrupted. It
515 // should advance the time as if priv_step() were called, and
516 // also perform whatever cleanup might be required.
517 ////////////////////////////////////////////////////////////////////
518 void CMetaInterval::
520  if (_processing_events) {
521  enqueue_self_event(ET_finalize);
522  return;
523  }
524 
525  double duration = get_duration();
526  if (_state == S_initial) {
527  priv_initialize(duration);
528  }
529 
530  // Do all remaining events.
531  _processing_events = true;
532  ActiveEvents new_active;
533  while (_next_event_index < _events.size()) {
534  PlaybackEvent *event = _events[_next_event_index];
535  _next_event_index++;
536 
537  // Do the indicated event.
538  do_event_forward(event, new_active, true);
539  }
540  finish_events_forward(double_to_int_time(duration), new_active);
541  _processing_events = false;
542 
543  _curr_t = duration;
544  _state = S_final;
545 
546  if (_event_queue.empty()) {
547  interval_done();
548  } else {
549  enqueue_done_event();
550  }
551 }
552 
553 ////////////////////////////////////////////////////////////////////
554 // Function: CMetaInterval::reverse_initialize
555 // Access: Published, Virtual
556 // Description: Similar to priv_initialize(), but this is called when the
557 // interval is being played backwards; it indicates that
558 // the interval should start at the finishing state and
559 // undo any intervening intervals.
560 ////////////////////////////////////////////////////////////////////
561 void CMetaInterval::
563  if (_processing_events) {
564  enqueue_self_event(ET_reverse_initialize, t);
565  return;
566  }
567 
568  check_stopped(get_class_type(), "priv_reverse_initialize");
569  // It may be tempting to flush the event_queue here, but don't do
570  // it. Those are events that must still be serviced from some
571  // previous interval operation. Throwing them away would be a
572  // mistake.
573 
574  recompute();
575  _next_event_index = _events.size();
576  _active.clear();
577 
578  int now = double_to_int_time(t);
579 
580  /*
581  // One special case: if we step to t == 0.0, it really means to the
582  // very beginning of the interval, *before* any events that occurred
583  // at time 0. (Most of the time, stepping to a particular time
584  // means *after* any events that occurred at that time.)
585  if (t == 0.0) {
586  now = -1;
587  }
588  */
589 
590  // Now look for events from the end down to the current time.
591  _processing_events = true;
592  ActiveEvents new_active;
593  while (_next_event_index > 0 &&
594  _events[_next_event_index - 1]->_time > now) {
595  _next_event_index--;
596  PlaybackEvent *event = _events[_next_event_index];
597 
598  // Do the indicated event.
599  do_event_reverse(event, new_active, true);
600  }
601  finish_events_reverse(now, new_active);
602  _processing_events = false;
603 
604  _curr_t = t;
605  _state = S_started;
606 }
607 
608 ////////////////////////////////////////////////////////////////////
609 // Function: CMetaInterval::reverse_instant
610 // Access: Published, Virtual
611 // Description: This is called in lieu of priv_reverse_initialize()
612 // .. priv_step() .. priv_reverse_finalize(), when everything is
613 // to happen within one frame. The interval should
614 // initialize itself, then leave itself in the initial
615 // state.
616 ////////////////////////////////////////////////////////////////////
617 void CMetaInterval::
619  if (_processing_events) {
620  enqueue_self_event(ET_reverse_instant);
621  return;
622  }
623 
624  check_stopped(get_class_type(), "priv_reverse_instant");
625  recompute();
626  _active.clear();
627 
628  // Apply all of the events. This just means we invoke "instant" for
629  // any end or instant event, ignoring the begin events.
630  _processing_events = true;
631  PlaybackEvents::reverse_iterator ei;
632  for (ei = _events.rbegin(); ei != _events.rend(); ++ei) {
633  PlaybackEvent *event = (*ei);
634  if (event->_type != PET_begin) {
635  enqueue_event(event->_n, ET_reverse_instant, true, 0);
636  }
637  }
638  _processing_events = false;
639 
640  _next_event_index = 0;
641  _curr_t = 0.0;
642  _state = S_initial;
643 }
644 
645 ////////////////////////////////////////////////////////////////////
646 // Function: CMetaInterval::reverse_finalize
647 // Access: Published, Virtual
648 // Description: Called generally following a priv_reverse_initialize(),
649 // this indicates the interval should set itself to the
650 // initial state.
651 ////////////////////////////////////////////////////////////////////
652 void CMetaInterval::
654  if (_processing_events) {
655  enqueue_self_event(ET_reverse_finalize);
656  return;
657  }
658 
659  if (_state == S_initial) {
660  priv_initialize(0.0);
661  }
662 
663  // Do all remaining events at the beginning.
664  _processing_events = true;
665  ActiveEvents new_active;
666 
667  while (_next_event_index > 0) {
668  _next_event_index--;
669  PlaybackEvent *event = _events[_next_event_index];
670 
671  do_event_reverse(event, new_active, true);
672  }
673  finish_events_reverse(0, new_active);
674  _processing_events = false;
675 
676  _curr_t = 0.0;
677  _state = S_initial;
678 }
679 
680 ////////////////////////////////////////////////////////////////////
681 // Function: CMetaInterval::interrupt
682 // Access: Published, Virtual
683 // Description: This is called while the interval is playing to
684 // indicate that it is about to be interrupted; that is,
685 // priv_step() will not be called for a length of time. But
686 // the interval should remain in its current state in
687 // anticipation of being eventually restarted when the
688 // calls to priv_step() eventually resume.
689 //
690 // The purpose of this function is to allow self-running
691 // intervals like sound intervals to stop the actual
692 // sound playback during the pause.
693 ////////////////////////////////////////////////////////////////////
694 void CMetaInterval::
696  if (_processing_events) {
697  enqueue_self_event(ET_interrupt);
698  return;
699  }
700 
701  _processing_events = true;
702  ActiveEvents::iterator ai;
703  for (ai = _active.begin(); ai != _active.end(); ++ai) {
704  PlaybackEvent *event = (*ai);
705  enqueue_event(event->_n, ET_interrupt, false);
706  }
707  _processing_events = false;
708 
709  if (_state == S_started) {
710  _state = S_paused;
711  }
712 }
713 
714 ////////////////////////////////////////////////////////////////////
715 // Function: CMetaInterval::pop_event
716 // Access: Published
717 // Description: Acknowledges that the external interval on the top of
718 // the queue has been extracted, and is about to be
719 // serviced by the scripting language. This prepares
720 // the interval so the next call to is_event_ready()
721 // will return information about the next external
722 // interval on the queue, if any.
723 ////////////////////////////////////////////////////////////////////
724 void CMetaInterval::
726 #ifndef NDEBUG
727  nassertv(!_event_queue.empty());
728  const EventQueueEntry &entry = _event_queue.front();
729  const IntervalDef &def = _defs[entry._n];
730  nassertv(def._type == DT_ext_index);
731 #endif
732  _event_queue.pop_front();
733 }
734 
735 ////////////////////////////////////////////////////////////////////
736 // Function: CMetaInterval::write
737 // Access: Published, Virtual
738 // Description:
739 ////////////////////////////////////////////////////////////////////
740 void CMetaInterval::
741 write(ostream &out, int indent_level) const {
742  recompute();
743 
744  // How many digits of precision should we output for time?
745  int num_decimals = (int)ceil(log10(_precision));
746  int total_digits = num_decimals + 4;
747  static const int max_digits = 32; // totally arbitrary
748  nassertv(total_digits <= max_digits);
749  char format_str[12];
750  sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
751 
752  indent(out, indent_level) << get_name() << ":\n";
753 
754  int extra_indent_level = 1;
755  Defs::const_iterator di;
756  for (di = _defs.begin(); di != _defs.end(); ++di) {
757  const IntervalDef &def = (*di);
758  char time_str[max_digits + 1];
759  sprintf(time_str, format_str, int_to_double_time(def._actual_begin_time));
760  indent(out, indent_level) << time_str;
761 
762  write_event_desc(out, def, extra_indent_level);
763  }
764 }
765 
766 ////////////////////////////////////////////////////////////////////
767 // Function: CMetaInterval::timeline
768 // Access: Published
769 // Description: Outputs a list of all events in the order in which
770 // they occur.
771 ////////////////////////////////////////////////////////////////////
772 void CMetaInterval::
773 timeline(ostream &out) const {
774  recompute();
775 
776  // How many digits of precision should we output for time?
777  int num_decimals = (int)ceil(log10(_precision));
778  int total_digits = num_decimals + 4;
779  static const int max_digits = 32; // totally arbitrary
780  nassertv(total_digits <= max_digits);
781  char format_str[12];
782  sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
783 
784  int extra_indent_level = 0;
785  PlaybackEvents::const_iterator ei;
786  for (ei = _events.begin(); ei != _events.end(); ++ei) {
787  const PlaybackEvent *event = (*ei);
788 
789  char time_str[max_digits + 1];
790  sprintf(time_str, format_str, int_to_double_time(event->_time));
791  out << time_str;
792 
793  switch (event->_type) {
794  case PET_begin:
795  out << " begin ";
796  break;
797  case PET_end:
798  out << " end ";
799  break;
800  case PET_instant:
801  out << " instant ";
802  break;
803  }
804 
805  int n = event->_n;
806  nassertv(n >= 0 && n < (int)_defs.size());
807  const IntervalDef &def = _defs[n];
808 
809  write_event_desc(out, def, extra_indent_level);
810  }
811 }
812 
813 ////////////////////////////////////////////////////////////////////
814 // Function: CMetaInterval::do_recompute
815 // Access: Protected, Virtual
816 // Description: Recomputes all of the events (and the duration)
817 // according to the set of interval defs.
818 ////////////////////////////////////////////////////////////////////
819 void CMetaInterval::
820 do_recompute() {
821  _dirty = false;
822  clear_events();
823 
824  int n = recompute_level(0, 0, _end_time);
825 
826  if (n != (int)_defs.size()) {
827  interval_cat.warning()
828  << "CMetaInterval pushes don't match pops.\n";
829  }
830 
831  // We do a stable_sort() to guarantee ordering of events that have
832  // the same start time. These must be invoked in the order in which
833  // they appear.
834  stable_sort(_events.begin(), _events.end(), IndirectLess<PlaybackEvent>());
835  _duration = int_to_double_time(_end_time);
836 }
837 
838 ////////////////////////////////////////////////////////////////////
839 // Function: CMetaInterval::clear_events
840 // Access: Private
841 // Description: Removes all entries from the _events list.
842 ////////////////////////////////////////////////////////////////////
843 void CMetaInterval::
844 clear_events() {
845  PlaybackEvents::iterator ei;
846  for (ei = _events.begin(); ei != _events.end(); ++ei) {
847  PlaybackEvent *event = (*ei);
848  delete event;
849  }
850  _events.clear();
851  _active.clear();
852 }
853 
854 ////////////////////////////////////////////////////////////////////
855 // Function: CMetaInterval::do_event_forward
856 // Access: Private
857 // Description: Process a single event in the interval, moving
858 // forwards in time. If the event represents a new
859 // begin, adds it to the new_active list; if it is an
860 // end, finalizes it.
861 //
862 // If is_initial is true, it is as if we are in
863 // initialize or finalize: instant events will be
864 // invoked only if they are marked open_ended.
865 ////////////////////////////////////////////////////////////////////
866 void CMetaInterval::
867 do_event_forward(CMetaInterval::PlaybackEvent *event,
868  CMetaInterval::ActiveEvents &new_active, bool is_initial) {
869  switch (event->_type) {
870  case PET_begin:
871  nassertv(event->_begin_event == event);
872  new_active.push_back(event);
873  break;
874 
875  case PET_end:
876  {
877  // Erase the event from either the new active or the current
878  // active lists.
879  ActiveEvents::iterator ai;
880  ai = find(new_active.begin(), new_active.end(), event->_begin_event);
881  if (ai != new_active.end()) {
882  new_active.erase(ai);
883  // This interval was new this frame; we must invoke it as
884  // an instant event.
885  enqueue_event(event->_n, ET_instant, is_initial);
886 
887  } else {
888  ai = find(_active.begin(), _active.end(), event->_begin_event);
889  if (ai != _active.end()) {
890  _active.erase(ai);
891  enqueue_event(event->_n, ET_finalize, is_initial);
892 
893  } else {
894  // Hmm, this event wasn't on either list. Maybe there was a
895  // start event on the list whose time was less than 0.
896  interval_cat.error()
897  << "Event " << event->_begin_event->_n << " not on active list.\n";
898  nassertv(false);
899  }
900  }
901  }
902  break;
903 
904  case PET_instant:
905  nassertv(event->_begin_event == event);
906  enqueue_event(event->_n, ET_instant, is_initial);
907  break;
908  }
909 }
910 
911 ////////////////////////////////////////////////////////////////////
912 // Function: CMetaInterval::finish_events_forward
913 // Access: Private
914 // Description: After walking through the event list and adding a
915 // bunch of new events to new_active, finished up by
916 // calling priv_step() on all of the events still in _active
917 // and priv_initialize() on all the events in new_active,
918 // then copying the events from new_active to active.
919 ////////////////////////////////////////////////////////////////////
920 void CMetaInterval::
921 finish_events_forward(int now, CMetaInterval::ActiveEvents &new_active) {
922  // Do whatever's still active.
923  ActiveEvents::iterator ai;
924  for (ai = _active.begin(); ai != _active.end(); ++ai) {
925  PlaybackEvent *event = (*ai);
926  enqueue_event(event->_n, ET_step, false, now - event->_time);
927  }
928 
929  // Initialize whatever new intervals we came across.
930  for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
931  PlaybackEvent *event = (*ai);
932  enqueue_event(event->_n, ET_initialize, false, now - event->_time);
933  _active.push_back(event);
934  }
935 }
936 
937 ////////////////////////////////////////////////////////////////////
938 // Function: CMetaInterval::do_event_reverse
939 // Access: Private
940 // Description: Process a single event in the interval, moving
941 // backwards in time. This undoes the indicated event.
942 // If the event represents a new begin, adds it to the
943 // new_active list; if it is an end, finalizes it.
944 //
945 // If is_initial is true, it is as if we are in
946 // reverse_initialize or reverse_finalize: instant
947 // events will be invoked only if they are marked
948 // open_ended.
949 ////////////////////////////////////////////////////////////////////
950 void CMetaInterval::
951 do_event_reverse(CMetaInterval::PlaybackEvent *event,
952  CMetaInterval::ActiveEvents &new_active, bool is_initial) {
953  // Undo the indicated event.
954  switch (event->_type) {
955  case PET_begin:
956  {
957  nassertv(event->_begin_event == event);
958  // Erase the event from either the new active or the current
959  // active lists.
960  ActiveEvents::iterator ai;
961  ai = find(new_active.begin(), new_active.end(), event);
962  if (ai != new_active.end()) {
963  new_active.erase(ai);
964  // This interval was new this frame; we invoke it as an
965  // instant event.
966  enqueue_event(event->_n, ET_reverse_instant, is_initial);
967 
968  } else {
969  ai = find(_active.begin(), _active.end(), event);
970  if (ai != _active.end()) {
971  _active.erase(ai);
972  enqueue_event(event->_n, ET_reverse_finalize, is_initial);
973 
974  } else {
975  // Hmm, this event wasn't on either list. Maybe there was a
976  // stop event on the list whose time was greater than the
977  // total, somehow.
978  interval_cat.error()
979  << "Event " << event->_n << " not on active list.\n";
980  nassertv(false);
981  }
982  }
983  }
984  break;
985 
986  case PET_end:
987  new_active.push_front(event->_begin_event);
988  break;
989 
990  case PET_instant:
991  nassertv(event->_begin_event == event);
992  enqueue_event(event->_n, ET_reverse_instant, is_initial);
993  break;
994  }
995 }
996 
997 ////////////////////////////////////////////////////////////////////
998 // Function: CMetaInterval::finish_events_reverse
999 // Access: Private
1000 // Description: After walking through the event list and adding a
1001 // bunch of new events to new_active, finishes up by
1002 // calling priv_step() on all of the events still in _active
1003 // and priv_reverse_initialize() on all the events in
1004 // new_active, then copying the events from new_active
1005 // to active.
1006 ////////////////////////////////////////////////////////////////////
1007 void CMetaInterval::
1008 finish_events_reverse(int now, CMetaInterval::ActiveEvents &new_active) {
1009  // Do whatever's still active.
1010  ActiveEvents::iterator ai;
1011  for (ai = _active.begin(); ai != _active.end(); ++ai) {
1012  PlaybackEvent *event = (*ai);
1013  enqueue_event(event->_n, ET_step, false, now - event->_time);
1014  }
1015 
1016  // Initialize whatever new intervals we came across.
1017  for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
1018  PlaybackEvent *event = (*ai);
1019  enqueue_event(event->_n, ET_reverse_initialize, false, now - event->_time);
1020  _active.push_front(event);
1021  }
1022 }
1023 
1024 ////////////////////////////////////////////////////////////////////
1025 // Function: CMetaInterval::enqueue_event
1026 // Access: Private
1027 // Description: Enqueues the indicated interval for invocation after
1028 // we have finished scanning for events that need
1029 // processing this frame.
1030 //
1031 // is_initial is only relevant for event types
1032 // ET_instant or ET_reverse_instant, and indicates
1033 // whether we are in the priv_initialize() (or
1034 // priv_reverse_initialize()) call, and should therefore only
1035 // invoke open-ended intervals.
1036 //
1037 // time is only relevant for ET_initialize,
1038 // ET_reverse_initialize, and ET_step.
1039 ////////////////////////////////////////////////////////////////////
1040 void CMetaInterval::
1041 enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time) {
1042  nassertv(n >= 0 && n < (int)_defs.size());
1043  const IntervalDef &def = _defs[n];
1044  switch (def._type) {
1045  case DT_c_interval:
1046  if (is_initial &&
1047  (event_type == ET_instant || event_type == ET_reverse_instant) &&
1048  !def._c_interval->get_open_ended()) {
1049  // Ignore a non-open-ended interval that we skipped completely
1050  // past on priv_initialize().
1051  return;
1052  } else {
1053  if (_event_queue.empty()) {
1054  // if the event queue is empty, we can process this C++
1055  // interval immediately. We only need to defer it if there
1056  // are external (e.g. Python) intervals in the queue that need
1057  // to be processed first.
1058  def._c_interval->priv_do_event(int_to_double_time(time), event_type);
1059  return;
1060  }
1061  }
1062  break;
1063 
1064  case DT_ext_index:
1065  if (is_initial &&
1066  (event_type == ET_instant || event_type == ET_reverse_instant) &&
1067  !def._ext_open_ended) {
1068  // Ignore a non-open-ended interval that we skipped completely
1069  // past on priv_initialize().
1070  return;
1071  }
1072  break;
1073 
1074  default:
1075  nassertv(false);
1076  return;
1077  }
1078 
1079  _event_queue.push_back(EventQueueEntry(n, event_type, time));
1080 }
1081 
1082 ////////////////////////////////////////////////////////////////////
1083 // Function: CMetaInterval::enqueue_self_event
1084 // Access: Private
1085 // Description: Enqueues a reference to *this* interval. This is
1086 // called only when the interval is recursively
1087 // re-entered; the request will be serviced when the
1088 // current request is done processing.
1089 //
1090 // time is only relevant for ET_initialize,
1091 // ET_reverse_initialize, and ET_step.
1092 ////////////////////////////////////////////////////////////////////
1093 void CMetaInterval::
1094 enqueue_self_event(CInterval::EventType event_type, double t) {
1095  interval_cat.info()
1096  << "Recursive reentry detected into " << *this << "\n";
1097  int time = double_to_int_time(t);
1098  _event_queue.push_back(EventQueueEntry(-1, event_type, time));
1099 }
1100 
1101 ////////////////////////////////////////////////////////////////////
1102 // Function: CMetaInterval::enqueue_done_event
1103 // Access: Private
1104 // Description: Enqueues a special "event" that simply marks the end
1105 // of processing of the interval; the interval's done
1106 // event should be thrown now, if it is defined.
1107 ////////////////////////////////////////////////////////////////////
1108 void CMetaInterval::
1109 enqueue_done_event() {
1110  _event_queue.push_back(EventQueueEntry(-2, ET_finalize, 0));
1111 }
1112 
1113 ////////////////////////////////////////////////////////////////////
1114 // Function: CMetaInterval::service_event_queue
1115 // Access: Private
1116 // Description: Invokes whatever C++ intervals might be at the head
1117 // of the queue, and prepares for passing an external
1118 // interval to the scripting language.
1119 //
1120 // The return value is true if there remains at least
1121 // one external event to be serviced, false if all
1122 // events are handled.
1123 ////////////////////////////////////////////////////////////////////
1124 bool CMetaInterval::
1125 service_event_queue() {
1126  while (!_event_queue.empty()) {
1127  nassertr(!_processing_events, true);
1128  const EventQueueEntry &entry = _event_queue.front();
1129  if (entry._n == -1) {
1130  // Index -1 is a special code for *this* interval.
1131  priv_do_event(int_to_double_time(entry._time), entry._event_type);
1132 
1133  } else if (entry._n == -2) {
1134  // Index -2 is a special code to indicate the interval is now
1135  // done, and its done event should be thrown.
1136  interval_done();
1137 
1138  } else {
1139  nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false);
1140  const IntervalDef &def = _defs[entry._n];
1141  switch (def._type) {
1142  case DT_c_interval:
1143  // Handle the C++ event.
1144  def._c_interval->priv_do_event(int_to_double_time(entry._time), entry._event_type);
1145  break;
1146 
1147  case DT_ext_index:
1148  // Here's an external event; leave it there and return.
1149  return true;
1150 
1151  default:
1152  nassertr(false, false);
1153  return false;
1154  }
1155  }
1156  _event_queue.pop_front();
1157  }
1158 
1159  // No more events on the queue.
1160  nassertr(!_processing_events, false);
1161  return false;
1162 }
1163 
1164 ////////////////////////////////////////////////////////////////////
1165 // Function: CMetaInterval::recompute_level
1166 // Access: Private
1167 // Description: Recursively recomputes a complete level (delimited by
1168 // push/pop definitions).
1169 //
1170 // The value n on entry refers to the first entry after
1171 // the push; the return value will reference the
1172 // matching pop, or an index greater than the last
1173 // element in the array if there was no matching pop.
1174 //
1175 // The level_begin value indicates the begin time of
1176 // this level. On return, level_end is filled with the
1177 // end time of this level.
1178 ////////////////////////////////////////////////////////////////////
1179 int CMetaInterval::
1180 recompute_level(int n, int level_begin, int &level_end) {
1181  level_end = level_begin;
1182  int previous_begin = level_begin;
1183  int previous_end = level_begin;
1184 
1185  while (n < (int)_defs.size() && _defs[n]._type != DT_pop_level) {
1186  IntervalDef &def = _defs[n];
1187  int begin_time = previous_begin;
1188  int end_time = previous_end;
1189  switch (def._type) {
1190  case DT_c_interval:
1191  begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
1192  def._actual_begin_time = begin_time;
1193  end_time = begin_time + double_to_int_time(def._c_interval->get_duration());
1194 
1195  if (def._c_interval->is_exact_type(WaitInterval::get_class_type())) {
1196  // Don't bother enqueuing events for WaitIntervals; they're
1197  // just there to fill up time.
1198 
1199  } else {
1200  if (begin_time == end_time) {
1201  _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
1202  } else {
1203  PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
1204  PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
1205  end->_begin_event = begin;
1206  _events.push_back(begin);
1207  _events.push_back(end);
1208  }
1209  }
1210  break;
1211 
1212  case DT_ext_index:
1213  begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
1214  def._actual_begin_time = begin_time;
1215  end_time = begin_time + double_to_int_time(def._ext_duration);
1216  if (begin_time == end_time) {
1217  _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
1218  } else {
1219  PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
1220  PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
1221  end->_begin_event = begin;
1222  _events.push_back(begin);
1223  _events.push_back(end);
1224  }
1225  break;
1226 
1227  case DT_push_level:
1228  begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
1229  def._actual_begin_time = begin_time;
1230  n = recompute_level(n + 1, begin_time, end_time);
1231  break;
1232 
1233  case DT_pop_level:
1234  nassertr(false, _defs.size());
1235  break;
1236  }
1237 
1238  previous_begin = begin_time;
1239  previous_end = end_time;
1240  level_end = max(level_end, end_time);
1241  n++;
1242  }
1243 
1244  if (n < (int)_defs.size()) {
1245  IntervalDef &def = _defs[n];
1246  // If we have a pop record, check it for a phony duration.
1247  if (def._ext_duration >= 0.0) {
1248  level_end = level_begin + double_to_int_time(def._ext_duration);
1249  }
1250 
1251  // The final pop "begins" at the level end time, just for clarity
1252  // on output.
1253  def._actual_begin_time = level_end;
1254  }
1255 
1256  return n;
1257 }
1258 
1259 ////////////////////////////////////////////////////////////////////
1260 // Function: CMetaInterval::get_begin_time
1261 // Access: Private
1262 // Description: Returns the integer begin time indicated by the given
1263 // IntervalDef, given the indicated level begin,
1264 // previous begin, and previous end times.
1265 ////////////////////////////////////////////////////////////////////
1266 int CMetaInterval::
1267 get_begin_time(const CMetaInterval::IntervalDef &def, int level_begin,
1268  int previous_begin, int previous_end) {
1269  switch (def._rel_to) {
1270  case RS_previous_end:
1271  return previous_end + double_to_int_time(def._rel_time);
1272 
1273  case RS_previous_begin:
1274  return previous_begin + double_to_int_time(def._rel_time);
1275 
1276  case RS_level_begin:
1277  return level_begin + double_to_int_time(def._rel_time);
1278  }
1279 
1280  nassertr(false, previous_end);
1281  return previous_end;
1282 }
1283 
1284 ////////////////////////////////////////////////////////////////////
1285 // Function: CMetaInterval::write_event_desc
1286 // Access: Private
1287 // Description: Formats an event for output, for write() or
1288 // timeline().
1289 ////////////////////////////////////////////////////////////////////
1290 void CMetaInterval::
1291 write_event_desc(ostream &out, const CMetaInterval::IntervalDef &def,
1292  int &extra_indent_level) const {
1293  switch (def._type) {
1294  case DT_c_interval:
1295  indent(out, extra_indent_level)
1296  << *def._c_interval;
1297  if (!def._c_interval->get_open_ended()) {
1298  out << " (!oe)";
1299  }
1300  out << "\n";
1301  break;
1302 
1303  case DT_ext_index:
1304  indent(out, extra_indent_level)
1305  << "*" << def._ext_name;
1306  if (def._ext_duration != 0.0) {
1307  out << " dur " << def._ext_duration;
1308  }
1309  if (!def._ext_open_ended) {
1310  out << " (!oe)";
1311  }
1312  out<< "\n";
1313  break;
1314 
1315  case DT_push_level:
1316  indent(out, extra_indent_level)
1317  << def._ext_name << " {\n";
1318  extra_indent_level += 2;
1319  break;
1320 
1321  case DT_pop_level:
1322  extra_indent_level -= 2;
1323  indent(out, extra_indent_level)
1324  << "}\n";
1325  break;
1326  }
1327 }
double get_interval_start_time(const string &name) const
Returns the actual start time, relative to the beginning of the interval, of the child interval with ...
virtual void priv_initialize(double t)
This replaces the first call to priv_step(), and indicates that the interval has just begun...
virtual void priv_finalize()
This is called when an interval is interrupted.
int push_level(const string &name, double rel_time, RelativeStart rel_to)
Marks the beginning of a nested level of child intervals.
The base class for timeline components.
Definition: cInterval.h:39
virtual void priv_instant()
This is called in lieu of priv_initialize() .
This is our own Panda specialization on the default STL list.
Definition: plist.h:38
virtual void priv_reverse_instant()
This is called in lieu of priv_reverse_initialize()
bool set_interval_start_time(const string &name, double rel_time, RelativeStart rel_to=RS_level_begin)
Adjusts the start time of the child interval with the given name, if found.
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
Definition: indirectLess.h:28
virtual void priv_reverse_finalize()
Called generally following a priv_reverse_initialize(), this indicates the interval should set itself...
int pop_level(double duration=-1.0)
Finishes a level marked by a previous call to push_level(), and returns to the previous level...
A lightweight class that represents a single element that may be timed and/or counted via stats...
const string & get_name() const
Returns the interval&#39;s name.
Definition: cInterval.I:22
void pop_event()
Acknowledges that the external interval on the top of the queue has been extracted, and is about to be serviced by the scripting language.
int add_ext_index(int ext_index, const string &name, double duration, bool open_ended, double rel_time, RelativeStart rel_to)
Adds a new external interval to the list.
void timeline(ostream &out) const
Outputs a list of all events in the order in which they occur.
int add_c_interval(CInterval *c_interval, double rel_time=0.0f, RelativeStart rel_to=RS_previous_end)
Adds a new CInterval to the list.
double get_interval_end_time(const string &name) const
Returns the actual end time, relative to the beginning of the interval, of the child interval with th...
double get_duration() const
Returns the duration of the interval in seconds.
Definition: cInterval.I:32
void clear_intervals()
Resets the list of intervals and prepares for receiving a new list.
void priv_do_event(double t, EventType event)
Calls the appropriate event function indicated by the EventType.
Definition: cInterval.cxx:325
void mark_dirty()
Called by a derived class to indicate the interval has been changed internally and must be recomputed...
Definition: cInterval.cxx:746
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
virtual void priv_step(double t)
Advances the time on the interval.
virtual void priv_interrupt()
This is called while the interval is playing to indicate that it is about to be interrupted; that is...
virtual void priv_reverse_initialize(double t)
Similar to priv_initialize(), but this is called when the interval is being played backwards; it indi...