Panda3D
cInterval.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 cInterval.cxx
10 * @author drose
11 * @date 2002-08-27
12 */
13
14#include "cInterval.h"
15#include "cIntervalManager.h"
16#include "indent.h"
17#include "clockObject.h"
18#include "event.h"
19#include "eventQueue.h"
20#include "pStatTimer.h"
21
22using std::ostream;
23using std::string;
24
25PStatCollector CInterval::_root_pcollector("App:Show code:ivalLoop");
26TypeHandle CInterval::_type_handle;
27
28static inline string
29get_pstats_name(const string &name) {
30 string pname = name;
31 size_t hyphen = pname.find('-');
32 if (hyphen != string::npos) {
33 pname = pname.substr(0, hyphen);
34 }
35 return pname;
36}
37
38/**
39 *
40 */
41CInterval::
42CInterval(const string &name, double duration, bool open_ended) :
43 _state(S_initial),
44 _curr_t(0.0),
45 _name(name),
46 _pname(get_pstats_name(name)),
47 _duration(std::max(duration, 0.0)),
48 _open_ended(open_ended),
49 _dirty(false),
50 _ival_pcollector(_root_pcollector, _pname)
51{
52 _auto_pause = false;
53 _auto_finish = false;
54 _wants_t_callback = false;
55 _last_t_callback = -1.0;
57 _clock_start = 0.0;
58 _start_t = 0.0;
59 _end_t = _duration;
60 _start_t_at_start = true;
61 _end_t_at_end = true;
62 _play_rate = 1.0;
63 _do_loop = false;
64 _loop_count = 0;
65
66 if (interval_cat.is_spam()) {
67 interval_cat.spam()
68 << "Constructing interval " << (void *)this << ", duration = "
69 << _duration << "\n";
70 }
71}
72
73/**
74 *
75 */
76CInterval::
77~CInterval() {
78 if (interval_cat.is_spam()) {
79 interval_cat.spam()
80 << "Destructing interval " << (void *)this << "\n";
81 }
82}
83
84/**
85 * Explicitly sets the time within the interval. Normally, you would use
86 * start() .. finish() to let the time play normally, but this may be used to
87 * set the time to some particular value.
88 */
89void CInterval::
90set_t(double t) {
91 // There doesn't seem to be any reason to clamp this, and it breaks looping
92 // intervals. The interval code should properly handle t values outside the
93 // proper range. t = min(max(t, 0.0), get_duration());
94
95 switch (get_state()) {
96 case S_initial:
98 if (is_playing()) {
100 } else {
102 }
103 break;
104
105 case S_started:
106 // Support modifying t while the interval is playing. We assume
107 // is_playing() will be true in this state.
108 nassertv(is_playing());
110 priv_step(t);
111 setup_resume();
112 break;
113
114 case S_paused:
115 // Support modifying t while the interval is paused. In this case, we
116 // simply step to the new value of t; but this will change the state to
117 // S_started, so we must then change it back to S_paused by hand (because
118 // we're still paused).
119 priv_step(t);
121 break;
122
123 case S_final:
125 if (is_playing()) {
126 setup_resume();
127 } else {
129 }
130 break;
131 }
132}
133
134/**
135 * Starts the interval playing by registering it with the current
136 * CIntervalManager. The interval will play to the end and stop.
137 *
138 * If end_t is less than zero, it indicates the end of the interval.
139 */
141start(double start_t, double end_t, double play_rate) {
142 setup_play(start_t, end_t, play_rate, false);
143 _manager->add_c_interval(this, false);
144}
145
146/**
147 * Starts the interval playing by registering it with the current
148 * CIntervalManager. The interval will play until it is interrupted with
149 * finish() or pause(), looping back to start_t when it reaches end_t.
150 *
151 * If end_t is less than zero, it indicates the end of the interval.
152 */
154loop(double start_t, double end_t, double play_rate) {
155 setup_play(start_t, end_t, play_rate, true);
156 _manager->add_c_interval(this, false);
157}
158
159/**
160 * Stops the interval from playing but leaves it in its current state. It may
161 * later be resumed from this point by calling resume().
162 */
164pause() {
165 if (get_state() == S_started) {
167 }
168 int index = _manager->find_c_interval(this->get_name());
169 if (index >= 0) {
170 _manager->remove_c_interval(index);
171 }
172 return get_t();
173}
174
175/**
176 * Restarts the interval from its current point after a previous call to
177 * pause().
178 */
180resume() {
181 setup_resume();
182 _manager->add_c_interval(this, false);
183}
184
185/**
186 * Restarts the interval from the indicated point after a previous call to
187 * pause().
188 */
190resume(double start_t) {
191 set_t(start_t);
192 setup_resume();
193 _manager->add_c_interval(this, false);
194}
195
196/**
197 * Restarts the interval from the current point after a previous call to
198 * pause() (or a previous play-to-point-and-stop), to play until the indicated
199 * point and then stop.
200 */
202resume_until(double end_t) {
203 setup_resume_until(end_t);
204 _manager->add_c_interval(this, false);
205}
206
207/**
208 * Stops the interval from playing and sets it to its final state.
209 */
211finish() {
212 switch (get_state()) {
213 case S_initial:
214 priv_instant();
215 break;
216
217 case S_final:
218 break;
219
220 default:
222 }
223
224 int index = _manager->find_c_interval(this->get_name());
225 if (index >= 0) {
226 _manager->remove_c_interval(index);
227 }
228}
229
230/**
231 * Pauses the interval, if it is playing, and resets its state to its initial
232 * state, abandoning any state changes already in progress in the middle of
233 * the interval. Calling this is like pausing the interval and discarding it,
234 * creating a new one in its place.
235 */
238 pause();
239
240 _state = S_initial;
241 _curr_t = 0.0;
242}
243
244/**
245 * Returns true if the interval is currently playing, false otherwise.
246 */
247bool CInterval::
248is_playing() const {
249 int index = _manager->find_c_interval(this->get_name());
250 return (index >= 0);
251}
252
253/**
254 * Returns the play rate as set by the last call to start(), loop(), or
255 * set_play_rate().
256 */
257double CInterval::
258get_play_rate() const {
259 return _play_rate;
260}
261
262/**
263 * Changes the play rate of the interval. If the interval is already started,
264 * this changes its speed on-the-fly. Note that since play_rate is a
265 * parameter to start() and loop(), the next call to start() or loop() will
266 * reset this parameter.
267 */
268void CInterval::
269set_play_rate(double play_rate) {
270 if (is_playing()) {
271 pause();
272 _play_rate = play_rate;
273 resume();
274 } else {
275 _play_rate = play_rate;
276 }
277}
278
279/**
280 * Calls the appropriate event function indicated by the EventType.
281 */
283priv_do_event(double t, EventType event) {
284 PStatTimer timer(_ival_pcollector);
285 switch (event) {
286 case ET_initialize:
288 return;
289
290 case ET_instant:
291 priv_instant();
292 return;
293
294 case ET_step:
295 priv_step(t);
296 return;
297
298 case ET_finalize:
300 return;
301
302 case ET_reverse_initialize:
304 return;
305
306 case ET_reverse_instant:
308 return;
309
310 case ET_reverse_finalize:
312 return;
313
314 case ET_interrupt:
316 return;
317 }
318
319 interval_cat.warning()
320 << "Invalid event type: " << (int)event << "\n";
321}
322
323/**
324 * This replaces the first call to priv_step(), and indicates that the
325 * interval has just begun. This may be overridden by derived classes that
326 * need to do some explicit initialization on the first call.
327 */
329priv_initialize(double t) {
330 check_stopped(get_class_type(), "priv_initialize");
331 recompute();
332 _state = S_started;
333 priv_step(t);
334}
335
336/**
337 * This is called in lieu of priv_initialize() .. priv_step() ..
338 * priv_finalize(), when everything is to happen within one frame. The
339 * interval should initialize itself, then leave itself in the final state.
340 */
342priv_instant() {
343 check_stopped(get_class_type(), "priv_instant");
344 recompute();
345 _state = S_started;
347 _state = S_final;
348 interval_done();
349}
350
351/**
352 * Advances the time on the interval. The time may either increase (the
353 * normal case) or decrease (e.g. if the interval is being played by a
354 * slider).
355 */
357priv_step(double t) {
358 check_started(get_class_type(), "priv_step");
359 _state = S_started;
360 _curr_t = t;
361}
362
363/**
364 * This is called to stop an interval, forcing it to whatever state it would
365 * be after it played all the way through. It's generally invoked by
366 * set_final_t().
367 */
370 check_started(get_class_type(), "priv_finalize");
371 double duration = get_duration();
372 priv_step(duration);
373 _state = S_final;
374 interval_done();
375}
376
377/**
378 * Similar to priv_initialize(), but this is called when the interval is being
379 * played backwards; it indicates that the interval should start at the
380 * finishing state and undo any intervening intervals.
381 */
383priv_reverse_initialize(double t) {
384 check_stopped(get_class_type(), "priv_reverse_initialize");
385 recompute();
386 _state = S_started;
387 priv_step(t);
388}
389
390/**
391 * This is called in lieu of priv_reverse_initialize() .. priv_step() ..
392 * priv_reverse_finalize(), when everything is to happen within one frame.
393 * The interval should initialize itself, then leave itself in the initial
394 * state.
395 */
398 check_stopped(get_class_type(), "priv_reverse_instant");
399 recompute();
400 _state = S_started;
401 priv_step(0.0);
402 _state = S_initial;
403}
404
405/**
406 * Called generally following a priv_reverse_initialize(), this indicates the
407 * interval should set itself to the initial state.
408 */
411 check_started(get_class_type(), "priv_reverse_finalize");
412 priv_step(0.0);
413 _state = S_initial;
414}
415
416/**
417 * This is called while the interval is playing to indicate that it is about
418 * to be interrupted; that is, priv_step() will not be called for a length of
419 * time. But the interval should remain in its current state in anticipation
420 * of being eventually restarted when the calls to priv_step() eventually
421 * resume.
422 *
423 * The purpose of this function is to allow self-running intervals like sound
424 * intervals to stop the actual sound playback during the pause.
425 */
428 check_started(get_class_type(), "priv_interrupt");
429 _state = S_paused;
430}
431
432/**
433 *
434 */
435void CInterval::
436output(ostream &out) const {
437 out << get_name();
438 if (get_duration() != 0.0) {
439 out << " dur " << get_duration();
440 }
441}
442
443/**
444 *
445 */
446void CInterval::
447write(ostream &out, int indent_level) const {
448 indent(out, indent_level) << *this << "\n";
449}
450
451/**
452 * Called to prepare the interval for automatic timed playback, e.g. via a
453 * Python task. The interval will be played from start_t to end_t, at a time
454 * factor specified by play_rate. start_t must always be less than end_t
455 * (except for the exception for end_t == -1, below), but if play_rate is
456 * negative the interval will be played backwards.
457 *
458 * Specify end_t of -1 to play the entire interval from start_t.
459 *
460 * Call step_play() repeatedly to execute the interval.
461 */
463setup_play(double start_t, double end_t, double play_rate, bool do_loop) {
464 nassertv(start_t < end_t || end_t < 0.0);
465 nassertv(play_rate != 0.0);
466 PStatTimer timer(_ival_pcollector);
467
468 double duration = get_duration();
469
470 if (start_t <= 0.0) {
471 _start_t = 0.0;
472 _start_t_at_start = true;
473 } else if (start_t > duration) {
474 _start_t = duration;
475 _start_t_at_start = false;
476 } else {
477 _start_t = start_t;
478 _start_t_at_start = false;
479 }
480 if (end_t < 0.0 || end_t >= duration) {
481 _end_t = duration;
482 _end_t_at_end = true;
483 } else {
484 _end_t = end_t;
485 _end_t_at_end = false;
486 }
487
489 _play_rate = play_rate;
490 _do_loop = do_loop;
491 _loop_count = 0;
492}
493
494/**
495 * Called to prepare the interval for restarting at the current point within
496 * the interval after an interruption.
497 */
499setup_resume() {
501 if (_play_rate > 0.0) {
502 _clock_start = now - ((get_t() - _start_t) / _play_rate);
503
504 } else if (_play_rate < 0.0) {
505 _clock_start = now - ((get_t() - _end_t) / _play_rate);
506 }
507 _loop_count = 0;
508}
509
510/**
511 * Called to prepare the interval for restarting from the current point after
512 * a previous call to pause() (or a previous play-to-point-and-stop), to play
513 * until the indicated point and then stop.
514 */
516setup_resume_until(double end_t) {
517 double duration = get_duration();
518
519 if (end_t < 0.0 || end_t >= duration) {
520 _end_t = duration;
521 _end_t_at_end = true;
522 } else {
523 _end_t = end_t;
524 _end_t_at_end = false;
525 }
526
527 setup_resume();
528}
529
530/**
531 * Should be called once per frame to execute the automatic timed playback
532 * begun with setup_play().
533 *
534 * Returns true if the interval should continue, false if it is done and
535 * should stop.
536 */
538step_play() {
539 PStatTimer timer(_ival_pcollector);
541
542 if (_play_rate >= 0.0) {
543 double t = (now - _clock_start) * _play_rate + _start_t;
544
545 if (_end_t_at_end) {
546 _end_t = get_duration();
547 }
548
549 if (t < _end_t) {
550 // In the middle of the interval, not a problem.
551 if (is_stopped()) {
553 } else {
554 priv_step(t);
555 }
556
557 } else {
558 // Past the ending point; time to finalize.
559 if (_end_t_at_end) {
560 // Only finalize if the playback cycle includes the whole interval.
561 if (is_stopped()) {
562 if (get_open_ended() || _loop_count != 0) {
563 priv_instant();
564 }
565 } else {
567 }
568 } else {
569 if (is_stopped()) {
570 priv_initialize(_end_t);
571 } else {
572 priv_step(_end_t);
573 }
574 }
575
576 // Advance the clock for the next loop cycle. We might have to advance
577 // multiple times if we skipped several cycles in the past frame.
578
579 if (_end_t == _start_t) {
580 // If the interval has no length, we loop exactly once each time.
581 _loop_count++;
582
583 } else {
584 // Otherwise, figure out how many loops we need to skip.
585 double time_per_loop = (_end_t - _start_t) / _play_rate;
586 double num_loops = floor((now - _clock_start) / time_per_loop);
587 _loop_count += (int)num_loops;
588 _clock_start += num_loops * time_per_loop;
589 }
590 }
591
592 } else {
593 // Playing backwards.
594 double t = (now - _clock_start) * _play_rate + _end_t;
595
596 if (t >= _start_t) {
597 // In the middle of the interval, not a problem.
598 if (is_stopped()) {
600 } else {
601 priv_step(t);
602 }
603
604 } else {
605 // Past the ending point; time to finalize.
606 if (_start_t_at_start) {
607 // Only finalize if the playback cycle includes the whole interval.
608 if (is_stopped()) {
609 if (get_open_ended() || _loop_count != 0) {
611 }
612 } else {
614 }
615 } else {
616 if (is_stopped()) {
617 priv_reverse_initialize(_start_t);
618 } else {
619 priv_step(_start_t);
620 }
621 }
622
623 // Advance the clock for the next loop cycle. We might have to advance
624 // multiple times if we skipped several cycles in the past frame.
625
626 if (_end_t == _start_t) {
627 // If the interval has no length, we loop exactly once each time.
628 _loop_count++;
629
630 } else {
631 // Otherwise, figure out how many loops we need to skip.
632 double time_per_loop = (_end_t - _start_t) / -_play_rate;
633 double num_loops = floor((now - _clock_start) / time_per_loop);
634 _loop_count += (int)num_loops;
635 _clock_start += num_loops * time_per_loop;
636 }
637 }
638 }
639
640 bool should_continue = (_loop_count == 0 || _do_loop);
641
642 if (!should_continue && _state == S_started) {
644 }
645
646 return should_continue;
647}
648
649/**
650 * Called by a derived class to indicate the interval has been changed
651 * internally and must be recomputed before its duration may be returned.
652 */
654mark_dirty() {
655 if (!_dirty) {
656 _dirty = true;
657 Parents::iterator pi;
658 for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
659 (*pi)->mark_dirty();
660 }
661 }
662}
663
664/**
665 * Called internally whenever the interval reaches its final state.
666 */
667void CInterval::
668interval_done() {
669 if (!_done_event.empty()) {
670 _manager->get_event_queue()->queue_event(new Event(_done_event));
671 }
672}
673
674/**
675 * Does whatever processing is necessary to recompute the interval after a
676 * call to mark_dirty() has indicated a recomputation is necessary.
677 */
678void CInterval::
679do_recompute() {
680 _dirty = false;
681}
682
683ostream &
684operator << (ostream &out, CInterval::State state) {
685 switch (state) {
686 case CInterval::S_initial:
687 return out << "initial";
688
689 case CInterval::S_started:
690 return out << "started";
691
692 case CInterval::S_paused:
693 return out << "paused";
694
695 case CInterval::S_final:
696 return out << "final";
697 }
698
699 return out << "**invalid state(" << (int)state << ")**";
700}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int find_c_interval(const std::string &name) const
Returns the index associated with the named interval, if there is such an interval,...
void remove_c_interval(int index)
Removes the indicated interval from the queue immediately.
static CIntervalManager * get_global_ptr()
Returns the pointer to the one global CIntervalManager object.
EventQueue * get_event_queue() const
Returns the custom event queue to be used for throwing done events from intervals as they finish.
int add_c_interval(CInterval *interval, bool external)
Adds the interval to the manager, and returns a unique index for the interval.
is_playing
Returns true if the interval is currently playing, false otherwise.
Definition: cInterval.h:137
set_play_rate
Changes the play rate of the interval.
Definition: cInterval.h:136
virtual void priv_reverse_finalize()
Called generally following a priv_reverse_initialize(), this indicates the interval should set itself...
Definition: cInterval.cxx:410
void resume()
Restarts the interval from its current point after a previous call to pause().
Definition: cInterval.cxx:180
bool step_play()
Should be called once per frame to execute the automatic timed playback begun with setup_play().
Definition: cInterval.cxx:538
virtual void priv_step(double t)
Advances the time on the interval.
Definition: cInterval.cxx:357
void setup_play(double start_time, double end_time, double play_rate, bool do_loop)
Called to prepare the interval for automatic timed playback, e.g.
Definition: cInterval.cxx:463
void loop(double start_t=0.0, double end_t=-1.0, double play_rate=1.0)
Starts the interval playing by registering it with the current CIntervalManager.
Definition: cInterval.cxx:154
get_name
Returns the interval's name.
Definition: cInterval.h:124
set_t
Explicitly sets the time within the interval.
Definition: cInterval.h:132
get_duration
Returns the duration of the interval in seconds.
Definition: cInterval.h:127
is_stopped
Returns true if the interval is in either its initial or final states (but not in a running or paused...
Definition: cInterval.h:130
get_open_ended
Returns the state of the "open_ended" flag.
Definition: cInterval.h:128
get_state
Indicates the state the interval believes it is in: whether it has been started, is currently in the ...
Definition: cInterval.h:129
virtual void priv_initialize(double t)
This replaces the first call to priv_step(), and indicates that the interval has just begun.
Definition: cInterval.cxx:329
void mark_dirty()
Called by a derived class to indicate the interval has been changed internally and must be recomputed...
Definition: cInterval.cxx:654
virtual void priv_reverse_initialize(double t)
Similar to priv_initialize(), but this is called when the interval is being played backwards; it indi...
Definition: cInterval.cxx:383
get_play_rate
Returns the play rate as set by the last call to start(), loop(), or set_play_rate().
Definition: cInterval.h:136
void resume_until(double end_t)
Restarts the interval from the current point after a previous call to pause() (or a previous play-to-...
Definition: cInterval.cxx:202
void priv_do_event(double t, EventType event)
Calls the appropriate event function indicated by the EventType.
Definition: cInterval.cxx:283
virtual void priv_finalize()
This is called to stop an interval, forcing it to whatever state it would be after it played all the ...
Definition: cInterval.cxx:369
get_t
Returns the current time of the interval: the last value of t passed to priv_initialize(),...
Definition: cInterval.h:132
void clear_to_initial()
Pauses the interval, if it is playing, and resets its state to its initial state, abandoning any stat...
Definition: cInterval.cxx:237
virtual void priv_reverse_instant()
This is called in lieu of priv_reverse_initialize() .
Definition: cInterval.cxx:397
virtual void priv_interrupt()
This is called while the interval is playing to indicate that it is about to be interrupted; that is,...
Definition: cInterval.cxx:427
void setup_resume()
Called to prepare the interval for restarting at the current point within the interval after an inter...
Definition: cInterval.cxx:499
virtual void priv_instant()
This is called in lieu of priv_initialize() .
Definition: cInterval.cxx:342
void setup_resume_until(double end_t)
Called to prepare the interval for restarting from the current point after a previous call to pause()...
Definition: cInterval.cxx:516
void start(double start_t=0.0, double end_t=-1.0, double play_rate=1.0)
Starts the interval playing by registering it with the current CIntervalManager.
Definition: cInterval.cxx:141
void finish()
Stops the interval from playing and sets it to its final state.
Definition: cInterval.cxx:211
double pause()
Stops the interval from playing but leaves it in its current state.
Definition: cInterval.cxx:164
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
A named event, possibly with parameters.
Definition: event.h:33
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
STL namespace.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.