2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Joshua Leung
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/animation/anim_draw.c
28 * \ingroup edanimation
31 #include "BLO_sys_types.h"
33 #include "DNA_anim_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_userdef_types.h"
40 #include "BKE_context.h"
41 #include "BKE_blender.h"
42 #include "BKE_global.h"
44 #include "BKE_object.h"
46 #include "ED_anim_api.h"
47 #include "ED_keyframes_edit.h"
49 #include "RNA_access.h"
53 #include "UI_interface.h"
54 #include "UI_resources.h"
55 #include "UI_view2d.h"
57 /* *************************************************** */
58 /* TIME CODE FORMATTING */
60 /* Generate timecode/frame number string and store in the supplied string
61 * - buffer: must be at least 13 chars long
62 * - power: special setting for View2D grid drawing,
63 * used to specify how detailed we need to be
64 * - timecodes: boolean specifying whether timecodes or
65 * frame numbers get drawn
66 * - cfra: time in frames or seconds, consistent with the values shown by timecodes
68 // TODO: have this in kernel instead under scene?
69 void ANIM_timecode_string_from_frame(char *str, Scene *scene, int power, short timecodes, float cfra)
72 int hours = 0, minutes = 0, seconds = 0, frames = 0;
73 float raw_seconds = cfra;
78 /* correction for negative cfraues */
84 /* XXX should we only display a single digit for hours since clips are
85 * VERY UNLIKELY to be more than 1-2 hours max? However, that would
86 * go against conventions...
88 hours = (int)cfra / 3600;
89 cfra = (float)fmod(cfra, 3600);
93 minutes = (int)cfra / 60;
94 cfra = (float)fmod(cfra, 60);
98 * Frames are derived from 'fraction' of second. We need to perform some additional rounding
99 * to cope with 'half' frames, etc., which should be fine in most cases
102 frames = (int)floor( (((double)cfra - (double)seconds) * FPS) + 0.5);
105 /* seconds (with pixel offset rounding) */
106 seconds = (int)floor(cfra + GLA_PIXEL_OFS);
109 switch (U.timecode_style) {
110 case USER_TIMECODE_MINIMAL:
112 /* - In general, minutes and seconds should be shown, as most clips will be
113 * within this length. Hours will only be included if relevant.
114 * - Only show frames when zoomed in enough for them to be relevant
115 * (using separator of '+' for frames).
116 * When showing frames, use slightly different display to avoid confusion with mm:ss format
119 /* include "frames" in display */
120 if (hours) sprintf(str, "%s%02d:%02d:%02d+%02d", neg, hours, minutes, seconds, frames);
121 else if (minutes) sprintf(str, "%s%02d:%02d+%02d", neg, minutes, seconds, frames);
122 else sprintf(str, "%s%d+%02d", neg, seconds, frames);
125 /* don't include 'frames' in display */
126 if (hours) sprintf(str, "%s%02d:%02d:%02d", neg, hours, minutes, seconds);
127 else sprintf(str, "%s%02d:%02d", neg, minutes, seconds);
132 case USER_TIMECODE_SMPTE_MSF:
134 /* reduced SMPTE format that always shows minutes, seconds, frames. Hours only shown as needed. */
135 if (hours) sprintf(str, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames);
136 else sprintf(str, "%s%02d:%02d:%02d", neg, minutes, seconds, frames);
140 case USER_TIMECODE_MILLISECONDS:
142 /* reduced SMPTE. Instead of frames, milliseconds are shown */
143 int ms_dp = (power <= 0) ? (1 - power) : 1; /* precision of decimal part */
144 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) */
146 if (hours) sprintf(str, "%s%02d:%02d:%0*.*f", neg, hours, minutes, s_pad, ms_dp, cfra);
147 else sprintf(str, "%s%02d:%0*.*f", neg, minutes, s_pad, ms_dp, cfra);
151 case USER_TIMECODE_SECONDS_ONLY:
153 /* only show the original seconds display */
154 /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
155 if (power <= 0) sprintf(str, "%.*f", 1 - power, raw_seconds);
156 else sprintf(str, "%d", (int)floor(raw_seconds + GLA_PIXEL_OFS));
160 case USER_TIMECODE_SMPTE_FULL:
163 /* full SMPTE format */
164 sprintf(str, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames);
170 /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
171 if (power <= 0) sprintf(str, "%.*f", 1 - power, cfra);
172 else sprintf(str, "%d", (int)floor(cfra + GLA_PIXEL_OFS));
176 /* *************************************************** */
177 /* CURRENT FRAME DRAWING */
179 /* Draw current frame number in a little green box beside the current frame indicator */
180 static void draw_cfra_number(Scene *scene, View2D *v2d, float cfra, short time)
182 float xscale, yscale, x, y;
183 char numstr[32] = " t"; /* t is the character to start replacing from */
186 /* because the frame number text is subject to the same scaling as the contents of the view */
187 UI_view2d_getscale(v2d, &xscale, &yscale);
188 glScalef(1.0f / xscale, 1.0f, 1.0f);
190 /* get timecode string
191 * - padding on str-buf passed so that it doesn't sit on the frame indicator
192 * - power = 0, gives 'standard' behavior for time
193 * but power = 1 is required for frames (to get integer frames)
196 ANIM_timecode_string_from_frame(&numstr[4], scene, 0, time, FRA2TIME(cfra));
198 ANIM_timecode_string_from_frame(&numstr[4], scene, 1, time, cfra);
199 slen = (short)UI_GetStringWidth(numstr) - 1;
201 /* get starting coordinates for drawing */
203 y = 0.9f * U.widget_unit;
205 /* draw green box around/behind text */
206 UI_ThemeColorShade(TH_CFRAME, 0);
207 glRectf(x, y, x + slen, y + 0.75f * U.widget_unit);
209 /* draw current frame number - black text */
210 UI_ThemeColor(TH_TEXT);
211 UI_DrawString(x - 0.25f * U.widget_unit, y + 0.15f * U.widget_unit, numstr);
213 /* restore view transform */
214 glScalef(xscale, 1.0, 1.0);
217 /* General call for drawing current frame indicator in animation editor */
218 void ANIM_draw_cfra(const bContext *C, View2D *v2d, short flag)
220 Scene *scene = CTX_data_scene(C);
223 /* Draw a light green line to indicate current frame */
224 vec[0] = (float)(scene->r.cfra * scene->r.framelen);
226 UI_ThemeColor(TH_CFRAME);
227 if (flag & DRAWCFRA_WIDE)
232 glBegin(GL_LINE_STRIP);
233 vec[1] = v2d->cur.ymin - 500.0f; /* XXX arbitrary... want it go to bottom */
236 vec[1] = v2d->cur.ymax;
242 /* Draw current frame number in a little box */
243 if (flag & DRAWCFRA_SHOW_NUMBOX) {
244 UI_view2d_view_orthoSpecial(CTX_wm_region(C), v2d, 1);
245 draw_cfra_number(scene, v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS));
249 /* *************************************************** */
250 /* PREVIEW RANGE 'CURTAINS' */
251 /* Note: 'Preview Range' tools are defined in anim_ops.c */
253 /* Draw preview range 'curtains' for highlighting where the animation data is */
254 void ANIM_draw_previewrange(const bContext *C, View2D *v2d)
256 Scene *scene = CTX_data_scene(C);
258 /* only draw this if preview range is set */
260 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
262 glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
264 /* only draw two separate 'curtains' if there's no overlap between them */
266 glRectf(v2d->cur.xmin, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax);
267 glRectf((float)PEFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
270 glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
277 /* *************************************************** */
278 /* NLA-MAPPING UTILITIES (required for drawing and also editing keyframes) */
280 /* Obtain the AnimData block providing NLA-mapping for the given channel (if applicable) */
281 // TODO: do not supply return this if the animdata tells us that there is no mapping to perform
282 AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale)
288 /* abort if rendering - we may get some race condition issues... */
289 if (G.is_rendering) return NULL;
291 /* handling depends on the type of animation-context we've got */
298 /* ------------------- */
300 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "restore", i.e. mapping points back to action-time */
301 static short bezt_nlamapping_restore(KeyframeEditData *ked, BezTriple *bezt)
303 /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
304 AnimData *adt = (AnimData *)ked->data;
305 short only_keys = (short)ked->i1;
307 /* adjust BezTriple handles only if allowed to */
308 if (only_keys == 0) {
309 bezt->vec[0][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_UNMAP);
310 bezt->vec[2][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_UNMAP);
313 bezt->vec[1][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_UNMAP);
318 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "apply", i.e. mapping points to NLA-mapped global time */
319 static short bezt_nlamapping_apply(KeyframeEditData *ked, BezTriple *bezt)
321 /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
322 AnimData *adt = (AnimData *)ked->data;
323 short only_keys = (short)ked->i1;
325 /* adjust BezTriple handles only if allowed to */
326 if (only_keys == 0) {
327 bezt->vec[0][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_MAP);
328 bezt->vec[2][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_MAP);
331 bezt->vec[1][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_MAP);
337 /* Apply/Unapply NLA mapping to all keyframes in the nominated F-Curve
338 * - restore = whether to map points back to non-mapped time
339 * - only_keys = whether to only adjust the location of the center point of beztriples
341 void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, short restore, short only_keys)
343 KeyframeEditData ked = {{NULL}};
344 KeyframeEditFunc map_cb;
347 * - AnimData is stored in 'data'
348 * - only_keys is stored in 'i1'
350 ked.data = (void *)adt;
351 ked.i1 = (int)only_keys;
353 /* get editing callback */
355 map_cb = bezt_nlamapping_restore;
357 map_cb = bezt_nlamapping_apply;
359 /* apply to F-Curve */
360 ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, map_cb, NULL);
363 /* *************************************************** */
364 /* UNITS CONVERSION MAPPING (required for drawing and editing keyframes) */
366 /* Get unit conversion factor for given ID + F-Curve */
367 float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short restore)
370 if (id && fcu && fcu->rna_path) {
371 PointerRNA ptr, id_ptr;
374 /* get RNA property that F-Curve affects */
375 RNA_id_pointer_create(id, &id_ptr);
376 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
377 /* rotations: radians <-> degrees? */
378 if (RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)) == PROP_UNIT_ROTATION) {
379 /* if the radians flag is not set, default to using degrees which need conversions */
380 if ((scene) && (scene->unit.system_rotation == USER_UNIT_ROT_RADIANS) == 0) {
382 return DEG2RADF(1.0f); /* degrees to radians */
384 return RAD2DEGF(1.0f); /* radians to degrees */
388 /* TODO: other rotation types here as necessary */
392 /* no mapping needs to occur... */
396 /* ----------------------- */
398 /* helper function for ANIM_unit_mapping_apply_fcurve -> mapping callback for unit mapping */
399 static short bezt_unit_mapping_apply(KeyframeEditData *ked, BezTriple *bezt)
401 /* mapping factor is stored in f1, flags are stored in i1 */
402 const bool only_keys = (ked->i1 & ANIM_UNITCONV_ONLYKEYS);
403 const bool sel_vs = (ked->i1 & ANIM_UNITCONV_SELVERTS);
404 const bool skip_knot = (ked->i1 & ANIM_UNITCONV_SKIPKNOTS);
407 /* adjust BezTriple handles only if allowed to */
408 if (only_keys == false) {
409 if ((sel_vs == false) || (bezt->f1 & SELECT))
410 bezt->vec[0][1] *= fac;
411 if ((sel_vs == false) || (bezt->f3 & SELECT))
412 bezt->vec[2][1] *= fac;
415 if (skip_knot == false) {
416 if ((sel_vs == false) || (bezt->f2 & SELECT))
417 bezt->vec[1][1] *= fac;
423 /* Apply/Unapply units conversions to keyframes */
424 void ANIM_unit_mapping_apply_fcurve(Scene *scene, ID *id, FCurve *fcu, short flag)
426 KeyframeEditData ked;
427 KeyframeEditFunc sel_cb;
430 /* abort if rendering - we may get some race condition issues... */
431 if (G.is_rendering) return;
433 /* calculate mapping factor, and abort if nothing to change */
434 fac = ANIM_unit_mapping_get_factor(scene, id, fcu, (flag & ANIM_UNITCONV_RESTORE));
439 * - mapping factor is stored in f1
440 * - flags are stored in 'i1'
442 memset(&ked, 0, sizeof(KeyframeEditData));
447 if (flag & ANIM_UNITCONV_ONLYSEL)
448 sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
452 /* apply to F-Curve */
453 ANIM_fcurve_keyframes_loop(&ked, fcu, sel_cb, bezt_unit_mapping_apply, NULL);
455 // FIXME: loop here for samples should be generalised
461 for (i = 0, fpt = fcu->fpt; i < fcu->totvert; i++, fpt++) {
462 /* apply unit mapping */
468 /* *************************************************** */