UI: Refactor timecode functions into BLI_timecode
[blender.git] / source / blender / editors / animation / anim_draw.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Joshua Leung
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/animation/anim_draw.c
28  *  \ingroup edanimation
29  */
30
31 #include "BLI_sys_types.h"
32
33 #include "DNA_anim_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_space_types.h"
37 #include "DNA_userdef_types.h"
38
39 #include "BLI_math.h"
40 #include "BLI_timecode.h"
41
42 #include "BKE_context.h"
43 #include "BKE_blender.h"
44 #include "BKE_global.h"
45 #include "BKE_nla.h"
46 #include "BKE_object.h"
47
48 #include "ED_anim_api.h"
49 #include "ED_keyframes_edit.h"
50
51 #include "RNA_access.h"
52
53 #include "BIF_gl.h"
54
55 #include "UI_interface.h"
56 #include "UI_resources.h"
57 #include "UI_view2d.h"
58
59 /* *************************************************** */
60 /* CURRENT FRAME DRAWING */
61
62 /* Draw current frame number in a little green box beside the current frame indicator */
63 static void draw_cfra_number(Scene *scene, View2D *v2d, const float cfra, const bool time)
64 {
65         float xscale, yscale, x, y;
66         char numstr[32] = "    t";  /* t is the character to start replacing from */
67         short slen;
68         
69         /* because the frame number text is subject to the same scaling as the contents of the view */
70         UI_view2d_getscale(v2d, &xscale, &yscale);
71         glScalef(1.0f / xscale, 1.0f, 1.0f);
72         
73         /* get timecode string 
74          *      - padding on str-buf passed so that it doesn't sit on the frame indicator
75          *      - power = 0, gives 'standard' behavior for time
76          *        but power = 1 is required for frames (to get integer frames)
77          */
78         if (time) {
79                 BLI_timecode_string_from_time(&numstr[4], sizeof(numstr) - 4, 0, FRA2TIME(cfra), FPS, U.timecode_style);
80         }
81         else {
82                 BLI_timecode_string_from_time_simple(&numstr[4], sizeof(numstr) - 4, 1, cfra);
83         }
84         slen = (short)UI_GetStringWidth(numstr) - 1;
85         
86         /* get starting coordinates for drawing */
87         x = cfra * xscale;
88         y = 0.9f * U.widget_unit;
89         
90         /* draw green box around/behind text */
91         UI_ThemeColorShade(TH_CFRAME, 0);
92         glRectf(x, y,  x + slen,  y + 0.75f * U.widget_unit);
93         
94         /* draw current frame number - black text */
95         UI_ThemeColor(TH_TEXT);
96         UI_DrawString(x - 0.25f * U.widget_unit, y + 0.15f * U.widget_unit, numstr);
97         
98         /* restore view transform */
99         glScalef(xscale, 1.0, 1.0);
100 }
101
102 /* General call for drawing current frame indicator in animation editor */
103 void ANIM_draw_cfra(const bContext *C, View2D *v2d, short flag)
104 {
105         Scene *scene = CTX_data_scene(C);
106         float vec[2];
107         
108         /* Draw a light green line to indicate current frame */
109         vec[0] = (float)(scene->r.cfra * scene->r.framelen);
110         
111         UI_ThemeColor(TH_CFRAME);
112         if (flag & DRAWCFRA_WIDE)
113                 glLineWidth(3.0);
114         else
115                 glLineWidth(2.0);
116         
117         glBegin(GL_LINE_STRIP);
118         vec[1] = v2d->cur.ymin - 500.0f;    /* XXX arbitrary... want it go to bottom */
119         glVertex2fv(vec);
120                 
121         vec[1] = v2d->cur.ymax;
122         glVertex2fv(vec);
123         glEnd();
124         
125         glLineWidth(1.0);
126         
127         /* Draw current frame number in a little box */
128         if (flag & DRAWCFRA_SHOW_NUMBOX) {
129                 UI_view2d_view_orthoSpecial(CTX_wm_region(C), v2d, 1);
130                 draw_cfra_number(scene, v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS) != 0);
131         }
132 }
133
134 /* *************************************************** */
135 /* PREVIEW RANGE 'CURTAINS' */
136 /* Note: 'Preview Range' tools are defined in anim_ops.c */
137
138 /* Draw preview range 'curtains' for highlighting where the animation data is */
139 void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width)
140 {
141         Scene *scene = CTX_data_scene(C);
142         
143         /* only draw this if preview range is set */
144         if (PRVRANGEON) {
145                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
146                 glEnable(GL_BLEND);
147                 glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
148                 
149                 /* only draw two separate 'curtains' if there's no overlap between them */
150                 if (PSFRA < PEFRA + end_frame_width) {
151                         glRectf(v2d->cur.xmin, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax);
152                         glRectf((float)(PEFRA + end_frame_width), v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
153                 }
154                 else {
155                         glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
156                 }
157                 
158                 glDisable(GL_BLEND);
159         }
160 }
161
162 /* *************************************************** */
163 /* NLA-MAPPING UTILITIES (required for drawing and also editing keyframes)  */
164
165 /* Obtain the AnimData block providing NLA-mapping for the given channel (if applicable) */
166 // TODO: do not supply return this if the animdata tells us that there is no mapping to perform
167 AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale)
168 {
169         /* sanity checks */
170         if (ac == NULL)
171                 return NULL;
172         
173         /* abort if rendering - we may get some race condition issues... */
174         if (G.is_rendering) return NULL;
175         
176         /* handling depends on the type of animation-context we've got */
177         if (ale)
178                 return ale->adt;
179         else
180                 return NULL;
181 }
182
183 /* ------------------- */
184
185 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "restore", i.e. mapping points back to action-time */
186 static short bezt_nlamapping_restore(KeyframeEditData *ked, BezTriple *bezt)
187 {
188         /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
189         AnimData *adt = (AnimData *)ked->data;
190         short only_keys = (short)ked->i1;
191         
192         /* adjust BezTriple handles only if allowed to */
193         if (only_keys == 0) {
194                 bezt->vec[0][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_UNMAP);
195                 bezt->vec[2][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_UNMAP);
196         }
197         
198         bezt->vec[1][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_UNMAP);
199         
200         return 0;
201 }
202
203 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "apply", i.e. mapping points to NLA-mapped global time */
204 static short bezt_nlamapping_apply(KeyframeEditData *ked, BezTriple *bezt)
205 {
206         /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
207         AnimData *adt = (AnimData *)ked->data;
208         short only_keys = (short)ked->i1;
209         
210         /* adjust BezTriple handles only if allowed to */
211         if (only_keys == 0) {
212                 bezt->vec[0][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_MAP);
213                 bezt->vec[2][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_MAP);
214         }
215         
216         bezt->vec[1][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_MAP);
217         
218         return 0;
219 }
220
221
222 /* Apply/Unapply NLA mapping to all keyframes in the nominated F-Curve 
223  *      - restore = whether to map points back to non-mapped time 
224  *  - only_keys = whether to only adjust the location of the center point of beztriples
225  */
226 void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, short restore, short only_keys)
227 {
228         KeyframeEditData ked = {{NULL}};
229         KeyframeEditFunc map_cb;
230         
231         /* init edit data 
232          *      - AnimData is stored in 'data'
233          *      - only_keys is stored in 'i1'
234          */
235         ked.data = (void *)adt;
236         ked.i1 = (int)only_keys;
237         
238         /* get editing callback */
239         if (restore)
240                 map_cb = bezt_nlamapping_restore;
241         else
242                 map_cb = bezt_nlamapping_apply;
243         
244         /* apply to F-Curve */
245         ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, map_cb, NULL);
246 }
247
248 /* *************************************************** */
249 /* UNITS CONVERSION MAPPING (required for drawing and editing keyframes) */
250
251 /* Get flags used for normalization in ANIM_unit_mapping_get_factor. */
252 short ANIM_get_normalization_flags(bAnimContext *ac)
253 {
254         if (ac->sl->spacetype == SPACE_IPO) {
255                 SpaceIpo *sipo = (SpaceIpo *) ac->sl;
256                 bool use_normalization = (sipo->flag & SIPO_NORMALIZE) != 0;
257                 bool freeze_normalization = (sipo->flag & SIPO_NORMALIZE_FREEZE) != 0;
258                 return use_normalization
259                     ? (ANIM_UNITCONV_NORMALIZE |  (freeze_normalization ? ANIM_UNITCONV_NORMALIZE_FREEZE : 0))
260                     : 0;
261         }
262
263         return 0;
264 }
265
266 static float normalzation_factor_get(FCurve *fcu, short flag)
267 {
268         float factor = 1.0f;
269
270         if (flag & ANIM_UNITCONV_RESTORE) {
271                 return 1.0f / fcu->prev_norm_factor;
272         }
273
274         if (flag & ANIM_UNITCONV_NORMALIZE_FREEZE) {
275                 return fcu->prev_norm_factor;
276         }
277
278         if (G.moving & G_TRANSFORM_FCURVES) {
279                 return fcu->prev_norm_factor;
280         }
281
282         fcu->prev_norm_factor = 1.0f;
283         if (fcu->bezt) {
284                 BezTriple *bezt;
285                 int i;
286                 float max_coord = -FLT_MAX;
287
288                 if (fcu->totvert < 1) {
289                         return 1.0f;
290                 }
291
292                 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
293                         max_coord = max_ff(max_coord, fabsf(bezt->vec[0][1]));
294                         max_coord = max_ff(max_coord, fabsf(bezt->vec[1][1]));
295                         max_coord = max_ff(max_coord, fabsf(bezt->vec[2][1]));
296                 }
297
298                 if (max_coord > FLT_EPSILON) {
299                         factor = 1.0f / max_coord;
300                 }
301         }
302         fcu->prev_norm_factor = factor;
303         return factor;
304 }
305
306 /* Get unit conversion factor for given ID + F-Curve */
307 float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag)
308 {
309         if (flag & ANIM_UNITCONV_NORMALIZE) {
310                 return normalzation_factor_get(fcu, flag);
311         }
312
313         /* sanity checks */
314         if (id && fcu && fcu->rna_path) {
315                 PointerRNA ptr, id_ptr;
316                 PropertyRNA *prop;
317                 
318                 /* get RNA property that F-Curve affects */
319                 RNA_id_pointer_create(id, &id_ptr);
320                 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
321                         /* rotations: radians <-> degrees? */
322                         if (RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)) == PROP_UNIT_ROTATION) {
323                                 /* if the radians flag is not set, default to using degrees which need conversions */
324                                 if ((scene) && (scene->unit.system_rotation == USER_UNIT_ROT_RADIANS) == 0) {
325                                         if (flag & ANIM_UNITCONV_RESTORE)
326                                                 return DEG2RADF(1.0f);  /* degrees to radians */
327                                         else
328                                                 return RAD2DEGF(1.0f);  /* radians to degrees */
329                                 }
330                         }
331                         
332                         /* TODO: other rotation types here as necessary */
333                 }
334         }
335
336         /* no mapping needs to occur... */
337         return 1.0f;
338 }
339
340 /* *************************************************** */