F-Curve Modifiers: Basic GUI for Generator Modifier working
[blender.git] / source / blender / editors / space_graph / graph_buttons.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) 2009 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation, Joshua Leung
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_anim_types.h"
35 #include "DNA_action_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_userdef_types.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "BLI_arithb.h"
45 #include "BLI_blenlib.h"
46 #include "BLI_editVert.h"
47 #include "BLI_rand.h"
48
49 #include "BKE_animsys.h"
50 #include "BKE_action.h"
51 #include "BKE_context.h"
52 #include "BKE_curve.h"
53 #include "BKE_customdata.h"
54 #include "BKE_depsgraph.h"
55 #include "BKE_fcurve.h"
56 #include "BKE_object.h"
57 #include "BKE_global.h"
58 #include "BKE_scene.h"
59 #include "BKE_screen.h"
60 #include "BKE_utildefines.h"
61
62 #include "BIF_gl.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "RNA_access.h"
68 #include "RNA_define.h"
69
70 #include "ED_anim_api.h"
71 #include "ED_keyframing.h"
72 #include "ED_screen.h"
73 #include "ED_types.h"
74 #include "ED_util.h"
75
76 #include "UI_interface.h"
77 #include "UI_resources.h"
78 #include "UI_view2d.h"
79
80 #include "graph_intern.h"       // own include
81
82
83 /* ******************* graph editor space & buttons ************** */
84
85 #define B_NOP           1
86 #define B_REDR          2
87
88 /* -------------- */
89
90 static void do_graph_region_buttons(bContext *C, void *arg, int event)
91 {
92         //Scene *scene= CTX_data_scene(C);
93         
94         switch(event) {
95
96         }
97         
98         /* default for now */
99         //WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, ob);
100 }
101
102 static void graph_panel_properties(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale)     
103 {
104         FCurve *fcu= (FCurve *)ale->data;
105         uiBlock *block;
106         char name[128];
107
108         block= uiBeginBlock(C, ar, "graph_panel_properties", UI_EMBOSS, UI_HELV);
109         if (uiNewPanel(C, ar, block, "Properties", "Graph", 340, 30, 318, 254)==0) return;
110         uiBlockSetHandleFunc(block, do_graph_region_buttons, NULL);
111
112         /* to force height */
113         uiNewPanelHeight(block, 204);
114
115         /* Info - Active F-Curve */
116         uiDefBut(block, LABEL, 1, "Active F-Curve:",                                    10, 200, 150, 19, NULL, 0.0, 0.0, 0, 0, "");
117         
118         if (ale->id) { 
119                 // icon of active blocktype - is this really necessary?
120                 int icon= geticon_anim_blocktype(GS(ale->id->name));
121                 
122                 // xxx type of icon-but is currently "LABEL", as that one is plain...
123                 uiDefIconBut(block, LABEL, 1, icon, 10, 180, 20, 19, NULL, 0, 0, 0, 0, "ID-type that F-Curve belongs to");
124         }
125         
126         getname_anim_fcurve(name, ale->id, fcu);
127         uiDefBut(block, LABEL, 1, name, 30, 180, 300, 19, NULL, 0.0, 0.0, 0, 0, "Name of Active F-Curve");
128         
129         /* TODO: the following settings could be added here
130          *      - F-Curve coloring mode - mode selector + color selector
131          *      - Access details (ID-block + RNA-Path + Array Index)
132          *      - ...
133          */
134 }
135
136 /* -------------- */
137
138 #define B_IPO_DEPCHANGE         10
139
140 static void do_graph_region_driver_buttons(bContext *C, void *arg, int event)
141 {
142         Scene *scene= CTX_data_scene(C);
143         
144         switch(event) {
145                 case B_IPO_DEPCHANGE:
146                 {
147                         /* rebuild depsgraph for the new deps */
148                         DAG_scene_sort(scene);
149                         
150                         /* TODO: which one? we need some way of sending these updates since curves from non-active ob could be being edited */
151                         //DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
152                         //DAG_object_flush_update(scene, ob, OB_RECALC_OB);
153                 }
154                         break;
155         }
156         
157         /* default for now */
158         //WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, ob);
159 }
160
161 static void graph_panel_drivers(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale)        
162 {
163         FCurve *fcu= (FCurve *)ale->data;
164         ChannelDriver *driver= fcu->driver;
165         uiBlock *block;
166         uiBut *but;
167         int len;
168
169         block= uiBeginBlock(C, ar, "graph_panel_drivers", UI_EMBOSS, UI_HELV);
170         if (uiNewPanel(C, ar, block, "Drivers", "Graph", 340, 30, 318, 254)==0) return;
171         uiBlockSetHandleFunc(block, do_graph_region_driver_buttons, NULL);
172
173         /* to force height */
174         uiNewPanelHeight(block, 204);
175         
176         /* type */
177         uiDefBut(block, LABEL, 1, "Type:",                                      10, 200, 120, 20, NULL, 0.0, 0.0, 0, 0, "");
178         uiDefButI(block, MENU, B_IPO_DEPCHANGE,
179                                         "Driver Type%t|Transform Channel%x0|Scripted Expression%x1|Rotational Difference%x2", 
180                                         130,200,180,20, &driver->type, 0, 0, 0, 0, "Driver type");
181                                         
182         /* buttons to draw depends on type of driver */
183         if (driver->type == DRIVER_TYPE_PYTHON) { /* PyDriver */
184                 uiDefBut(block, TEX, B_REDR, "Expr: ", 10,160,300,20, driver->expression, 0, 255, 0, 0, "One-liner Python Expression to use as Scripted Expression");
185                 
186                 if (driver->flag & DRIVER_FLAG_INVALID) {
187                         uiDefIconBut(block, LABEL, 1, ICON_ERROR, 10, 140, 20, 19, NULL, 0, 0, 0, 0, "");
188                         uiDefBut(block, LABEL, 0, "Error: invalid Python expression",
189                                         30,140,230,19, NULL, 0, 0, 0, 0, "");
190                 }
191         }
192         else { /* Channel or RotDiff - RotDiff just has extra settings */
193                 /* Driver Object */
194                 but= uiDefBut(block, TEX, B_IPO_DEPCHANGE, "OB: ",      10,160,150,20, driver->id->name+2, 0.0, 21.0, 0, 0, "Object that controls this Driver.");
195                 uiButSetFunc(but, test_idbutton_cb, driver->id->name, NULL);
196                 
197                 // XXX should we hide these technical details?
198                 if (driver->id) {
199                         /* Array Index */
200                         // XXX ideally this is grouped with the path, but that can get quite long...
201                         uiDefButI(block, NUM, B_IPO_DEPCHANGE, "Index: ", 170,160,140,20, &driver->array_index, 0, INT_MAX, 0, 0, "Index to the specific property used as Driver if applicable.");
202                         
203                         /* RNA-Path - allocate if non-existant */
204                         if (driver->rna_path == NULL) {
205                                 driver->rna_path= MEM_callocN(256, "Driver RNA-Path");
206                                 len= 255;
207                         }
208                         else
209                                 len= strlen(driver->rna_path);
210                         uiDefBut(block, TEX, B_IPO_DEPCHANGE, "Path: ", 10,130,300,20, driver->rna_path, 0, len, 0, 0, "RNA Path (from Driver Object) to property used as Driver.");
211                 }
212                 
213                 /* for rotational difference, show second target... */
214                 if (driver->type == DRIVER_TYPE_ROTDIFF) {
215                         // TODO...
216                 }
217         }
218 }
219
220 /* -------------- */
221
222 #define B_FMODIFIER_REDRAW              20
223
224 static void do_graph_region_modifier_buttons(bContext *C, void *arg, int event)
225 {
226         switch (event) {
227                 case B_REDR:
228                 case B_FMODIFIER_REDRAW: // XXX this should send depsgraph updates too
229                         ED_area_tag_redraw(CTX_wm_area(C));
230                         return; /* no notifier! */
231         }
232 }
233
234 /* macro for use here to draw background box and set height */
235 // XXX for now, roundbox has it's callback func set to NULL to not intercept events
236 #define DRAW_BACKDROP(height) \
237         { \
238                 if (active) uiBlockSetCol(block, TH_BUT_ACTION); \
239                         but= uiDefBut(block, ROUNDBOX, B_REDR, "", 10-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12.0, (float)rb_col, ""); \
240                         uiButSetFunc(but, NULL, NULL, NULL); \
241                 if (active) uiBlockSetCol(block, TH_AUTO); \
242         }
243
244 /* callback to verify modifier data */
245 static void validate_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v)
246 {
247         FModifier *fcm= (FModifier *)fcm_v;
248         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
249         
250         /* call the verify callback on the modifier if applicable */
251         if (fmi && fmi->verify_data)
252                 fmi->verify_data(fcm);
253 }
254         
255 /* draw settings for generator modifier */
256 static void _draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col)
257 {
258         FMod_Generator *data= (FMod_Generator *)fcm->data;
259         char gen_mode[]="Generator Type%t|Expanded Polynomial%x0|Factorised Polynomial%x1|Built-In Function%x2|Expression%x3";
260         //char fn_type[]="Built-In Function%t|Sin%x0|Cos%x1|Tan%x2|Square Root%x3|Natural Log%x4";
261         int cy= *yco - 30;
262         uiBut *but;
263         
264         /* set the height */
265         (*height) = 70;
266         switch (data->mode) {
267                 case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */
268                         (*height) += 20*(data->poly_order+1) + 35;
269                         break;
270                 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */
271                         (*height) += 25 * data->poly_order;
272                         break;
273                 case FCM_GENERATOR_FUNCTION: /* builtin function */
274                         (*height) += 50; // xxx
275                         break;
276                 case FCM_GENERATOR_EXPRESSION: /* py-expression */
277                         // xxx nothing to draw 
278                         break;
279         }
280         
281         /* basic settings (backdrop + mode selector + some padding) */
282         //DRAW_BACKDROP((*height)); // XXX buggy...
283         but= uiDefButS(block, MENU, /*B_FMODIFIER_REDRAW*/B_REDR, gen_mode, 10,cy,width-30,19, &data->mode, 0, 0, 0, 0, "Selects type of generator algorithm.");
284         uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
285         cy -= 35;
286         
287         /* now add settings for individual modifiers */
288         switch (data->mode) {
289                 case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */
290                 {
291                         float *cp = NULL;
292                         char xval[32];
293                         unsigned int i;
294                         
295                         /* draw polynomial order selector */
296                                 // XXX this needs validation!
297                         but= uiDefButS(block, NUM, B_FMODIFIER_REDRAW, "Poly Order: ", 10,cy,width-30,19, &data->poly_order, 1, 100, 0, 0, "'Order' of the Polynomial - for a polynomial with n terms, 'order' is n-1");
298                         uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm);
299                         cy -= 35;
300                         
301                         /* draw controls for each coefficient and a + sign at end of row */
302                         cp= data->coefficients;
303                         for (i=0; (i < data->arraysize) && (cp); i++, cp++) {
304                                 /* coefficient */
305                                 uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "", 50, cy, 150, 20, cp, -FLT_MAX, FLT_MAX, 10, 3, "Coefficient for polynomial");
306                                 
307                                 /* 'x' param (and '+' if necessary) */
308                                 if (i == 0)
309                                         strcpy(xval, "");
310                                 else if (i == 1)
311                                         strcpy(xval, "x");
312                                 else
313                                         sprintf(xval, "x^%d", i);
314                                 uiDefBut(block, LABEL, 1, xval, 200, cy, 50, 20, NULL, 0.0, 0.0, 0, 0, "Power of x");
315                                 
316                                 if ( (i != (data->arraysize - 1)) || ((i==0) && data->arraysize==2) )
317                                         uiDefBut(block, LABEL, 1, "+", 300, cy, 30, 20, NULL, 0.0, 0.0, 0, 0, "Power of x");
318                                 
319                                 cy -= 20;
320                         }
321                 }
322                         break;
323                 
324                 case FCM_GENERATOR_EXPRESSION: /* py-expression */
325                         // TODO...
326                         break;
327         }
328 }
329
330
331 static void graph_panel_modifier_draw(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco)
332 {
333         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
334         uiBut *but;
335         short active= (fcm->flag & FMODIFIER_FLAG_ACTIVE);
336         short width= 314;
337         short height = 0; 
338         int rb_col;
339         
340         /* draw header */
341         {
342                 uiBlockSetEmboss(block, UI_EMBOSSN);
343                 
344                 /* rounded header */
345 #if 0 // XXX buggy...
346                 if (active) uiBlockSetCol(block, TH_BUT_ACTION);
347                         rb_col= (active)?-20:20;
348                         but= uiDefBut(block, ROUNDBOX, B_REDR, "", 10-8, *yco-2, width, 24, NULL, 5.0, 0.0, 15.0, (float)(rb_col-20), ""); 
349                 if (active) uiBlockSetCol(block, TH_AUTO);
350 #endif // XXX buggy
351                 
352                 /* expand */
353                 uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_EXPANDED, B_REDR, ICON_TRIA_RIGHT,      10-7, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is expanded");
354                 
355                 /* name */
356                 if (fmi)
357                         uiDefBut(block, LABEL, 1, fmi->name,    10+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type");
358                 else
359                         uiDefBut(block, LABEL, 1, "<Unknown Modifier>", 10+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type");
360                 
361                 /* delete button */
362                 but= uiDefIconBut(block, BUT, B_REDR, ICON_X, 10+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete layer");
363                 //uiButSetFunc(but, gp_ui_dellayer_cb, gpd, NULL);
364                 
365                 uiBlockSetEmboss(block, UI_EMBOSS);
366         }
367         
368         /* when modifier is expanded, draw settings */
369         if (fcm->flag & FMODIFIER_FLAG_EXPANDED) {
370                 /* draw settings for individual modifiers */
371                 switch (fcm->type) {
372                         case FMODIFIER_TYPE_GENERATOR: /* Generator */
373                                 _draw_modifier__generator(block, fcu, fcm, yco, &height, width, active, rb_col);
374                                 break;
375                         
376                         default: /* unknown type */
377                                 height= 96;
378                                 //DRAW_BACKDROP(height); // XXX buggy...
379                                 break;
380                 }
381         }
382         
383         /* adjust height for new to start */
384         (*yco) -= (height + 27); 
385 }
386
387 static void graph_panel_modifiers(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale)      
388 {
389         FCurve *fcu= (FCurve *)ale->data;
390         FModifier *fcm;
391         uiBlock *block;
392         int yco= 190;
393         
394         block= uiBeginBlock(C, ar, "graph_panel_modifiers", UI_EMBOSS, UI_HELV);
395         if (uiNewPanel(C, ar, block, "Modifiers", "Graph", 340, 30, 318, 254)==0) return;
396         uiBlockSetHandleFunc(block, do_graph_region_modifier_buttons, NULL);
397         
398         uiNewPanelHeight(block, 204);
399         
400         /* 'add modifier' button at top of panel */
401         // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator
402         uiDefButO(block, BUT, "GRAPHEDIT_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, "Add Modifier", 10, 225, 150, 20, "Adds a new F-Curve Modifier for the active F-Curve");
403         
404         /* draw each modifier */
405         for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next)
406                 graph_panel_modifier_draw(block, fcu, fcm, &yco);
407         
408         /* since these buttons can have variable height */
409         if (yco < 0)
410                 uiNewPanelHeight(block, (204 - yco));
411         else
412                 uiNewPanelHeight(block, 204);
413 }
414
415 /* -------------- */
416
417 /* Find 'active' F-Curve. It must be editable, since that's the purpose of these buttons (subject to change).  
418  * We return the 'wrapper' since it contains valuable context info (about hierarchy), which will need to be freed 
419  * when the caller is done with it.
420  */
421 // TODO: move this to anim api with another name?
422 bAnimListElem *get_active_fcurve_channel (bAnimContext *ac)
423 {
424         ListBase anim_data = {NULL, NULL};
425         int filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE | ANIMFILTER_CURVESONLY);
426         int items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
427         
428         /* We take the first F-Curve only, since some other ones may have had 'active' flag set
429          * if they were from linked data.
430          */
431         if (items) {
432                 bAnimListElem *ale= (bAnimListElem *)anim_data.first;
433                 
434                 /* remove first item from list, then free the rest of the list and return the stored one */
435                 BLI_remlink(&anim_data, ale);
436                 BLI_freelistN(&anim_data);
437                 
438                 return ale;
439         }
440         
441         /* no active F-Curve */
442         return NULL;
443 }
444
445 void graph_region_buttons(const bContext *C, ARegion *ar)
446 {
447         SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C);
448         bAnimContext ac;
449         bAnimListElem *ale= NULL;
450         
451         /* for now, only draw if we could init the anim-context info (necessary for all animation-related tools) 
452          * to work correctly is able to be correctly retrieved. There's no point showing empty panels?
453          */
454         if (ANIM_animdata_get_context(C, &ac) == 0) 
455                 return;
456         
457         
458         /* try to find 'active' F-Curve */
459         ale= get_active_fcurve_channel(&ac);
460         if (ale == NULL) 
461                 return; 
462                 
463         /* for now, the properties panel displays info about the selected channels */
464         graph_panel_properties(C, ar, 0, ale);
465         
466         /* driver settings for active F-Curve (only for 'Drivers' mode) */
467         if (sipo->mode == SIPO_MODE_DRIVERS)
468                 graph_panel_drivers(C, ar, 0, ale);
469         
470         /* modifiers */
471         graph_panel_modifiers(C, ar, 0, ale);
472         
473
474         uiDrawPanels(C, 1);             /* 1 = align */
475         uiMatchPanelsView2d(ar); /* sets v2d->totrct */
476         
477         /* free temp data */
478         MEM_freeN(ale);
479 }
480
481
482 static int graph_properties(bContext *C, wmOperator *op)
483 {
484         ScrArea *sa= CTX_wm_area(C);
485         ARegion *ar= graph_has_buttons_region(sa);
486         
487         if(ar) {
488                 ar->flag ^= RGN_FLAG_HIDDEN;
489                 ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */
490                 
491                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
492                 ED_area_tag_redraw(sa);
493         }
494         return OPERATOR_FINISHED;
495 }
496
497 void GRAPHEDIT_OT_properties(wmOperatorType *ot)
498 {
499         ot->name= "Properties";
500         ot->idname= "GRAPHEDIT_OT_properties";
501         
502         ot->exec= graph_properties;
503         ot->poll= ED_operator_ipo_active; // xxx
504         
505         /* flags */
506         ot->flag= 0;
507 }