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