1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*!
34 \class SoTimerSensor SoTimerSensor.h Inventor/sensors/SoTimerSensor.h
35 \brief The SoTimerSensor class is a sensor which will trigger at given intervals.
36
37 \ingroup sensors
38
39 Use sensors of this class when you want a job repeated at a certain
40 interval, without explicitly needing to reschedule the sensor
41 (i.e. SoTimerSensor automatically re-schedules itself after it has
42 been triggered).
43
44 SoTimerSensor instances is commonly used to trigger animation
45 updates at a constant framerate.
46 */
47
48 #include <Inventor/sensors/SoTimerSensor.h>
49 #include <Inventor/SoDB.h>
50 #include <cassert>
51
52 #if COIN_DEBUG
53 #include <Inventor/errors/SoDebugError.h>
54 #endif // COIN_DEBUG
55
56 #define DEBUG_TIMERSENSOR_TRACE 0
57
58 /*!
59 Default constructor. Sets up an interval of 1/30th of a second.
60 */
SoTimerSensor(void)61 SoTimerSensor::SoTimerSensor(void)
62 {
63 this->interval.setValue(1.0f/30.0f);
64 this->setbasetime = FALSE;
65 this->istriggering = FALSE;
66 }
67
68 /*!
69 Constructor taking as parameters the sensor callback function and
70 the userdata which will be passed the callback.
71
72 \sa setFunction(), setData()
73 */
SoTimerSensor(SoSensorCB * func,void * data)74 SoTimerSensor::SoTimerSensor(SoSensorCB * func, void * data)
75 : inherited(func, data)
76 {
77 this->interval.setValue(1.0f/30.0f);
78 this->setbasetime = FALSE;
79 this->istriggering = FALSE;
80 }
81
82 /*!
83 Destructor.
84 */
~SoTimerSensor(void)85 SoTimerSensor::~SoTimerSensor(void)
86 {
87 if (this->isScheduled()) this->unschedule();
88 }
89
90 /*!
91 Set the \a base trigger time.
92
93 If you use this method, the trigger times will be on intervals from
94 the given value.
95
96 Without an explicitly set base time, the next trigger invocation
97 after a trigger has happened will be on the current time plus the
98 interval time. Note that this will of course cause the timer to
99 drift.
100
101 \sa getBaseTime()
102 */
103 void
setBaseTime(const SbTime & baseref)104 SoTimerSensor::setBaseTime(const SbTime & baseref)
105 {
106 //FIXME: There is some humbug when setting baseTime to 0. Probably a
107 //floating point precision thing. Investigate. wiesener 20100810
108 this->base = baseref;
109 this->setbasetime = TRUE;
110 }
111
112 /*!
113 Returns the base trigger time.
114
115 \sa setBaseTime()
116 */
117 const SbTime &
getBaseTime(void) const118 SoTimerSensor::getBaseTime(void) const
119 {
120 return this->base;
121 }
122
123 /*!
124 Sets the time interval between each time the sensor triggers.
125
126 \sa getInterval()
127 */
128 void
setInterval(const SbTime & intervalref)129 SoTimerSensor::setInterval(const SbTime & intervalref)
130 {
131 this->interval = intervalref;
132 }
133
134 /*!
135 Returns the timer trigger interval.
136
137 \sa setInterval()
138 */
139 const SbTime &
getInterval(void) const140 SoTimerSensor::getInterval(void) const
141 {
142 return this->interval;
143 }
144
145 /*!
146 Set new trigger time based on the given schedule time,
147 and schedules the sensor for triggering.
148 */
149 void
reschedule(const SbTime & schedtime)150 SoTimerSensor::reschedule(const SbTime & schedtime)
151 {
152 this->scheduled = FALSE;
153 this->istriggering = FALSE;
154
155 if (!this->setbasetime) {
156 this->base = schedtime;
157 this->setTriggerTime(this->base + this->interval);
158 #if DEBUG_TIMERSENSOR_TRACE // debug
159 SoDebugError::postInfo("SoTimerSensor::reschedule",
160 "(setbasetime) base: %lf, new trigger time: %lf",
161 this->base.getValue(), this->getTriggerTime().getValue());
162 #endif // debug
163 }
164 else {
165 int intervals = (int)((schedtime - this->base)/this->interval) + 1;
166
167 if ( intervals < 0 )
168 intervals = 0;
169
170 this->setTriggerTime(this->base + intervals * this->interval);
171
172 #if DEBUG_TIMERSENSOR_TRACE
173 SoDebugError::postInfo("SoTimerSensor::reschedule",
174 "base: %lf, new trigger time: %lf",
175 this->base.getValue(), this->getTriggerTime().getValue());
176 #endif
177 }
178
179 // don't call this node's schedule as it calls this method
180 inherited::schedule();
181 }
182
183 /*!
184 Overrides the virtual schedule() method to be able to set up the
185 base time, if this was not done by the user.
186
187 If no base time was set, base time will then equal the current time.
188
189 \sa unschedule(), isScheduled()
190 */
191 void
schedule(void)192 SoTimerSensor::schedule(void)
193 {
194 #if DEBUG_TIMERSENSOR_TRACE // debug
195 SoDebugError::postInfo("SoTimerSensor::schedule", "");
196 #endif // debug
197
198 #if COIN_DEBUG
199 if (this->isScheduled()) {
200 SoDebugError::postWarning("SoTimerSensor::schedule",
201 "was already scheduled!");
202 return;
203 }
204 #endif // COIN_DEBUG
205
206 // need to handle the case where the callback has unscheduled
207 // the timer, and then scheduled it again. Since we are
208 // triggering, we can't just reschedule, but need to add
209 // it to SoSensorManager's list of rescheduled timers.
210 if (this->istriggering) {
211 // shouldn't get here if we're scheduled, but test anyway
212 if (!this->scheduled) {
213 SoDB::getSensorManager()->rescheduleTimer(this);
214 }
215 }
216 else {
217 this->reschedule(SbTime::getTimeOfDay());
218 }
219 }
220
221 /*!
222 Overrides the virtual unschedule() method to handle unschedule()
223 calls during triggering.
224 */
225 void
unschedule(void)226 SoTimerSensor::unschedule(void)
227 {
228 #if DEBUG_TIMERSENSOR_TRACE // debug
229 SoDebugError::postInfo("SoTimerSensor::unschedule", "%p start", this);
230 #endif // debug
231
232 #if COIN_DEBUG
233 if (!this->isScheduled()) {
234 SoDebugError::postWarning("SoTimerSensor::unschedule",
235 "%p not scheduled (istriggering=%s)",
236 this, this->istriggering ? "TRUE" : "FALSE");
237 return;
238 }
239 #endif // COIN_DEBUG
240
241 if (this->istriggering) {
242 SoDB::getSensorManager()->removeRescheduledTimer(this);
243 this->scheduled = FALSE;
244 this->istriggering = FALSE;
245 }
246 else inherited::unschedule();
247
248 #if DEBUG_TIMERSENSOR_TRACE // debug
249 SoDebugError::postInfo("SoTimerSensor::unschedule", "%p done", this);
250 #endif // debug
251 }
252
253 /*!
254 Overides the virtual trigger() method to be able to reschedule
255 ourselves after we've been triggered.
256 */
257 void
trigger(void)258 SoTimerSensor::trigger(void)
259 {
260 #if DEBUG_TIMERSENSOR_TRACE // debug
261 SoDebugError::postInfo("SoTimerSensor::trigger", "%p start", this);
262 #endif // debug
263
264 this->istriggering = TRUE;
265 // This will cause SoSceneManager to reschedule this timer after
266 // the current queue has been processed.
267 SoDB::getSensorManager()->rescheduleTimer(this);
268
269 // don't call SoTimerQueueSensor::trigger() as it will clear
270 // the scheduled flag.
271 SoSensor::trigger();
272
273 #if DEBUG_TIMERSENSOR_TRACE // debug
274 SoDebugError::postInfo("SoTimerSensor::trigger", "%p done", this);
275 #endif // debug
276 }
277