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  */
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  */
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  */
124 add_c_interval(CInterval *c_interval,
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  */
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  */
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  */
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  */
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  */
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  */
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  */
371 priv_instant() {
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  */
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  */
472 priv_finalize() {
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  */
512 priv_reverse_initialize(double t) {
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  */
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  */
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  */
634 priv_interrupt() {
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  */
660 pop_event() {
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  */
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The base class for timeline components.
Definition: cInterval.h:36
get_name
Returns the interval's name.
Definition: cInterval.h:124
get_duration
Returns the duration of the interval in seconds.
Definition: cInterval.h:127
void mark_dirty()
Called by a derived class to indicate the interval has been changed internally and must be recomputed...
Definition: cInterval.cxx:654
void priv_do_event(double t, EventType event)
Calls the appropriate event function indicated by the EventType.
Definition: cInterval.cxx:283
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 ...
virtual void priv_reverse_instant()
This is called in lieu of priv_reverse_initialize() .
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.
virtual void priv_reverse_initialize(double t)
Similar to priv_initialize(), but this is called when the interval is being played backwards; it indi...
virtual void priv_interrupt()
This is called while the interval is playing to indicate that it is about to be interrupted; that is,...
void pop_event()
Acknowledges that the external interval on the top of the queue has been extracted,...
virtual void priv_step(double t)
Advances the time on the interval.
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.
virtual void priv_initialize(double t)
This replaces the first call to priv_step(), and indicates that the interval has just begun.
int pop_level(double duration=-1.0)
Finishes a level marked by a previous call to push_level(), and returns to the previous level.
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...
virtual void priv_reverse_finalize()
Called generally following a priv_reverse_initialize(), this indicates the interval should set itself...
int push_level(const std::string &name, double rel_time, RelativeStart rel_to)
Marks the beginning of a nested level of child intervals.
void timeline(std::ostream &out) const
Outputs a list of all events in the order in which they occur.
virtual void priv_instant()
This is called in lieu of priv_initialize() .
virtual void priv_finalize()
This is called when an interval is interrupted.
void clear_intervals()
Resets the list of intervals and prepares for receiving a new list.
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.
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
Definition: indirectLess.h:25
A lightweight class that represents a single element that may be timed and/or counted via stats.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.