Graph Editor: Restoring most tools
[blender-staging.git] / source / blender / editors / animation / keyframes_general.c
1 /**
2  * $Id: 
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation
21  * All rights reserved.
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #include <float.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_blenlib.h"
36 #include "BLI_arithb.h"
37
38 #include "DNA_anim_types.h"
39 #include "DNA_action_types.h"
40 #include "DNA_curve_types.h"
41 #include "DNA_ipo_types.h" // XXX to be removed
42 #include "DNA_key_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_space_types.h"
45 #include "DNA_scene_types.h"
46
47 #include "BKE_action.h"
48 #include "BKE_fcurve.h"
49 #include "BKE_key.h"
50 #include "BKE_utildefines.h"
51
52 #include "ED_anim_api.h"
53 #include "ED_keyframing.h"
54 #include "ED_keyframes_edit.h"
55
56 /* This file contains code for various keyframe-editing tools which are 'destructive'
57  * (i.e. they will modify the order of the keyframes, and change the size of the array).
58  * While some of these tools may eventually be moved out into blenkernel, for now, it is
59  * fine to have these calls here.
60  * 
61  * There are also a few tools here which cannot be easily coded for in the other system (yet).
62  * These may also be moved around at some point, but for now, they 
63  *
64  * - Joshua Leung, Dec 2008
65  */
66  
67 /* **************************************************** */
68
69 /* Only delete the nominated keyframe from provided ipo-curve. 
70  * Not recommended to be used many times successively. For that
71  * there is delete_ipo_keys(). 
72  */
73 void delete_fcurve_key(FCurve *fcu, int index, short do_recalc)
74 {
75         /* firstly check that index is valid */
76         if (index < 0) 
77                 index *= -1;
78         if (fcu == NULL) 
79                 return;
80         if (index >= fcu->totvert)
81                 return;
82         
83         /*      Delete this key */
84         memmove(&fcu->bezt[index], &fcu->bezt[index+1], sizeof(BezTriple)*(fcu->totvert-index-1));
85         fcu->totvert--;
86         
87         /* recalc handles - only if it won't cause problems */
88         if (do_recalc)
89                 calchandles_fcurve(fcu);
90 }
91
92 /* Delete selected keyframes in given F-Curve */
93 void delete_fcurve_keys(FCurve *fcu)
94 {
95         int i;
96         
97         /* Delete selected BezTriples */
98         for (i=0; i < fcu->totvert; i++) {
99                 if (fcu->bezt[i].f2 & SELECT) {
100                         memmove(&fcu->bezt[i], &fcu->bezt[i+1], sizeof(BezTriple)*(fcu->totvert-i-1));
101                         fcu->totvert--;
102                         i--;
103                 }
104         }
105         
106 #if 0 // XXX for now, we don't get rid of empty curves...
107         /* Only delete if there isn't an ipo-driver still hanging around on an empty curve */
108         if ((icu->totvert==0) && (icu->driver==NULL)) {
109                 BLI_remlink(&ipo->curve, icu);
110                 free_ipo_curve(icu);
111         }
112 #endif 
113 }
114
115 /* ---------------- */
116
117 /* duplicate selected keyframes for the given F-Curve */
118 void duplicate_fcurve_keys(FCurve *fcu)
119 {
120         BezTriple *newbezt;
121         int i;
122
123         if (fcu == NULL)
124                 return;
125         
126         // XXX this does not take into account sample data...
127         for (i=0; i < fcu->totvert; i++) {
128                 /* If a key is selected */
129                 if (fcu->bezt[i].f2 & SELECT) {
130                         /* Expand the list */
131                         newbezt = MEM_callocN(sizeof(BezTriple) * (fcu->totvert+1), "beztriple");
132                         
133                         memcpy(newbezt, fcu->bezt, sizeof(BezTriple) * (i+1));
134                         memcpy(newbezt+i+1, fcu->bezt+i, sizeof(BezTriple));
135                         memcpy(newbezt+i+2, fcu->bezt+i+1, sizeof (BezTriple) *(fcu->totvert-(i+1)));
136                         fcu->totvert++;
137                         
138                         /* reassign pointers... (free old, and add new) */
139                         MEM_freeN(fcu->bezt);
140                         fcu->bezt=newbezt;
141                         
142                         /* Unselect the current key */
143                         BEZ_DESEL(&fcu->bezt[i]);
144                         i++;
145                         
146                         /* Select the copied key */
147                         BEZ_SEL(&fcu->bezt[i]);
148                 }
149         }
150 }
151
152 /* **************************************************** */
153 /* Various Tools */
154
155 /* Basic IPO-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only */
156 void clean_fcurve(FCurve *fcu, float thresh)
157 {
158         BezTriple *old_bezts, *bezt, *beztn;
159         BezTriple *lastb;
160         int totCount, i;
161         
162         /* check if any points  */
163         if ((fcu == NULL) || (fcu->totvert <= 1)) 
164                 return;
165         
166         /* make a copy of the old BezTriples, and clear IPO curve */
167         old_bezts = fcu->bezt;
168         totCount = fcu->totvert;        
169         fcu->bezt = NULL;
170         fcu->totvert = 0;
171         
172         /* now insert first keyframe, as it should be ok */
173         bezt = old_bezts;
174         insert_vert_fcurve(fcu, bezt->vec[1][0], bezt->vec[1][1], 0);
175         
176         /* Loop through BezTriples, comparing them. Skip any that do 
177          * not fit the criteria for "ok" points.
178          */
179         for (i=1; i<totCount; i++) {    
180                 float prev[2], cur[2], next[2];
181                 
182                 /* get BezTriples and their values */
183                 if (i < (totCount - 1)) {
184                         beztn = (old_bezts + (i+1));
185                         next[0]= beztn->vec[1][0]; next[1]= beztn->vec[1][1];
186                 }
187                 else {
188                         beztn = NULL;
189                         next[0] = next[1] = 0.0f;
190                 }
191                 lastb= (fcu->bezt + (fcu->totvert - 1));
192                 bezt= (old_bezts + i);
193                 
194                 /* get references for quicker access */
195                 prev[0] = lastb->vec[1][0]; prev[1] = lastb->vec[1][1];
196                 cur[0] = bezt->vec[1][0]; cur[1] = bezt->vec[1][1];
197                 
198                 /* check if current bezt occurs at same time as last ok */
199                 if (IS_EQT(cur[0], prev[0], thresh)) {
200                         /* If there is a next beztriple, and if occurs at the same time, only insert 
201                          * if there is a considerable distance between the points, and also if the 
202                          * current is further away than the next one is to the previous.
203                          */
204                         if (beztn && (IS_EQT(cur[0], next[0], thresh)) && 
205                                 (IS_EQT(next[1], prev[1], thresh)==0)) 
206                         {
207                                 /* only add if current is further away from previous */
208                                 if (cur[1] > next[1]) {
209                                         if (IS_EQT(cur[1], prev[1], thresh) == 0) {
210                                                 /* add new keyframe */
211                                                 insert_vert_fcurve(fcu, cur[0], cur[1], 0);
212                                         }
213                                 }
214                         }
215                         else {
216                                 /* only add if values are a considerable distance apart */
217                                 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
218                                         /* add new keyframe */
219                                         insert_vert_fcurve(fcu, cur[0], cur[1], 0);
220                                 }
221                         }
222                 }
223                 else {
224                         /* checks required are dependent on whether this is last keyframe or not */
225                         if (beztn) {
226                                 /* does current have same value as previous and next? */
227                                 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
228                                         /* add new keyframe*/
229                                         insert_vert_fcurve(fcu, cur[0], cur[1], 0);
230                                 }
231                                 else if (IS_EQT(cur[1], next[1], thresh) == 0) {
232                                         /* add new keyframe */
233                                         insert_vert_fcurve(fcu, cur[0], cur[1], 0);
234                                 }
235                         }
236                         else {  
237                                 /* add if value doesn't equal that of previous */
238                                 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
239                                         /* add new keyframe */
240                                         insert_vert_fcurve(fcu, cur[0], cur[1], 0);
241                                 }
242                         }
243                 }
244         }
245         
246         /* now free the memory used by the old BezTriples */
247         if (old_bezts)
248                 MEM_freeN(old_bezts);
249 }
250
251 /* ---------------- */
252
253 /* temp struct used for smooth_ipo */
254 typedef struct tSmooth_Bezt {
255         float *h1, *h2, *h3;    /* bezt->vec[0,1,2][1] */
256 } tSmooth_Bezt;
257
258 /* Use a weighted moving-means method to reduce intensity of fluctuations */
259 void smooth_fcurve (FCurve *fcu)
260 {
261         BezTriple *bezt;
262         int i, x, totSel = 0;
263         
264         /* first loop through - count how many verts are selected, and fix up handles 
265          *      this is done for both modes
266          */
267         bezt= fcu->bezt;
268         for (i=0; i < fcu->totvert; i++, bezt++) {                                              
269                 if (BEZSELECTED(bezt)) {                                                        
270                         /* line point's handles up with point's vertical position */
271                         bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1];
272                         if ((bezt->h1==HD_AUTO) || (bezt->h1==HD_VECT)) bezt->h1= HD_ALIGN;
273                         if ((bezt->h2==HD_AUTO) || (bezt->h2==HD_VECT)) bezt->h2= HD_ALIGN;
274                         
275                         /* add value to total */
276                         totSel++;
277                 }
278         }
279         
280                 /* if any points were selected, allocate tSmooth_Bezt points to work on */
281                 if (totSel >= 3) {
282                         tSmooth_Bezt *tarray, *tsb;
283                         
284                         /* allocate memory in one go */
285                         tsb= tarray= MEM_callocN(totSel*sizeof(tSmooth_Bezt), "tSmooth_Bezt Array");
286                         
287                         /* populate tarray with data of selected points */
288                         bezt= fcu->bezt;
289                         for (i=0, x=0; (i < fcu->totvert) && (x < totSel); i++, bezt++) {
290                                 if (BEZSELECTED(bezt)) {
291                                         /* tsb simply needs pointer to vec, and index */
292                                         tsb->h1 = &bezt->vec[0][1];
293                                         tsb->h2 = &bezt->vec[1][1];
294                                         tsb->h3 = &bezt->vec[2][1];
295                                         
296                                         /* advance to the next tsb to populate */
297                                         if (x < totSel- 1) 
298                                                 tsb++;
299                                         else
300                                                 break;
301                                 }
302                         }
303                         
304         /* calculate the new smoothed F-Curve's with weighted averages:
305          *      - this is done with two passes
306          *      - uses 5 points for each operation (which stores in the relevant handles)
307          *      -       previous: w/a ratio = 3:5:2:1:1
308          *      -       next: w/a ratio = 1:1:2:5:3
309          */
310         
311         /* round 1: calculate previous and next */ 
312         tsb= tarray;
313         for (i=0; i < totSel; i++, tsb++) {
314                 /* don't touch end points (otherwise, curves slowly explode) */
315                 if (ELEM(i, 0, (totSel-1)) == 0) {
316                         const tSmooth_Bezt *tP1 = tsb - 1;
317                         const tSmooth_Bezt *tP2 = (i-2 > 0) ? (tsb - 2) : (NULL);
318                         const tSmooth_Bezt *tN1 = tsb + 1;
319                         const tSmooth_Bezt *tN2 = (i+2 < totSel) ? (tsb + 2) : (NULL);
320                         
321                         const float p1 = *tP1->h2;
322                         const float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2);
323                         const float c1 = *tsb->h2;
324                         const float n1 = *tN1->h2;
325                         const float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2);
326                         
327                         /* calculate previous and next */
328                         *tsb->h1= (3*p2 + 5*p1 + 2*c1 + n1 + n2) / 12;
329                         *tsb->h3= (p2 + p1 + 2*c1 + 5*n1 + 3*n2) / 12;
330                 }
331         }
332         
333         /* round 2: calculate new values and reset handles */
334         tsb= tarray;
335         for (i=0; i < totSel; i++, tsb++) {
336                 /* calculate new position by averaging handles */
337                 *tsb->h2 = (*tsb->h1 + *tsb->h3) / 2;
338                 
339                 /* reset handles now */
340                 *tsb->h1 = *tsb->h2;
341                 *tsb->h3 = *tsb->h2;
342         }
343         
344         /* free memory required for tarray */
345         MEM_freeN(tarray);
346 }
347         
348         /* recalculate handles */
349         calchandles_fcurve(fcu);
350 }
351
352 /* **************************************************** */