Statistics
| Branch: | Tag: | Revision:

hlrc / server / src / Animation.cpp @ d81c4f8b

History | View | Annotate | Download (7.505 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 90fa4b4e Robert Haschke
#include <stdexcept>
31
32 0c286af0 Simon Schulz
using namespace std;
33
34 f150aab5 Robert Haschke
Animation::Animation() {
35
        active = false;
36
        repetitions = 1;
37
        duration_each = 1000.0; // in ms
38
        scale = 1.0;
39 0c286af0 Simon Schulz
}
40
41 f150aab5 Robert Haschke
Animation::~Animation() {
42 0c286af0 Simon Schulz
}
43
44 f150aab5 Robert Haschke
void Animation::start() {
45
        if (active) {
46
                printf("> Animation already active. ignored play() call\n");
47
                return;
48
        }
49 0c286af0 Simon Schulz
50 f150aab5 Robert Haschke
        active = true;
51
        repetition_now = 0;
52 0c286af0 Simon Schulz
53 f150aab5 Robert Haschke
        // Set up end time
54
        int duration = duration_each * repetitions;
55 90fa4b4e Robert Haschke
        start_time = std::chrono::steady_clock::now();
56
        end_time = start_time + std::chrono::milliseconds(duration);
57 0c286af0 Simon Schulz
}
58
59 f150aab5 Robert Haschke
bool Animation::is_active() {
60
        return active;
61 0c286af0 Simon Schulz
}
62
63
//! calculate motion profile based on constant acceleration
64
//! \param  tn time now (normalized to [0...1[)
65
//! \return value [0...1], f[tn=0] = 0
66 f150aab5 Robert Haschke
float Animation::apply_motion_profile_positive(float tn) {
67
        float result = 0.0;
68
69
        if ((tn < 0.0) || (tn > 1.0)) {
70
                printf("> Animation::apply_motion_profile(%4.2f) called with invalid input (allowed is [0..1])\n", tn);
71
                return 0.0;
72
        }
73
74
        if (tn <= 0.5) {
75
                // accelerated motion
76
                result = (2.0 * pow(tn, 2));
77
        }
78
        else if (tn <= 1.0) {
79
                // deaccelerate
80
                result = (1.0 - 2.0 * pow(1.0 - tn, 2));
81
        }
82
        return result;
83 0c286af0 Simon Schulz
}
84
85 f150aab5 Robert Haschke
// start from 0, go to +1, go to -1, ... , -1 to 0
86
float Animation::apply_motion_profile_symmetric(float tn, unsigned int rep) {
87
        unsigned int phases = 2 * rep + 1;
88
        float fstep = 1.0 / phases;
89
        float result = 0.0;
90
91
        if (tn > 1.0) {
92
                printf("> apply_motion_profile_symmetric(%f, %d) -> invalid tn > 1.0!\n", tn, rep);
93
                exit(1);
94
        }
95
        else if (tn <= fstep) {
96
                // first slow advance to +1.0
97
                result = apply_motion_profile_positive(tn / (fstep));
98
        }
99
        else if (tn >= 1.0 - fstep) {
100
                // last slow return from -1 to 0
101
                result = -1.0 + apply_motion_profile_positive((tn - (1.0 - fstep)) / (fstep));
102
        }
103
        else {
104
                // motion in between, alternating between +1,-1,+1,...,-1
105
                // convert to ]0,phases-fstep]
106
                float tnn = tn - fstep;
107
                // convert to (multiples) ]0,fstep]
108
                tnn = fmod(tnn, 2 * fstep);
109
                // result has to be from+1 to -1
110
                if (tnn <= fstep) {
111
                        //+1 -> -1
112
                        result = 1.0 - 2.0 * apply_motion_profile_positive(tnn / fstep);
113
                }
114
                else {
115
                        //-1 -> +1
116
                        result = -1.0 + 2.0 * apply_motion_profile_positive((tnn - fstep) / fstep);
117
                }
118
        }
119
        return result;
120 0c286af0 Simon Schulz
}
121
122 f150aab5 Robert Haschke
// start from 0, go to +1, goto 0, ... , +1 to 0
123
float Animation::apply_motion_profile_asymmetric(float tn, unsigned int rep) {
124
        unsigned int phases = 2 * rep;
125
        float fstep = 1.0 / phases;
126
        float result = 0.0;
127
128
        // motion in between, alternating between 0,1,0,1...
129
        // convert to (multiples) ]0,2*fstep]
130
        float tnn = fmod(tn, 2 * fstep);
131
132
        // result has to be from+1 to -1
133
        if (tnn <= fstep) {
134
                // 0->1
135
                result = apply_motion_profile_positive(tnn / fstep);
136
        }
137
        else {
138
                // 1->0
139
                result = 1.0 - apply_motion_profile_positive((tnn - fstep) / fstep);
140
        }
141
        return result;
142 0c286af0 Simon Schulz
}
143
144 f150aab5 Robert Haschke
void Animation::apply_on_gazestate(humotion::GazeState* gaze) {
145
        // first check: active?
146
        if (!active) {
147
                return;
148
        }
149
150
        // now check if this animation ended:
151 90fa4b4e Robert Haschke
        std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
152 f150aab5 Robert Haschke
        if (now > end_time) {
153
                active = false;
154
                return;
155
        }
156
157
        // milliseconds from the beginning of this ani:
158 90fa4b4e Robert Haschke
        float elapsed_ms = std::chrono::duration<float, milli>(now - start_time).count();
159
        // printf("milli %f\n",elapsed_ms);
160 f150aab5 Robert Haschke
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
}