Revision f150aab5 server/src/Animation.cpp

View differences:

server/src/Animation.cpp
1 1
/*
2
* 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
*/
2
 * 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 28

  
29 29
#include "Animation.h"
30 30
using namespace std;
31 31
using namespace boost;
32 32

  
33
Animation::Animation(){
34
    active = false;
35
    repetitions = 1;
36
    duration_each = 1000.0; //in ms
37
    scale = 1.0;
33
Animation::Animation() {
34
	active = false;
35
	repetitions = 1;
36
	duration_each = 1000.0; // in ms
37
	scale = 1.0;
38 38
}
39 39

  
40
Animation::~Animation(){
40
Animation::~Animation() {
41 41
}
42 42

  
43
void Animation::start(){
44
    if (active){
45
        printf("> Animation already active. ignored play() call\n");
46
        return;
47
    }
43
void Animation::start() {
44
	if (active) {
45
		printf("> Animation already active. ignored play() call\n");
46
		return;
47
	}
48 48

  
49
    active = true;
50
    repetition_now = 0;
49
	active = true;
50
	repetition_now = 0;
51 51

  
52
    //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);
52
	// 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 56
}
57 57

  
58
bool Animation::is_active(){
59
    return active;
58
bool Animation::is_active() {
59
	return active;
60 60
}
61 61

  
62 62
//! calculate motion profile based on constant acceleration
63 63
//! \param  tn time now (normalized to [0...1[)
64 64
//! \return value [0...1], f[tn=0] = 0
65
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
    }else if (tn <= 1.0){
77
        //deaccelerate
78
        result = (1.0 - 2.0 * pow(1.0 - tn, 2));
79
    }
80
    return result;
65
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;
81 82
}
82 83

  
83
//start from 0, go to +1, go to -1, ... , -1 to 0
84
float Animation::apply_motion_profile_symmetric(float tn, unsigned int rep){
85
    unsigned int phases = 2*rep + 1;
86
    float fstep = 1.0 / phases;
87
    float result = 0.0;
88

  
89
    if (tn > 1.0){
90
        printf("> apply_motion_profile_symmetric(%f, %d) -> invalid tn > 1.0!\n",tn,rep);
91
        exit(1);
92
    }else if (tn <= fstep){
93
        //first slow advance to +1.0
94
        result = apply_motion_profile_positive(tn / (fstep));
95
    }else if (tn >= 1.0-fstep){
96
        //last slow return from -1 to 0
97
        result = -1.0 + apply_motion_profile_positive((tn-(1.0-fstep)) / (fstep));
98
    }else{
99
        //motion in between, alternating between +1,-1,+1,...,-1
100
        //convert to ]0,phases-fstep]
101
        float tnn = tn - fstep;
102
        //convert to (multiples) ]0,fstep]
103
        tnn = fmod(tnn, 2*fstep);
104
        //result has to be from+1 to -1
105
        if (tnn <= fstep){
106
            //+1 -> -1
107
            result = 1.0 - 2.0 * apply_motion_profile_positive(tnn/fstep);
108
        }else{
109
            //-1 -> +1
110
            result = -1.0 + 2.0 * apply_motion_profile_positive((tnn-fstep)/fstep);
111
        }
112
    }
113
    return result;
84
// 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;
114 119
}
115 120

  
116
//start from 0, go to +1, goto 0, ... , +1 to 0
117
float Animation::apply_motion_profile_asymmetric(float tn, unsigned int rep){
118
    unsigned int phases = 2*rep;
119
    float fstep = 1.0 / phases;
120
    float result = 0.0;
121

  
122
    //motion in between, alternating between 0,1,0,1...
123
    //convert to (multiples) ]0,2*fstep]
124
    float tnn = fmod(tn, 2*fstep);
125

  
126
    //result has to be from+1 to -1
127
    if (tnn <= fstep){
128
        //0->1
129
        result = apply_motion_profile_positive(tnn/fstep);
130
    }else{
131
        //1->0
132
        result = 1.0 - apply_motion_profile_positive((tnn-fstep)/fstep);
133
    }
134
    return result;
121
// 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;
135 141
}
136 142

  
137
void Animation::apply_on_gazestate(humotion::GazeState *gaze){
138
    //first check: active?
139
    if (!active){
140
        return;
141
    }
142

  
143
    //now check if this animation ended:
144
    system_time now = get_system_time(); //posix_time::microsec_clock::local_time();
145
    if (now > end_time){
146
        active = false;
147
        return;
148
    }
149

  
150
    //milliseconds from the beginning of this ani:
151
    posix_time::time_duration diff = now - start_time;
152
    float elapsed_ms = diff.total_milliseconds();
153
    //printf("mili %f\n",elapsed_ms);
154

  
155
    float max_elongation = 0.0;
156
    float profile = 0.0;
157

  
158

  
159
    //TODO: animation subclasses instead of switch() !!!
160
    switch((int)target){
161
        case(IDLE):
162
            //nothing to do!
163
            break;
164

  
165
        case(HEAD_SHAKE):
166
            max_elongation = scale * 15.0;
167
            profile = apply_motion_profile_symmetric(elapsed_ms / (repetitions * duration_each), repetitions);
168
            gaze->pan_offset = 0.5 * max_elongation * profile;
169
            //printf("%f %f %f %f DDD\n",elapsed_ms,elapsed_ms / (repetitions * duration_each), profile, gaze->pan_offset);fflush(stdout);
170
            break;
171

  
172

  
173
        case(HEAD_NOD):
174
            max_elongation = scale * 10.0;
175
            profile = apply_motion_profile_asymmetric(elapsed_ms / (repetitions * duration_each), repetitions);
176
            gaze->tilt_offset = - max_elongation * profile;
177
            break;
178

  
179
        case(EYEBLINK_L):
180
            if(elapsed_ms >= repetition_now*duration_each){
181
                //NOTE: duration/2 because we close the eye for 0.5x the time and keep it open for 0.5x time ;)
182
                //      scale can influence this 0.5/0.5 divider. i.e. scale = 0.5 -> 0.25*t closed & 0.75*t open
183
                gaze->eyeblink_request_left = scale * duration_each/2;
184
                repetition_now++;
185
            }
186
            break;
187

  
188
        case(EYEBLINK_R):
189
            if(elapsed_ms >= repetition_now*duration_each){
190
                //NOTE: see EYEBLINK_L for formula description
191
                gaze->eyeblink_request_right = scale * duration_each/2;
192
                repetition_now++;
193
            }
194
            break;
195

  
196
        case(EYEBLINK_BOTH):
197
            if(elapsed_ms >= repetition_now*duration_each){
198
                //NOTE: see EYEBLINK_L for formula description
199
                gaze->eyeblink_request_left  = scale * duration_each/2;
200
                gaze->eyeblink_request_right = scale * duration_each/2;
201
                repetition_now++;
202
            }
203
            break;
204

  
205
        case(ENGAGEMENT_LEFT):
206
            //TODO: use smooth animation instead of step like change
207
            gaze->pan_offset = scale * -10.0;
208
            break;
209

  
210
        case(ENGAGEMENT_RIGHT):
211
            //TODO: use smooth animation instead of step like change
212
            gaze->pan_offset = scale * 10.0;
213
            break;
214

  
215
        case(EYEBROWS_RAISE):
216
            profile = 15.0 * scale * apply_motion_profile_asymmetric(elapsed_ms / (repetitions * duration_each), repetitions);
217
            gaze->eyebrow_left   += -profile;
218
            gaze->eyebrow_right  +=  profile;
219
            break;
220

  
221
        case(EYEBROWS_LOWER):
222
            profile = 15.0 * scale * apply_motion_profile_asymmetric(elapsed_ms / (repetitions * duration_each), repetitions);
223
            gaze->eyebrow_left   +=  profile;
224
            gaze->eyebrow_right  += -profile;
225
            break;
226

  
227

  
228

  
229
        default:
230
            printf("> invalid animation target id (%d)\n",(int)target);
231
            active = false;
232
            throw runtime_error("unsupported animation id requested");
233
            break;
234
    }
235

  
236

  
237
    //ok, currently active. overwrite values:
238
    //gaze->tilt_offset = 20;
239
    //gaze->roll_offset = 20;
240

  
143
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 241
}
242 242

  
243
bool Animation::collides_with(Animation* ani) {
244
	// same type -> always bad
245
	if (ani->target == target)
246
		return true;
243 247

  
244
bool Animation::collides_with(Animation *ani){
245
    //same type -> always bad
246
    if (ani->target == target) return true;
247

  
248
    switch((int)target){
249
        case(IDLE):
250
            break;
251

  
252
        default:
253
            throw runtime_error("missing collides_with() case for animation");
254
            break;
255

  
256
        case(HEAD_SHAKE):
257
            break;
248
	switch ((int)target) {
249
		case (IDLE):
250
			break;
258 251

  
252
		default:
253
			throw runtime_error("missing collides_with() case for animation");
254
			break;
259 255

  
260
        case(HEAD_NOD):
261
            break;
256
		case (HEAD_SHAKE):
257
			break;
262 258

  
263
        case(EYEBLINK_L):
264
            if (ani->target == EYEBLINK_BOTH) return true;
265
            break;
259
		case (HEAD_NOD):
260
			break;
266 261

  
267
        case(EYEBLINK_R):
268
            if (ani->target == EYEBLINK_BOTH) return true;
269
            break;
262
		case (EYEBLINK_L):
263
			if (ani->target == EYEBLINK_BOTH)
264
				return true;
265
			break;
270 266

  
271
        case(EYEBLINK_BOTH):
272
            if (ani->target == EYEBLINK_L) return true;
273
            if (ani->target == EYEBLINK_R) return true;
274
            break;
267
		case (EYEBLINK_R):
268
			if (ani->target == EYEBLINK_BOTH)
269
				return true;
270
			break;
275 271

  
276
        case(EYEBROWS_RAISE):
277
            break;
272
		case (EYEBLINK_BOTH):
273
			if (ani->target == EYEBLINK_L)
274
				return true;
275
			if (ani->target == EYEBLINK_R)
276
				return true;
277
			break;
278 278

  
279
        case(EYEBROWS_LOWER):
280
            break;
281
    }
279
		case (EYEBROWS_RAISE):
280
			break;
282 281

  
283
    //fine
284
    return false;
282
		case (EYEBROWS_LOWER):
283
			break;
284
	}
285 285

  
286
	// fine
287
	return false;
286 288
}

Also available in: Unified diff