Panda3D
Loading...
Searching...
No Matches
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
24using std::string;
25
26TypeHandle CMetaInterval::_type_handle;
27
28/**
29 *
30 */
31CMetaInterval::
32CMetaInterval(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 */
44CMetaInterval::
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 */
101push_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 */
124add_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 */
159add_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 */
187pop_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 */
211set_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 */
249get_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 */
283get_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 */
320priv_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 */
371priv_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 */
410priv_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 */
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 */
512priv_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 */
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 */
660pop_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 */
673void CMetaInterval::
674write(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 */
703timeline(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 */
747void CMetaInterval::
748do_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 */
768void CMetaInterval::
769clear_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 */
787void CMetaInterval::
788do_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 */
838void CMetaInterval::
839finish_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 */
864void CMetaInterval::
865do_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 */
915void CMetaInterval::
916finish_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 */
944void CMetaInterval::
945enqueue_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 */
993void CMetaInterval::
994enqueue_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 */
1005void CMetaInterval::
1006enqueue_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 */
1017bool CMetaInterval::
1018service_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 */
1068int CMetaInterval::
1069recompute_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 */
1152int CMetaInterval::
1153get_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 */
1173void CMetaInterval::
1174write_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:126
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...
void priv_do_event(double t, EventType event)
Calls the appropriate event function indicated by the EventType.
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...
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.