4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Joshua Leung
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file blender/editors/animation/anim_draw.c
30 * \ingroup edanimation
33 #include "BLO_sys_types.h"
35 #include "DNA_anim_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_scene_types.h"
40 #include "BKE_context.h"
42 #include "BKE_object.h"
44 #include "ED_anim_api.h"
45 #include "ED_keyframes_edit.h"
47 #include "RNA_access.h"
51 #include "UI_interface.h"
52 #include "UI_resources.h"
53 #include "UI_view2d.h"
56 extern void ui_rasterpos_safe(float x, float y, float aspect);
58 /* *************************************************** */
59 /* TIME CODE FORMATTING */
61 /* Generate timecode/frame number string and store in the supplied string
62 * - buffer: must be at least 13 chars long
63 * - power: special setting for View2D grid drawing,
64 * used to specify how detailed we need to be
65 * - timecodes: boolean specifying whether timecodes or
66 * frame numbers get drawn
67 * - cfra: time in frames or seconds, consistent with the values shown by timecodes
69 // TODO: have this in kernel instead under scene?
70 void ANIM_timecode_string_from_frame (char *str, Scene *scene, int power, short timecodes, float cfra)
73 int hours=0, minutes=0, seconds=0, frames=0;
74 float raw_seconds= cfra;
79 /* correction for negative cfraues */
85 /* XXX should we only display a single digit for hours since clips are
86 * VERY UNLIKELY to be more than 1-2 hours max? However, that would
87 * go against conventions...
89 hours= (int)cfra / 3600;
90 cfra= (float)fmod(cfra, 3600);
94 minutes= (int)cfra / 60;
95 cfra= (float)fmod(cfra, 60);
99 * Frames are derived from 'fraction' of second. We need to perform some additional rounding
100 * to cope with 'half' frames, etc., which should be fine in most cases
103 frames= (int)floor( (((double)cfra - (double)seconds) * FPS) + 0.5 );
106 /* seconds (with pixel offset rounding) */
107 seconds= (int)floor(cfra + 0.375f);
110 switch (U.timecode_style) {
111 case USER_TIMECODE_MINIMAL:
113 /* - In general, minutes and seconds should be shown, as most clips will be
114 * within this length. Hours will only be included if relevant.
115 * - Only show frames when zoomed in enough for them to be relevant
116 * (using separator of '+' for frames).
117 * When showing frames, use slightly different display to avoid confusion with mm:ss format
120 /* include "frames" in display */
121 if (hours) sprintf(str, "%s%02d:%02d:%02d+%02d", neg, hours, minutes, seconds, frames);
122 else if (minutes) sprintf(str, "%s%02d:%02d+%02d", neg, minutes, seconds, frames);
123 else sprintf(str, "%s%d+%02d", neg, seconds, frames);
126 /* don't include 'frames' in display */
127 if (hours) sprintf(str, "%s%02d:%02d:%02d", neg, hours, minutes, seconds);
128 else sprintf(str, "%s%02d:%02d", neg, minutes, seconds);
133 case USER_TIMECODE_SMPTE_MSF:
135 /* reduced SMPTE format that always shows minutes, seconds, frames. Hours only shown as needed. */
136 if (hours) sprintf(str, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames);
137 else sprintf(str, "%s%02d:%02d:%02d", neg, minutes, seconds, frames);
141 case USER_TIMECODE_MILLISECONDS:
143 /* reduced SMPTE. Instead of frames, milliseconds are shown */
144 int ms_dp= (power <= 0) ? (1 - power) : 1; /* precision of decimal part */
145 int s_pad= ms_dp+3; /* to get 2 digit whole-number part for seconds display (i.e. 3 is for 2 digits + radix, on top of full length) */
147 if (hours) sprintf(str, "%s%02d:%02d:%0*.*f", neg, hours, minutes, s_pad, ms_dp, cfra);
148 else sprintf(str, "%s%02d:%0*.*f", neg, minutes, s_pad, ms_dp, cfra);
152 case USER_TIMECODE_SECONDS_ONLY:
154 /* only show the original seconds display */
155 /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
156 if (power <= 0) sprintf(str, "%.*f", 1-power, raw_seconds);
157 else sprintf(str, "%d", (int)floor(raw_seconds + 0.375f));
161 case USER_TIMECODE_SMPTE_FULL:
164 /* full SMPTE format */
165 sprintf(str, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames);
171 /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
172 if (power <= 0) sprintf(str, "%.*f", 1-power, cfra);
173 else sprintf(str, "%d", (int)floor(cfra + 0.375f));
177 /* *************************************************** */
178 /* CURRENT FRAME DRAWING */
180 /* Draw current frame number in a little green box beside the current frame indicator */
181 static void draw_cfra_number (Scene *scene, View2D *v2d, float cfra, short time)
183 float xscale, yscale, x, y;
184 char str[32] = " t"; /* t is the character to start replacing from */
187 /* because the frame number text is subject to the same scaling as the contents of the view */
188 UI_view2d_getscale(v2d, &xscale, &yscale);
189 glScalef(1.0f/xscale, 1.0f, 1.0f);
191 /* get timecode string
192 * - padding on str-buf passed so that it doesn't sit on the frame indicator
193 * - power = 0, gives 'standard' behaviour for time
194 * but power = 1 is required for frames (to get integer frames)
197 ANIM_timecode_string_from_frame(&str[4], scene, 0, time, FRA2TIME(cfra));
199 ANIM_timecode_string_from_frame(&str[4], scene, 1, time, cfra);
200 slen= (short)UI_GetStringWidth(str) - 1;
202 /* get starting coordinates for drawing */
206 /* draw green box around/behind text */
207 UI_ThemeColorShade(TH_CFRAME, 0);
208 glRectf(x, y, x+slen, y+15);
210 /* draw current frame number - black text */
211 UI_ThemeColor(TH_TEXT);
212 UI_DrawString(x-5, y+3, str);
214 /* restore view transform */
215 glScalef(xscale, 1.0, 1.0);
218 /* General call for drawing current frame indicator in animation editor */
219 void ANIM_draw_cfra (const bContext *C, View2D *v2d, short flag)
221 Scene *scene= CTX_data_scene(C);
224 /* Draw a light green line to indicate current frame */
225 vec[0]= (float)(scene->r.cfra * scene->r.framelen);
227 UI_ThemeColor(TH_CFRAME);
230 glBegin(GL_LINE_STRIP);
231 vec[1]= v2d->cur.ymin-500.0f; /* XXX arbitrary... want it go to bottom */
234 vec[1]= v2d->cur.ymax;
238 /* Draw dark green line if slow-parenting/time-offset is enabled */
239 if (flag & DRAWCFRA_SHOW_TIMEOFS) {
242 float timeoffset= give_timeoffset(ob);
243 // XXX ob->ipoflag is depreceated!
244 if ((ob->ipoflag & OB_OFFS_OB) && (timeoffset != 0.0f)) {
245 vec[0]-= timeoffset; /* could avoid calling twice */
247 UI_ThemeColorShade(TH_CFRAME, -30);
249 glBegin(GL_LINE_STRIP);
250 /*vec[1]= v2d->cur.ymax;*/ // this is set already. this line is only included
253 vec[1]= v2d->cur.ymin;
262 /* Draw current frame number in a little box */
263 if (flag & DRAWCFRA_SHOW_NUMBOX) {
264 UI_view2d_view_orthoSpecial(CTX_wm_region(C), v2d, 1);
265 draw_cfra_number(scene, v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS));
269 /* *************************************************** */
270 /* PREVIEW RANGE 'CURTAINS' */
271 /* Note: 'Preview Range' tools are defined in anim_ops.c */
273 /* Draw preview range 'curtains' for highlighting where the animation data is */
274 void ANIM_draw_previewrange (const bContext *C, View2D *v2d)
276 Scene *scene= CTX_data_scene(C);
278 /* only draw this if preview range is set */
280 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
282 glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
284 /* only draw two separate 'curtains' if there's no overlap between them */
286 glRectf(v2d->cur.xmin, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax);
287 glRectf((float)PEFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
290 glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
297 /* *************************************************** */
298 /* NLA-MAPPING UTILITIES (required for drawing and also editing keyframes) */
300 /* Obtain the AnimData block providing NLA-mapping for the given channel (if applicable) */
301 // TODO: do not supply return this if the animdata tells us that there is no mapping to perform
302 AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale)
308 /* handling depends on the type of animation-context we've got */
315 /* ------------------- */
317 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "restore", i.e. mapping points back to action-time */
318 static short bezt_nlamapping_restore(KeyframeEditData *ked, BezTriple *bezt)
320 /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
321 AnimData *adt= (AnimData *)ked->data;
322 short only_keys= (short)ked->i1;
324 /* adjust BezTriple handles only if allowed to */
325 if (only_keys == 0) {
326 bezt->vec[0][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_UNMAP);
327 bezt->vec[2][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_UNMAP);
330 bezt->vec[1][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_UNMAP);
335 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "apply", i.e. mapping points to NLA-mapped global time */
336 static short bezt_nlamapping_apply(KeyframeEditData *ked, BezTriple *bezt)
338 /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
339 AnimData *adt= (AnimData*)ked->data;
340 short only_keys= (short)ked->i1;
342 /* adjust BezTriple handles only if allowed to */
343 if (only_keys == 0) {
344 bezt->vec[0][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_MAP);
345 bezt->vec[2][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_MAP);
348 bezt->vec[1][0]= BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_MAP);
354 /* Apply/Unapply NLA mapping to all keyframes in the nominated F-Curve
355 * - restore = whether to map points back to non-mapped time
356 * - only_keys = whether to only adjust the location of the center point of beztriples
358 void ANIM_nla_mapping_apply_fcurve (AnimData *adt, FCurve *fcu, short restore, short only_keys)
360 KeyframeEditData ked= {{NULL}};
361 KeyframeEditFunc map_cb;
364 * - AnimData is stored in 'data'
365 * - only_keys is stored in 'i1'
367 ked.data= (void *)adt;
368 ked.i1= (int)only_keys;
370 /* get editing callback */
372 map_cb= bezt_nlamapping_restore;
374 map_cb= bezt_nlamapping_apply;
376 /* apply to F-Curve */
377 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, map_cb, NULL);
380 /* *************************************************** */
381 /* UNITS CONVERSION MAPPING (required for drawing and editing keyframes) */
383 /* Get unit conversion factor for given ID + F-Curve */
384 float ANIM_unit_mapping_get_factor (Scene *scene, ID *id, FCurve *fcu, short restore)
387 if (id && fcu && fcu->rna_path)
389 PointerRNA ptr, id_ptr;
392 /* get RNA property that F-Curve affects */
393 RNA_id_pointer_create(id, &id_ptr);
394 if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop))
396 /* rotations: radians <-> degrees? */
397 if (RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)) == PROP_UNIT_ROTATION)
399 /* if the radians flag is not set, default to using degrees which need conversions */
400 if ((scene) && (scene->unit.system_rotation == USER_UNIT_ROT_RADIANS) == 0) {
402 return M_PI / 180.0; /* degrees to radians */
404 return 180.0 / M_PI; /* radians to degrees */
408 // TODO: other rotation types here as necessary
412 /* no mapping needs to occur... */
416 /* ----------------------- */
418 /* helper function for ANIM_unit_mapping_apply_fcurve -> mapping callback for unit mapping */
419 static short bezt_unit_mapping_apply (KeyframeEditData *ked, BezTriple *bezt)
421 /* mapping factor is stored in f1, flags are stored in i1 */
422 short only_keys= (ked->i1 & ANIM_UNITCONV_ONLYKEYS);
423 short sel_vs= (ked->i1 & ANIM_UNITCONV_SELVERTS);
426 /* adjust BezTriple handles only if allowed to */
427 if (only_keys == 0) {
428 if ((sel_vs==0) || (bezt->f1 & SELECT))
429 bezt->vec[0][1] *= fac;
430 if ((sel_vs==0) || (bezt->f3 & SELECT))
431 bezt->vec[2][1] *= fac;
434 if ((sel_vs == 0) || (bezt->f2 & SELECT))
435 bezt->vec[1][1] *= fac;
440 /* Apply/Unapply units conversions to keyframes */
441 void ANIM_unit_mapping_apply_fcurve (Scene *scene, ID *id, FCurve *fcu, short flag)
443 KeyframeEditData ked;
444 KeyframeEditFunc sel_cb;
447 /* calculate mapping factor, and abort if nothing to change */
448 fac= ANIM_unit_mapping_get_factor(scene, id, fcu, (flag & ANIM_UNITCONV_RESTORE));
453 * - mapping factor is stored in f1
454 * - flags are stored in 'i1'
456 memset(&ked, 0, sizeof(KeyframeEditData));
461 if (flag & ANIM_UNITCONV_ONLYSEL)
462 sel_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
466 /* apply to F-Curve */
467 ANIM_fcurve_keyframes_loop(&ked, fcu, sel_cb, bezt_unit_mapping_apply, NULL);
469 // FIXME: loop here for samples should be generalised
475 for (i=0, fpt=fcu->fpt; i < fcu->totvert; i++, fpt++) {
476 /* apply unit mapping */
482 /* *************************************************** */