Mercurial > MadButterfly
annotate src/animate.c @ 489:23c7667b3ec0 Android_Skia
Fix a potential bug when destroy a rdman.
When a rdman is dirty, free shapes and coords works specially.
Objects are append to a free list. They are not real freed until
rdman being clean. redraw_man_destroy() free shapes and coords with
free functions of them. If rdman is dirty when destroy it, objects
would be leaked. The changeset make rdman clean before free shapes
and coords to make objects being freed correctly.
author | Thinker K.F. Li <thinker@branda.to> |
---|---|
date | Sun, 22 Nov 2009 20:41:27 +0800 |
parents | f90c60967a9c |
children | 586e50f82c1f |
rev | line source |
---|---|
111 | 1 /*! \file |
2 * \brief Animation tools. | |
3 * | |
117 | 4 * \sa \ref ani |
111 | 5 */ |
6 /*! \page ani What is Animation? | |
41 | 7 * |
117 | 8 * Animation is a program to move, resize, rotate, ..., changing |
9 * graphics on the output screen. | |
10 * | |
11 * \image html program.png | |
12 * | |
41 | 13 * XXX: Program is a sequence of actions duration a perior. |
14 * Actions are grouped into words. A program defines | |
15 * the order and time of playing of words. A word | |
16 * defines how long to perform a set of actions. Actions | |
17 * in a word are performed concurrently. | |
45 | 18 * |
19 * Animation shapes are updated periodically. Every action | |
20 * are working with start, step, and stop 3 calls. start is | |
21 * called when start time the word, contain it, due. step is | |
22 * called periodically in duration of playing time start at | |
23 * 'start time'. When the clock run out the playing time of | |
24 * a word, it call stop of actions in the word. | |
111 | 25 * |
26 * A program is driven by a timer. Once a program is started, it registers | |
27 * with timer for periodic running. \ref mb_tman_t is timer of | |
28 * MadButterfly. The update frequence is 10fps by default, now. | |
29 * | |
117 | 30 * \section use_progm How to Use Animation Program? |
31 * Following code block creates a program with 2 words. First word is | |
32 * started immediately after the program been started. It is consisted | |
33 * for 1 second. Second word is started 1 second after the program been | |
34 * started. It is consisted for 2 seconds. There are 2 action in | |
35 * first word, they shift graphics managed by coord1 & coord2 by (50,50) and | |
36 * (-50,50) pixels, respectly. The shifting is performed incrementally | |
37 * in 1 second. Second word shifts coord1 and coord2, too. And, graphics | |
38 * managed by coord3 are hidden at end of the word. At end of code in the | |
39 * block, mb_progm_start() starts the program. 3rd argument of | |
40 * mb_progm_start() must be current wall time. | |
41 * | |
42 * \code | |
43 * progm = mb_progm_new(10, &rdman); | |
44 * | |
45 * MB_TIMEVAL_SET(&start, 0, 0); | |
46 * MB_TIMEVAL_SET(&playing, 1, 0); | |
47 * word = mb_progm_next_word(progm, &start, &playing); | |
48 * | |
49 * act = mb_shift_new(50, 50, coord1, word); | |
50 * act = mb_shift_new(-50, 50, coord2, word); | |
51 * | |
52 * MB_TIMEVAL_SET(&start, 1, 0); | |
53 * MB_TIMEVAL_SET(&playing, 2, 0); | |
54 * word = mb_progm_next_word(progm, &start, &playing); | |
55 * | |
56 * act = mb_shift_new(0, 20, coord1, word); | |
57 * act = mb_shift_new(0, -20, coord2, word); | |
58 * act = mb_visibility_new(VIS_HIDDEN, coord3, word); | |
59 * | |
60 * gettimeofday(&tv, NULL); | |
61 * MB_TIMEVAL_SET(&mbtv, tv.tv_sec, tv.tv_usec); | |
62 * mb_progm_start(progm, tman, &mbtv); | |
63 * \endcode | |
64 * | |
65 * | |
66 * \sa \ref animate.c | |
41 | 67 */ |
68 #include <stdio.h> | |
69 #include <stdlib.h> | |
70 #include <string.h> | |
186
530bb7728546
Move header files to $(top_srcdir)/include/ and prefixed with 'mb_'.
Thinker K.F. Li <thinker@branda.to>
parents:
185
diff
changeset
|
71 #include "mb_types.h" |
530bb7728546
Move header files to $(top_srcdir)/include/ and prefixed with 'mb_'.
Thinker K.F. Li <thinker@branda.to>
parents:
185
diff
changeset
|
72 #include "mb_redraw_man.h" |
530bb7728546
Move header files to $(top_srcdir)/include/ and prefixed with 'mb_'.
Thinker K.F. Li <thinker@branda.to>
parents:
185
diff
changeset
|
73 #include "mb_timer.h" |
530bb7728546
Move header files to $(top_srcdir)/include/ and prefixed with 'mb_'.
Thinker K.F. Li <thinker@branda.to>
parents:
185
diff
changeset
|
74 #include "mb_animate.h" |
41 | 75 |
76 | |
122
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
77 #define STEP_INTERVAL 90000 |
41 | 78 #define ASSERT(x) |
79 | |
80 /*! \brief A word is a set of concurrent actions in a program. | |
81 */ | |
82 struct _mb_word { | |
83 mb_timeval_t start_time; /*!< time to start the word */ | |
84 mb_timeval_t playing_time; /*!< time duration of playing */ | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
85 mb_timeval_t abs_start, abs_stop; |
41 | 86 STAILQ(mb_action_t) actions; |
87 }; | |
88 | |
89 /*! \brief A program describe a series of actions to animate shapes. | |
51 | 90 * |
91 * first_playing is an index to one of words. It always points to | |
92 * the first of words that is playing or waiting for playing. | |
41 | 93 */ |
94 struct _mb_progm { | |
95 redraw_man_t *rdman; | |
96 | |
46 | 97 mb_timeval_t start_time; |
41 | 98 int first_playing; /*!< first playing word. */ |
99 mb_tman_t *tman; | |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
100 subject_t *complete; /*!< notify when a program is completed. */ |
153
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
101 mb_timer_t *cur_timer; |
41 | 102 |
103 int n_words; | |
104 int max_words; | |
105 mb_word_t words[1]; | |
106 }; | |
107 | |
119 | 108 /*! \brief Create a program object. |
109 * | |
110 * \param max_words is maximum number of words the program can hold. | |
111 * \param rdman is a rdman that show graphics manipulated by it. | |
112 */ | |
41 | 113 mb_progm_t *mb_progm_new(int max_words, redraw_man_t *rdman) { |
114 mb_progm_t *progm; | |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
115 #ifndef UNITTEST |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
116 ob_factory_t *factory; |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
117 #endif /* UNITTEST */ |
41 | 118 int i; |
119 | |
120 progm = (mb_progm_t *)malloc(sizeof(mb_progm_t) + | |
121 (sizeof(mb_word_t) * (max_words - 1))); | |
122 if(progm == NULL) | |
123 return NULL; | |
124 | |
125 progm->rdman = rdman; | |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
126 |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
127 #ifndef UNITTEST |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
128 factory = rdman_get_ob_factory(rdman); |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
129 progm->complete = subject_new(factory, progm, OBJT_PROGM); |
194
45d9a1e2764d
Add mb_subtree_free animate action and fix bugs.
Thinker K.F. Li <thinker@branda.to>
parents:
192
diff
changeset
|
130 if(progm->complete == NULL) { |
45d9a1e2764d
Add mb_subtree_free animate action and fix bugs.
Thinker K.F. Li <thinker@branda.to>
parents:
192
diff
changeset
|
131 free(progm); |
45d9a1e2764d
Add mb_subtree_free animate action and fix bugs.
Thinker K.F. Li <thinker@branda.to>
parents:
192
diff
changeset
|
132 return NULL; |
45d9a1e2764d
Add mb_subtree_free animate action and fix bugs.
Thinker K.F. Li <thinker@branda.to>
parents:
192
diff
changeset
|
133 } |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
134 #endif /* UNITTEST */ |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
135 |
41 | 136 progm->n_words = 0; |
137 progm->max_words = max_words; | |
138 for(i = 0; i < max_words; i++) | |
139 STAILQ_INIT(progm->words[i].actions); | |
140 return progm; | |
141 } | |
142 | |
143 void mb_progm_free(mb_progm_t *progm) { | |
144 int n_words; | |
145 mb_word_t *word; | |
146 mb_action_t *cur_act; | |
147 int i; | |
148 | |
149 n_words = progm->n_words; | |
150 for(i = 0; i < n_words; i++) { | |
151 word = progm->words + i; | |
152 for(cur_act = STAILQ_HEAD(word->actions); | |
153 cur_act != NULL; | |
154 cur_act = STAILQ_HEAD(word->actions)) { | |
155 STAILQ_REMOVE(word->actions, mb_action_t, next, cur_act); | |
156 cur_act->free(cur_act); | |
157 } | |
158 } | |
124
ad5ab8e61c2b
Free subject of complete when a program is freed
Thinker K.F. Li <thinker@branda.to>
parents:
123
diff
changeset
|
159 |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
160 #ifndef UNITTEST |
192
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
161 subject_free(progm->complete); |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
162 #endif /* UNITTEST */ |
124
ad5ab8e61c2b
Free subject of complete when a program is freed
Thinker K.F. Li <thinker@branda.to>
parents:
123
diff
changeset
|
163 |
41 | 164 free(progm); |
165 } | |
166 | |
167 /*! \brief Add a new word into a program. | |
168 * | |
169 * The start time of new word should bigger or equal to last one. | |
170 * The time should be specified in incremental order. | |
171 */ | |
172 mb_word_t *mb_progm_next_word(mb_progm_t *progm, | |
173 const mb_timeval_t *start, | |
174 const mb_timeval_t *playing) { | |
175 mb_word_t *word; | |
176 if(progm->n_words >= progm->max_words) | |
177 return NULL; | |
178 if(progm->n_words && | |
179 MB_TIMEVAL_LATER(&progm->words[progm->n_words - 1].start_time, start)) | |
180 return NULL; | |
181 word = progm->words + progm->n_words++; | |
43
6270230b9248
Use MB_TIMEVAL_CP() instead of memcpy
Thinker K.F. Li <thinker@branda.to>
parents:
42
diff
changeset
|
182 MB_TIMEVAL_CP(&word->start_time, start); |
6270230b9248
Use MB_TIMEVAL_CP() instead of memcpy
Thinker K.F. Li <thinker@branda.to>
parents:
42
diff
changeset
|
183 MB_TIMEVAL_CP(&word->playing_time, playing); |
41 | 184 return word; |
185 } | |
186 | |
116
1d74eb3861b7
move animation actions from animate.c to files.
Thinker K.F. Li <thinker@branda.to>
parents:
111
diff
changeset
|
187 void mb_word_add_action(mb_word_t *word, mb_action_t *act) { |
41 | 188 STAILQ_INS_TAIL(word->actions, mb_action_t, next, act); |
189 } | |
190 | |
191 static void mb_word_start(mb_word_t *word, const mb_timeval_t *tmo, | |
192 const mb_timeval_t *now, redraw_man_t *rdman) { | |
42 | 193 mb_action_t *act; |
194 | |
195 for(act = STAILQ_HEAD(word->actions); | |
196 act != NULL; | |
197 act = STAILQ_NEXT(mb_action_t, next, act)) { | |
198 act->start(act, tmo, &word->playing_time, rdman); | |
199 } | |
41 | 200 } |
201 | |
202 static void mb_word_step(mb_word_t *word, const mb_timeval_t *tmo, | |
203 const mb_timeval_t *now, redraw_man_t *rdman) { | |
42 | 204 mb_action_t *act; |
205 | |
206 for(act = STAILQ_HEAD(word->actions); | |
207 act != NULL; | |
208 act = STAILQ_NEXT(mb_action_t, next, act)) { | |
209 act->step(act, tmo, rdman); | |
210 } | |
41 | 211 } |
212 | |
213 static void mb_word_stop(mb_word_t *word, const mb_timeval_t *tmo, | |
214 const mb_timeval_t *now, redraw_man_t *rdman) { | |
42 | 215 mb_action_t *act; |
216 | |
217 for(act = STAILQ_HEAD(word->actions); | |
218 act != NULL; | |
219 act = STAILQ_NEXT(mb_action_t, next, act)) { | |
220 act->stop(act, tmo, rdman); | |
221 } | |
41 | 222 } |
223 | |
122
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
224 /*! \brief Time stepping for a program. |
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
225 * |
46 | 226 * Any two actions in concurrent words never change the same attribute. |
227 * Start time of words in a program are specified in incremental order. | |
122
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
228 * |
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
229 * \note Program will take a big step at last monent. It is because |
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
230 * mb_progm_step() running mb_word_stop() if the word being stop |
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
231 * between now and next_tmo. It is not obviously if time stepping |
17e97e92b76e
Encapsulate X_MB_runtime_t and support X keyboard events.
Thinker K.F. Li <thinker@branda.to>
parents:
120
diff
changeset
|
232 * small. |
46 | 233 */ |
41 | 234 static void mb_progm_step(const mb_timeval_t *tmo, |
120 | 235 const mb_timeval_t *now, |
236 void *arg) { | |
41 | 237 mb_progm_t *progm = (mb_progm_t *)arg; |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
238 #ifndef UNITTEST |
129
ba581d8a4b9b
Now, tank1 can be controlled by user with keyboard
Thinker K.F. Li <thinker@branda.to>
parents:
128
diff
changeset
|
239 /*! \todo Leverage aspective programming to prevent problem of unittest. |
ba581d8a4b9b
Now, tank1 can be controlled by user with keyboard
Thinker K.F. Li <thinker@branda.to>
parents:
128
diff
changeset
|
240 */ |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
241 mb_progm_complete_t comp_evt; |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
242 #endif /* UNITTEST */ |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
243 mb_timeval_t next_tmo; |
41 | 244 mb_word_t *word; |
245 mb_timer_t *timer; | |
246 int i; | |
247 | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
248 MB_TIMEVAL_SET(&next_tmo, 0, STEP_INTERVAL); |
120 | 249 MB_TIMEVAL_ADD(&next_tmo, now); |
41 | 250 |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
251 /* _step() or _stop() words that in playing. */ |
41 | 252 i = progm->first_playing; |
253 for(word = progm->words + i; | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
254 i < progm->n_words && MB_TIMEVAL_LATER(tmo, &word->abs_start); |
41 | 255 word = progm->words + ++i) { |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
256 if(MB_TIMEVAL_LATER(tmo, &word->abs_stop)) |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
257 continue; |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
258 if(MB_TIMEVAL_LATER(&next_tmo, &word->abs_stop)) |
46 | 259 mb_word_stop(word, tmo, now, progm->rdman); |
260 else | |
41 | 261 mb_word_step(word, tmo, now, progm->rdman); |
262 } | |
263 | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
264 /* Start words that their abs_start is in duration |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
265 * from now to timeout for next update. |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
266 */ |
41 | 267 for(word = progm->words + i; |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
268 i < progm->n_words && MB_TIMEVAL_LATER(&next_tmo, &word->abs_start); |
41 | 269 word = progm->words + ++i) { |
270 mb_word_start(word, tmo, now, progm->rdman); | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
271 if(MB_TIMEVAL_LATER(&next_tmo, &word->abs_stop)) |
46 | 272 mb_word_stop(word, tmo, now, progm->rdman); |
41 | 273 } |
274 | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
275 /* Find a new first_playing if any consequence words, following current |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
276 * first_playing word, are stoped. |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
277 */ |
46 | 278 i = progm->first_playing; |
279 for(word = progm->words + i; | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
280 i < progm->n_words && MB_TIMEVAL_LATER(&next_tmo, &word->abs_stop); |
46 | 281 word = progm->words + ++i) { |
282 progm->first_playing++; | |
283 } | |
284 | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
285 /* Setup timeout for next update. */ |
41 | 286 if(progm->first_playing < progm->n_words) { |
42 | 287 word = progm->words + progm->first_playing; |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
288 if(MB_TIMEVAL_LATER(&word->abs_start, &next_tmo)) |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
289 MB_TIMEVAL_CP(&next_tmo, &word->abs_start); |
46 | 290 timer = mb_tman_timeout(progm->tman, &next_tmo, |
291 mb_progm_step, progm); | |
155
6749f6639924
Fix bug for STAILQ that fail to remove a node.
Thinker K.F. Li <thinker@branda.to>
parents:
153
diff
changeset
|
292 progm->cur_timer = timer; |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
293 } else { |
153
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
294 /* Make program to complete. */ |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
295 #ifndef UNITTEST |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
296 comp_evt.event.type = EVT_PROGM_COMPLETE; |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
297 comp_evt.event.tgt = comp_evt.event.cur_tgt = progm->complete; |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
298 comp_evt.progm = progm; |
192
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
299 subject_notify(progm->complete, &comp_evt.event); |
128
07dc9ec71221
Fix the problem that animate.c can not pass testcases
Thinker K.F. Li <thinker@branda.to>
parents:
124
diff
changeset
|
300 #endif /* UNITTEST */ |
153
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
301 progm->cur_timer = NULL; |
41 | 302 } |
303 } | |
304 | |
305 void mb_progm_start(mb_progm_t *progm, mb_tman_t *tman, | |
306 mb_timeval_t *now) { | |
307 mb_timer_t *timer; | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
308 mb_word_t *word; |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
309 int i; |
41 | 310 |
311 if(progm->n_words == 0) | |
312 return; | |
313 | |
314 progm->tman = tman; | |
43
6270230b9248
Use MB_TIMEVAL_CP() instead of memcpy
Thinker K.F. Li <thinker@branda.to>
parents:
42
diff
changeset
|
315 MB_TIMEVAL_CP(&progm->start_time, now); |
41 | 316 progm->first_playing = 0; |
317 | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
318 for(i = 0; i < progm->n_words; i++) { |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
319 word = progm->words + i; |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
320 MB_TIMEVAL_CP(&word->abs_start, &word->start_time); |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
321 MB_TIMEVAL_ADD(&word->abs_start, now); |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
322 MB_TIMEVAL_CP(&word->abs_stop, &word->abs_start); |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
323 MB_TIMEVAL_ADD(&word->abs_stop, &word->playing_time); |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
324 } |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
325 |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
326 if(MB_TIMEVAL_EQ(&progm->words[0].abs_start, now)) { |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
327 mb_progm_step(now, now, progm); |
41 | 328 return; |
329 } | |
330 | |
48
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
331 timer = mb_tman_timeout(tman, &progm->words[0].abs_start, |
bdf711cbf0fb
Use absolute time to dispatch animation actions.
Thinker K.F. Li <thinker@branda.to>
parents:
47
diff
changeset
|
332 mb_progm_step, progm); |
41 | 333 ASSERT(timer != NULL); |
153
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
334 |
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
335 /* We need timer to abort it. */ |
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
336 progm->cur_timer = timer; |
41 | 337 } |
338 | |
332
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
339 void mb_progm_finish(mb_progm_t *progm) { |
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
340 mb_timeval_t infi; |
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
341 |
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
342 mb_progm_abort(progm); |
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
343 MB_TIMEVAL_SET(&infi, 0x7fffffff,0); |
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
344 mb_progm_step(&progm->start_time, &infi,progm); |
f90c60967a9c
Add mb_progm_finish to terminate the current animation and put all objects in the final position.
wycc
parents:
194
diff
changeset
|
345 } |
153
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
346 void mb_progm_abort(mb_progm_t *progm) { |
131
6a8588df68af
Tank can change direction and navigate on the mud area
Thinker K.F. Li <thinker@branda.to>
parents:
129
diff
changeset
|
347 /*! \todo Make sure abort release resources. */ |
153
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
348 if(progm->cur_timer) { |
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
349 mb_tman_remove(progm->tman, progm->cur_timer); |
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
350 progm->cur_timer = NULL; |
9870b049b7f6
Make mb_progm_abort() work.
Thinker K.F. Li <thinker@branda.to>
parents:
131
diff
changeset
|
351 } |
42 | 352 } |
353 | |
123
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
354 /*! \brief Return event subject for completion of a program. |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
355 */ |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
356 subject_t *mb_progm_get_complete(mb_progm_t *progm) { |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
357 return progm->complete; |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
358 } |
9e2316dc6ecb
Program completion events
Thinker K.F. Li <thinker@branda.to>
parents:
122
diff
changeset
|
359 |
192
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
360 static void _free_completed_hdlr(event_t *event, void *arg) { |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
361 mb_progm_t *progm = (mb_progm_t *)arg; |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
362 |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
363 mb_progm_free(progm); |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
364 } |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
365 |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
366 /*! \brief The program should be freed after completed. */ |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
367 void mb_progm_free_completed(mb_progm_t *progm) { |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
368 subject_t *complete; |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
369 |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
370 complete = mb_progm_get_complete(progm); |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
371 subject_add_observer(complete, _free_completed_hdlr, progm); |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
372 } |
54fdc2a65242
Remove factory from observer APIs.
Thinker K.F. Li <thinker@branda.to>
parents:
186
diff
changeset
|
373 |
50 | 374 #ifdef UNITTEST |
375 | |
376 #include <CUnit/Basic.h> | |
377 | |
378 typedef struct _mb_dummy mb_dummy_t; | |
379 | |
380 struct _mb_dummy { | |
381 mb_action_t action; | |
382 int id; | |
383 int *logidx; | |
384 int *logs; | |
385 }; | |
386 | |
387 | |
388 static void mb_dummy_start(mb_action_t *act, | |
389 const mb_timeval_t *now, | |
390 const mb_timeval_t *playing_time, | |
391 redraw_man_t *rdman) { | |
392 mb_dummy_t *dummy = (mb_dummy_t *)act; | |
393 | |
394 dummy->logs[(*dummy->logidx)++] = dummy->id; | |
395 } | |
396 | |
397 static void mb_dummy_step(mb_action_t *act, | |
398 const mb_timeval_t *now, | |
399 redraw_man_t *rdman) { | |
400 mb_dummy_t *dummy = (mb_dummy_t *)act; | |
401 | |
402 dummy->logs[(*dummy->logidx)++] = dummy->id; | |
403 } | |
404 | |
405 static void mb_dummy_stop(mb_action_t *act, | |
406 const mb_timeval_t *now, | |
407 redraw_man_t *rdman) { | |
408 mb_dummy_t *dummy = (mb_dummy_t *)act; | |
409 | |
410 dummy->logs[(*dummy->logidx)++] = dummy->id; | |
411 } | |
412 | |
413 static void mb_dummy_free(mb_action_t *act) { | |
414 free(act); | |
415 } | |
416 | |
417 mb_action_t *mb_dummy_new(int id, int *logidx, int *logs, mb_word_t *word) { | |
418 mb_dummy_t *dummy; | |
419 | |
420 dummy = (mb_dummy_t *)malloc(sizeof(mb_dummy_t)); | |
421 if(dummy == NULL) | |
422 return NULL; | |
423 | |
424 dummy->id = id; | |
425 dummy->logidx = logidx; | |
426 dummy->logs = logs; | |
427 | |
428 dummy->action.start = mb_dummy_start; | |
429 dummy->action.step = mb_dummy_step; | |
430 dummy->action.stop = mb_dummy_stop; | |
431 dummy->action.free = mb_dummy_free; | |
432 | |
433 mb_word_add_action(word, (mb_action_t *)dummy); | |
434 | |
435 return (mb_action_t *)dummy; | |
436 } | |
437 | |
438 void test_animate_words(void) { | |
439 mb_progm_t *progm; | |
440 mb_word_t *word; | |
441 mb_action_t *acts[4]; | |
442 mb_tman_t *tman; | |
443 mb_timeval_t tv1, tv2, now, tmo_after; | |
444 int logcnt = 0; | |
445 int logs[256]; | |
446 int r; | |
447 | |
448 tman = mb_tman_new(); | |
449 CU_ASSERT(tman != NULL); | |
450 | |
451 progm = mb_progm_new(3, NULL); | |
452 CU_ASSERT(progm != NULL); | |
453 | |
454 MB_TIMEVAL_SET(&tv1, 1, 0); | |
455 MB_TIMEVAL_SET(&tv2, 0, STEP_INTERVAL * 3); | |
456 word = mb_progm_next_word(progm, &tv1, &tv2); | |
457 CU_ASSERT(word != NULL); | |
458 acts[0] = mb_dummy_new(0, &logcnt, logs, word); | |
459 CU_ASSERT(acts[0] != NULL); | |
460 | |
461 MB_TIMEVAL_SET(&tv1, 1, STEP_INTERVAL * 4 / 3); | |
462 MB_TIMEVAL_SET(&tv2, 0, STEP_INTERVAL / 3); | |
463 word = mb_progm_next_word(progm, &tv1, &tv2); | |
464 CU_ASSERT(word != NULL); | |
465 acts[1] = mb_dummy_new(1, &logcnt, logs, word); | |
466 CU_ASSERT(acts[1] != NULL); | |
467 | |
468 MB_TIMEVAL_SET(&tv1, 1, STEP_INTERVAL * 2); | |
469 MB_TIMEVAL_SET(&tv2, 0, STEP_INTERVAL * 3); | |
470 word = mb_progm_next_word(progm, &tv1, &tv2); | |
471 CU_ASSERT(word != NULL); | |
472 acts[2] = mb_dummy_new(2, &logcnt, logs, word); | |
473 CU_ASSERT(acts[2] != NULL); | |
474 | |
475 MB_TIMEVAL_SET(&now, 0, 0); | |
476 mb_progm_start(progm, tman, &now); | |
477 | |
478 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
479 CU_ASSERT(r == 0); | |
480 CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 1 && | |
481 MB_TIMEVAL_USEC(&tmo_after) == 0); | |
482 | |
483 /* 1.0s */ | |
484 MB_TIMEVAL_ADD(&now, &tmo_after); | |
485 mb_tman_handle_timeout(tman, &now); | |
486 CU_ASSERT(logcnt == 1); | |
487 | |
488 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
489 CU_ASSERT(r == 0); | |
490 CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 && | |
491 MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL); | |
492 | |
493 /* 1.1s */ | |
494 MB_TIMEVAL_ADD(&now, &tmo_after); | |
495 mb_tman_handle_timeout(tman, &now); | |
496 CU_ASSERT(logcnt == 4); | |
497 | |
498 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
499 CU_ASSERT(r == 0); | |
500 CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 && | |
501 MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL); | |
502 | |
503 /* 1.2s */ | |
504 MB_TIMEVAL_ADD(&now, &tmo_after); | |
505 mb_tman_handle_timeout(tman, &now); | |
506 CU_ASSERT(logcnt == 6); | |
507 | |
508 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
509 CU_ASSERT(r == 0); | |
510 CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 && | |
511 MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL); | |
512 | |
513 /* 1.3s */ | |
514 MB_TIMEVAL_ADD(&now, &tmo_after); | |
515 mb_tman_handle_timeout(tman, &now); | |
516 CU_ASSERT(logcnt == 8); | |
517 | |
518 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
519 CU_ASSERT(r == 0); | |
520 CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 && | |
521 MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL); | |
522 | |
523 /* 1.4s */ | |
524 MB_TIMEVAL_ADD(&now, &tmo_after); | |
525 mb_tman_handle_timeout(tman, &now); | |
526 CU_ASSERT(logcnt == 9); | |
527 | |
528 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
529 CU_ASSERT(r == 0); | |
530 CU_ASSERT(MB_TIMEVAL_SEC(&tmo_after) == 0 && | |
531 MB_TIMEVAL_USEC(&tmo_after) == STEP_INTERVAL); | |
532 | |
533 /* 1.5s */ | |
534 MB_TIMEVAL_ADD(&now, &tmo_after); | |
535 mb_tman_handle_timeout(tman, &now); | |
536 CU_ASSERT(logcnt == 10); | |
537 | |
538 r = mb_tman_next_timeout(tman, &now, &tmo_after); | |
539 CU_ASSERT(r == -1); | |
540 | |
541 mb_progm_free(progm); | |
542 mb_tman_free(tman); | |
543 } | |
544 | |
545 CU_pSuite get_animate_suite(void) { | |
546 CU_pSuite suite; | |
547 | |
548 suite = CU_add_suite("Suite_animate", NULL, NULL); | |
549 if(suite == NULL) | |
550 return NULL; | |
551 | |
552 CU_ADD_TEST(suite, test_animate_words); | |
553 | |
554 return suite; | |
555 } | |
556 | |
557 #endif /* UNITTEST */ |