hlrc / server / src / Animation.cpp @ master
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 | } |