hlrc / server / src / Animation.cpp @ f150aab5
History | View | Annotate | Download (7.547 KB)
1 | 0c286af0 | Simon Schulz | /*
|
---|---|---|---|
2 | f150aab5 | Robert Haschke | * This file is part of hlrc_server
|
3 | *
|
||
4 | * Copyright(c) sschulz <AT> techfak.uni-bielefeld.de
|
||
5 | * http://opensource.cit-ec.de/projects/hlrc_server
|
||
6 | *
|
||
7 | * This file may be licensed under the terms of the
|
||
8 | * GNU General Public License Version 3 (the ``GPL''),
|
||
9 | * or (at your option) any later version.
|
||
10 | *
|
||
11 | * Software distributed under the License is distributed
|
||
12 | * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
|
||
13 | * express or implied. See the GPL for the specific language
|
||
14 | * governing rights and limitations.
|
||
15 | *
|
||
16 | * You should have received a copy of the GPL along with this
|
||
17 | * program. If not, go to http://www.gnu.org/licenses/gpl.html
|
||
18 | * or write to the Free Software Foundation, Inc.,
|
||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
20 | *
|
||
21 | * The development of this software was supported by the
|
||
22 | * Excellence Cluster EXC 277 Cognitive Interaction Technology.
|
||
23 | * The Excellence Cluster EXC 277 is a grant of the Deutsche
|
||
24 | * Forschungsgemeinschaft (DFG) in the context of the German
|
||
25 | * Excellence Initiative.
|
||
26 | *
|
||
27 | */
|
||
28 | 0c286af0 | Simon Schulz | |
29 | #include "Animation.h" |
||
30 | using namespace std; |
||
31 | using namespace boost; |
||
32 | |||
33 | f150aab5 | Robert Haschke | Animation::Animation() { |
34 | active = false;
|
||
35 | repetitions = 1;
|
||
36 | duration_each = 1000.0; // in ms |
||
37 | scale = 1.0; |
||
38 | 0c286af0 | Simon Schulz | } |
39 | |||
40 | f150aab5 | Robert Haschke | Animation::~Animation() { |
41 | 0c286af0 | Simon Schulz | } |
42 | |||
43 | f150aab5 | Robert Haschke | void Animation::start() {
|
44 | if (active) {
|
||
45 | printf("> Animation already active. ignored play() call\n");
|
||
46 | return;
|
||
47 | } |
||
48 | 0c286af0 | Simon Schulz | |
49 | f150aab5 | Robert Haschke | active = true;
|
50 | repetition_now = 0;
|
||
51 | 0c286af0 | Simon Schulz | |
52 | f150aab5 | Robert Haschke | // Set up end time
|
53 | int duration = duration_each * repetitions;
|
||
54 | start_time = get_system_time(); // posix_time::microsec_clock::local_time();
|
||
55 | end_time = start_time + boost::posix_time::milliseconds(duration); |
||
56 | 0c286af0 | Simon Schulz | } |
57 | |||
58 | f150aab5 | Robert Haschke | bool Animation::is_active() {
|
59 | return active;
|
||
60 | 0c286af0 | Simon Schulz | } |
61 | |||
62 | //! calculate motion profile based on constant acceleration
|
||
63 | //! \param tn time now (normalized to [0...1[)
|
||
64 | //! \return value [0...1], f[tn=0] = 0
|
||
65 | f150aab5 | Robert Haschke | float Animation::apply_motion_profile_positive(float tn) { |
66 | float result = 0.0; |
||
67 | |||
68 | if ((tn < 0.0) || (tn > 1.0)) { |
||
69 | printf("> Animation::apply_motion_profile(%4.2f) called with invalid input (allowed is [0..1])\n", tn);
|
||
70 | return 0.0; |
||
71 | } |
||
72 | |||
73 | if (tn <= 0.5) { |
||
74 | // accelerated motion
|
||
75 | result = (2.0 * pow(tn, 2)); |
||
76 | } |
||
77 | else if (tn <= 1.0) { |
||
78 | // deaccelerate
|
||
79 | result = (1.0 - 2.0 * pow(1.0 - tn, 2)); |
||
80 | } |
||
81 | return result;
|
||
82 | 0c286af0 | Simon Schulz | } |
83 | |||
84 | f150aab5 | Robert Haschke | // start from 0, go to +1, go to -1, ... , -1 to 0
|
85 | float Animation::apply_motion_profile_symmetric(float tn, unsigned int rep) { |
||
86 | unsigned int phases = 2 * rep + 1; |
||
87 | float fstep = 1.0 / phases; |
||
88 | float result = 0.0; |
||
89 | |||
90 | if (tn > 1.0) { |
||
91 | printf("> apply_motion_profile_symmetric(%f, %d) -> invalid tn > 1.0!\n", tn, rep);
|
||
92 | exit(1);
|
||
93 | } |
||
94 | else if (tn <= fstep) { |
||
95 | // first slow advance to +1.0
|
||
96 | result = apply_motion_profile_positive(tn / (fstep)); |
||
97 | } |
||
98 | else if (tn >= 1.0 - fstep) { |
||
99 | // last slow return from -1 to 0
|
||
100 | result = -1.0 + apply_motion_profile_positive((tn - (1.0 - fstep)) / (fstep)); |
||
101 | } |
||
102 | else {
|
||
103 | // motion in between, alternating between +1,-1,+1,...,-1
|
||
104 | // convert to ]0,phases-fstep]
|
||
105 | float tnn = tn - fstep;
|
||
106 | // convert to (multiples) ]0,fstep]
|
||
107 | tnn = fmod(tnn, 2 * fstep);
|
||
108 | // result has to be from+1 to -1
|
||
109 | if (tnn <= fstep) {
|
||
110 | //+1 -> -1
|
||
111 | result = 1.0 - 2.0 * apply_motion_profile_positive(tnn / fstep); |
||
112 | } |
||
113 | else {
|
||
114 | //-1 -> +1
|
||
115 | result = -1.0 + 2.0 * apply_motion_profile_positive((tnn - fstep) / fstep); |
||
116 | } |
||
117 | } |
||
118 | return result;
|
||
119 | 0c286af0 | Simon Schulz | } |
120 | |||
121 | f150aab5 | Robert Haschke | // start from 0, go to +1, goto 0, ... , +1 to 0
|
122 | float Animation::apply_motion_profile_asymmetric(float tn, unsigned int rep) { |
||
123 | unsigned int phases = 2 * rep; |
||
124 | float fstep = 1.0 / phases; |
||
125 | float result = 0.0; |
||
126 | |||
127 | // motion in between, alternating between 0,1,0,1...
|
||
128 | // convert to (multiples) ]0,2*fstep]
|
||
129 | float tnn = fmod(tn, 2 * fstep); |
||
130 | |||
131 | // result has to be from+1 to -1
|
||
132 | if (tnn <= fstep) {
|
||
133 | // 0->1
|
||
134 | result = apply_motion_profile_positive(tnn / fstep); |
||
135 | } |
||
136 | else {
|
||
137 | // 1->0
|
||
138 | result = 1.0 - apply_motion_profile_positive((tnn - fstep) / fstep); |
||
139 | } |
||
140 | return result;
|
||
141 | 0c286af0 | Simon Schulz | } |
142 | |||
143 | f150aab5 | Robert Haschke | void Animation::apply_on_gazestate(humotion::GazeState* gaze) {
|
144 | // first check: active?
|
||
145 | if (!active) {
|
||
146 | return;
|
||
147 | } |
||
148 | |||
149 | // now check if this animation ended:
|
||
150 | system_time now = get_system_time(); // posix_time::microsec_clock::local_time();
|
||
151 | if (now > end_time) {
|
||
152 | active = false;
|
||
153 | return;
|
||
154 | } |
||
155 | |||
156 | // milliseconds from the beginning of this ani:
|
||
157 | posix_time::time_duration diff = now - start_time; |
||
158 | float elapsed_ms = diff.total_milliseconds();
|
||
159 | // printf("mili %f\n",elapsed_ms);
|
||
160 | |||
161 | float max_elongation = 0.0; |
||
162 | float profile = 0.0; |
||
163 | |||
164 | // TODO: animation subclasses instead of switch() !!!
|
||
165 | switch ((int)target) { |
||
166 | case (IDLE):
|
||
167 | // nothing to do!
|
||
168 | break;
|
||
169 | |||
170 | case (HEAD_SHAKE):
|
||
171 | max_elongation = scale * 15.0; |
||
172 | profile = apply_motion_profile_symmetric(elapsed_ms / (repetitions * duration_each), repetitions); |
||
173 | gaze->pan_offset = 0.5 * max_elongation * profile; |
||
174 | // printf("%f %f %f %f DDD\n",elapsed_ms,elapsed_ms / (repetitions * duration_each), profile, gaze->pan_offset);fflush(stdout);
|
||
175 | break;
|
||
176 | |||
177 | case (HEAD_NOD):
|
||
178 | max_elongation = scale * 10.0; |
||
179 | profile = apply_motion_profile_asymmetric(elapsed_ms / (repetitions * duration_each), repetitions); |
||
180 | gaze->tilt_offset = -max_elongation * profile; |
||
181 | break;
|
||
182 | |||
183 | case (EYEBLINK_L):
|
||
184 | if (elapsed_ms >= repetition_now * duration_each) {
|
||
185 | // NOTE: duration/2 because we close the eye for 0.5x the time and keep it open for 0.5x time ;)
|
||
186 | // scale can influence this 0.5/0.5 divider. i.e. scale = 0.5 -> 0.25*t closed & 0.75*t open
|
||
187 | gaze->eyeblink_request_left = scale * duration_each / 2;
|
||
188 | repetition_now++; |
||
189 | } |
||
190 | break;
|
||
191 | |||
192 | case (EYEBLINK_R):
|
||
193 | if (elapsed_ms >= repetition_now * duration_each) {
|
||
194 | // NOTE: see EYEBLINK_L for formula description
|
||
195 | gaze->eyeblink_request_right = scale * duration_each / 2;
|
||
196 | repetition_now++; |
||
197 | } |
||
198 | break;
|
||
199 | |||
200 | case (EYEBLINK_BOTH):
|
||
201 | if (elapsed_ms >= repetition_now * duration_each) {
|
||
202 | // NOTE: see EYEBLINK_L for formula description
|
||
203 | gaze->eyeblink_request_left = scale * duration_each / 2;
|
||
204 | gaze->eyeblink_request_right = scale * duration_each / 2;
|
||
205 | repetition_now++; |
||
206 | } |
||
207 | break;
|
||
208 | |||
209 | case (ENGAGEMENT_LEFT):
|
||
210 | // TODO: use smooth animation instead of step like change
|
||
211 | gaze->pan_offset = scale * -10.0; |
||
212 | break;
|
||
213 | |||
214 | case (ENGAGEMENT_RIGHT):
|
||
215 | // TODO: use smooth animation instead of step like change
|
||
216 | gaze->pan_offset = scale * 10.0; |
||
217 | break;
|
||
218 | |||
219 | case (EYEBROWS_RAISE):
|
||
220 | profile = 15.0 * scale * apply_motion_profile_asymmetric(elapsed_ms / (repetitions * duration_each), repetitions); |
||
221 | gaze->eyebrow_left += -profile; |
||
222 | gaze->eyebrow_right += profile; |
||
223 | break;
|
||
224 | |||
225 | case (EYEBROWS_LOWER):
|
||
226 | profile = 15.0 * scale * apply_motion_profile_asymmetric(elapsed_ms / (repetitions * duration_each), repetitions); |
||
227 | gaze->eyebrow_left += profile; |
||
228 | gaze->eyebrow_right += -profile; |
||
229 | break;
|
||
230 | |||
231 | default:
|
||
232 | printf("> invalid animation target id (%d)\n", (int)target); |
||
233 | active = false;
|
||
234 | throw runtime_error("unsupported animation id requested"); |
||
235 | break;
|
||
236 | } |
||
237 | |||
238 | // ok, currently active. overwrite values:
|
||
239 | // gaze->tilt_offset = 20;
|
||
240 | // gaze->roll_offset = 20;
|
||
241 | 0c286af0 | Simon Schulz | } |
242 | |||
243 | f150aab5 | Robert Haschke | bool Animation::collides_with(Animation* ani) {
|
244 | // same type -> always bad
|
||
245 | if (ani->target == target)
|
||
246 | return true; |
||
247 | 0c286af0 | Simon Schulz | |
248 | f150aab5 | Robert Haschke | switch ((int)target) { |
249 | case (IDLE):
|
||
250 | break;
|
||
251 | 0c286af0 | Simon Schulz | |
252 | f150aab5 | Robert Haschke | default:
|
253 | throw runtime_error("missing collides_with() case for animation"); |
||
254 | break;
|
||
255 | 0c286af0 | Simon Schulz | |
256 | f150aab5 | Robert Haschke | case (HEAD_SHAKE):
|
257 | break;
|
||
258 | 0c286af0 | Simon Schulz | |
259 | f150aab5 | Robert Haschke | case (HEAD_NOD):
|
260 | break;
|
||
261 | 0c286af0 | Simon Schulz | |
262 | f150aab5 | Robert Haschke | case (EYEBLINK_L):
|
263 | if (ani->target == EYEBLINK_BOTH)
|
||
264 | return true; |
||
265 | break;
|
||
266 | 0c286af0 | Simon Schulz | |
267 | f150aab5 | Robert Haschke | case (EYEBLINK_R):
|
268 | if (ani->target == EYEBLINK_BOTH)
|
||
269 | return true; |
||
270 | break;
|
||
271 | 0c286af0 | Simon Schulz | |
272 | f150aab5 | Robert Haschke | case (EYEBLINK_BOTH):
|
273 | if (ani->target == EYEBLINK_L)
|
||
274 | return true; |
||
275 | if (ani->target == EYEBLINK_R)
|
||
276 | return true; |
||
277 | break;
|
||
278 | 0c286af0 | Simon Schulz | |
279 | f150aab5 | Robert Haschke | case (EYEBROWS_RAISE):
|
280 | break;
|
||
281 | 0c286af0 | Simon Schulz | |
282 | f150aab5 | Robert Haschke | case (EYEBROWS_LOWER):
|
283 | break;
|
||
284 | } |
||
285 | 0c286af0 | Simon Schulz | |
286 | f150aab5 | Robert Haschke | // fine
|
287 | return false; |
||
288 | 0c286af0 | Simon Schulz | } |