fix [#26801] Undoing Animation modifiers undoes 2 steps.
[blender.git] / source / blender / editors / gpencil / gpencil_edit.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
21  * This is a new part of Blender
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/gpencil/gpencil_edit.c
29  *  \ingroup edgpencil
30  */
31
32  
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stddef.h>
38 #include <math.h>
39
40 #include "MEM_guardedalloc.h"
41
42
43 #include "BLI_math.h"
44 #include "BLI_blenlib.h"
45 #include "BLI_utildefines.h"
46
47 #include "DNA_curve_types.h"
48 #include "DNA_object_types.h"
49 #include "DNA_node_types.h"
50 #include "DNA_scene_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_view3d_types.h"
54 #include "DNA_gpencil_types.h"
55
56 #include "BKE_context.h"
57 #include "BKE_curve.h"
58 #include "BKE_gpencil.h"
59 #include "BKE_library.h"
60 #include "BKE_object.h"
61 #include "BKE_report.h"
62
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 "UI_view2d.h"
71
72 #include "ED_gpencil.h"
73 #include "ED_view3d.h"
74
75 #include "gpencil_intern.h"
76
77 /* ************************************************ */
78 /* Context Wrangling... */
79
80 /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
81 bGPdata **gpencil_data_get_pointers (bContext *C, PointerRNA *ptr)
82 {
83         Scene *scene= CTX_data_scene(C);
84         ScrArea *sa= CTX_wm_area(C);
85         
86         /* if there's an active area, check if the particular editor may
87          * have defined any special Grease Pencil context for editing...
88          */
89         if (sa) {
90                 switch (sa->spacetype) {
91                         case SPACE_VIEW3D: /* 3D-View */
92                         {
93                                 Object *ob= CTX_data_active_object(C);
94                                 
95                                 // TODO: we can include other data-types such as bones later if need be...
96                                 
97                                 /* just in case no active object */
98                                 if (ob) {
99                                         /* for now, as long as there's an object, default to using that in 3D-View */
100                                         if (ptr) RNA_id_pointer_create(&ob->id, ptr);
101                                         return &ob->gpd;
102                                 }
103                         }
104                                 break;
105                         
106                         case SPACE_NODE: /* Nodes Editor */
107                         {
108                                 SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
109                                 
110                                 /* return the GP data for the active node block/node */
111                                 if (snode && snode->nodetree) {
112                                         /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */
113                                         if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr);
114                                         return &snode->nodetree->gpd;
115                                 }
116                                 else {
117                                         /* even when there is no node-tree, don't allow this to flow to scene */
118                                         return NULL;
119                                 }
120                         }
121                                 break;
122                                 
123                         case SPACE_SEQ: /* Sequencer */
124                         {
125                                 //SpaceSeq *sseq= (SpaceSeq *)CTX_wm_space_data(C);
126                                 
127                                 /* return the GP data for the active strips/image/etc. */
128                         }
129                                 break;
130                                 
131                         case SPACE_IMAGE: /* Image/UV Editor */
132                         {
133                                 SpaceImage *sima= (SpaceImage *)CTX_wm_space_data(C);
134                                 
135                                 /* for now, Grease Pencil data is associated with the space... */
136                                 // XXX our convention for everything else is to link to data though...
137                                 if (ptr) RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_SpaceImageEditor, sima, ptr);
138                                 return &sima->gpd;
139                         }
140                                 break;
141                                 
142                         default: /* unsupported space */
143                                 return NULL;
144                 }
145         }
146         
147         /* just fall back on the scene's GP data */
148         if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
149         return (scene) ? &scene->gpd : NULL;
150 }
151
152 /* Get the active Grease Pencil datablock */
153 bGPdata *gpencil_data_get_active (bContext *C)
154 {
155         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
156         return (gpd_ptr) ? *(gpd_ptr) : NULL;
157 }
158
159 /* needed for offscreen rendering */
160 bGPdata *gpencil_data_get_active_v3d (Scene *scene)
161 {
162         bGPdata *gpd= scene->basact ? scene->basact->object->gpd : NULL;
163         return gpd ? gpd : scene->gpd;
164 }
165
166 /* ************************************************ */
167 /* Panel Operators */
168
169 /* poll callback for adding data/layers - special */
170 static int gp_add_poll (bContext *C)
171 {
172         /* the base line we have is that we have somewhere to add Grease Pencil data */
173         return gpencil_data_get_pointers(C, NULL) != NULL;
174 }
175
176 /* ******************* Add New Data ************************ */
177
178 /* add new datablock - wrapper around API */
179 static int gp_data_add_exec (bContext *C, wmOperator *op)
180 {
181         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
182         
183         if (gpd_ptr == NULL) {
184                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
185                 return OPERATOR_CANCELLED;
186         }
187         else {
188                 /* just add new datablock now */
189                 *gpd_ptr= gpencil_data_addnew("GPencil");
190         }
191         
192         /* notifiers */
193         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
194         
195         return OPERATOR_FINISHED;
196 }
197
198 void GPENCIL_OT_data_add (wmOperatorType *ot)
199 {
200         /* identifiers */
201         ot->name= "Grease Pencil Add New";
202         ot->idname= "GPENCIL_OT_data_add";
203         ot->description= "Add new Grease Pencil datablock";
204         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
205         
206         /* callbacks */
207         ot->exec= gp_data_add_exec;
208         ot->poll= gp_add_poll;
209 }
210
211 /* ******************* Unlink Data ************************ */
212
213 /* poll callback for adding data/layers - special */
214 static int gp_data_unlink_poll (bContext *C)
215 {
216         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
217         
218         /* if we have access to some active data, make sure there's a datablock before enabling this */
219         return (gpd_ptr && *gpd_ptr);
220 }
221
222
223 /* unlink datablock - wrapper around API */
224 static int gp_data_unlink_exec (bContext *C, wmOperator *op)
225 {
226         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
227         
228         if (gpd_ptr == NULL) {
229                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
230                 return OPERATOR_CANCELLED;
231         }
232         else {
233                 /* just unlink datablock now, decreasing its user count */
234                 bGPdata *gpd= (*gpd_ptr);
235                 
236                 gpd->id.us--;
237                 *gpd_ptr= NULL;
238         }
239         
240         /* notifiers */
241         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
242         
243         return OPERATOR_FINISHED;
244 }
245
246 void GPENCIL_OT_data_unlink (wmOperatorType *ot)
247 {
248         /* identifiers */
249         ot->name= "Grease Pencil Unlink";
250         ot->idname= "GPENCIL_OT_data_unlink";
251         ot->description= "Unlink active Grease Pencil datablock";
252         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
253         
254         /* callbacks */
255         ot->exec= gp_data_unlink_exec;
256         ot->poll= gp_data_unlink_poll;
257 }
258
259 /* ******************* Add New Layer ************************ */
260
261 /* add new layer - wrapper around API */
262 static int gp_layer_add_exec (bContext *C, wmOperator *op)
263 {
264         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
265         
266         /* if there's no existing Grease-Pencil data there, add some */
267         if (gpd_ptr == NULL) {
268                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
269                 return OPERATOR_CANCELLED;
270         }
271         if (*gpd_ptr == NULL)
272                 *gpd_ptr= gpencil_data_addnew("GPencil");
273                 
274         /* add new layer now */
275         gpencil_layer_addnew(*gpd_ptr);
276         
277         /* notifiers */
278         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
279         
280         return OPERATOR_FINISHED;
281 }
282
283 void GPENCIL_OT_layer_add (wmOperatorType *ot)
284 {
285         /* identifiers */
286         ot->name= "Add New Layer";
287         ot->idname= "GPENCIL_OT_layer_add";
288         ot->description= "Add new Grease Pencil layer for the active Grease Pencil datablock";
289         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
290         
291         /* callbacks */
292         ot->exec= gp_layer_add_exec;
293         ot->poll= gp_add_poll;
294 }
295
296 /* ******************* Delete Active Frame ************************ */
297
298 static int gp_actframe_delete_poll (bContext *C)
299 {
300         bGPdata *gpd= gpencil_data_get_active(C);
301         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
302         
303         /* only if there's an active layer with an active frame */
304         return (gpl && gpl->actframe);
305 }
306
307 /* delete active frame - wrapper around API calls */
308 static int gp_actframe_delete_exec (bContext *C, wmOperator *op)
309 {
310         Scene *scene= CTX_data_scene(C);
311         bGPdata *gpd= gpencil_data_get_active(C);
312         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
313         bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
314         
315         /* if there's no existing Grease-Pencil data there, add some */
316         if (gpd == NULL) {
317                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
318                 return OPERATOR_CANCELLED;
319         }
320         if ELEM(NULL, gpl, gpf) {
321                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
322                 return OPERATOR_CANCELLED;
323         }
324         
325         /* delete it... */
326         gpencil_layer_delframe(gpl, gpf);
327         
328         /* notifiers */
329         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
330         
331         return OPERATOR_FINISHED;
332 }
333
334 void GPENCIL_OT_active_frame_delete (wmOperatorType *ot)
335 {
336         /* identifiers */
337         ot->name= "Delete Active Frame";
338         ot->idname= "GPENCIL_OT_active_frame_delete";
339         ot->description= "Delete the active frame for the active Grease Pencil datablock";
340         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
341         
342         /* callbacks */
343         ot->exec= gp_actframe_delete_exec;
344         ot->poll= gp_actframe_delete_poll;
345 }
346
347 /* ************************************************ */
348 /* Grease Pencil to Data Operator */
349
350 /* defines for possible modes */
351 enum {
352         GP_STROKECONVERT_PATH = 1,
353         GP_STROKECONVERT_CURVE,
354 };
355
356 /* RNA enum define */
357 static EnumPropertyItem prop_gpencil_convertmodes[] = {
358         {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
359         {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
360         {0, NULL, 0, NULL, NULL}
361 };
362
363 /* --- */
364
365 /* convert the coordinates from the given stroke point into 3d-coordinates 
366  *      - assumes that the active space is the 3D-View
367  */
368 static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3])
369 {
370         Scene *scene= CTX_data_scene(C);
371         View3D *v3d= CTX_wm_view3d(C);
372         ARegion *ar= CTX_wm_region(C);
373         
374         if (gps->flag & GP_STROKE_3DSPACE) {
375                 /* directly use 3d-coordinates */
376                 copy_v3_v3(p3d, &pt->x);
377         }
378         else {
379                 float *fp= give_cursor(scene, v3d);
380                 float dvec[3];
381                 short mval[2];
382                 int mx, my;
383                 
384                 /* get screen coordinate */
385                 if (gps->flag & GP_STROKE_2DSPACE) {
386                         View2D *v2d= &ar->v2d;
387                         UI_view2d_view_to_region(v2d, pt->x, pt->y, &mx, &my);
388                 }
389                 else {
390                         mx= (int)(pt->x / 100 * ar->winx);
391                         my= (int)(pt->y / 100 * ar->winy);
392                 }
393                 mval[0]= (short)mx;
394                 mval[1]= (short)my;
395                 
396                 /* convert screen coordinate to 3d coordinates 
397                  *      - method taken from editview.c - mouse_cursor() 
398                  */
399                 project_short_noclip(ar, fp, mval);
400                 window_to_3d(ar, dvec, mval[0]-mx, mval[1]-my);
401                 sub_v3_v3v3(p3d, fp, dvec);
402         }
403 }
404
405 /* --- */
406
407 /* convert stroke to 3d path */
408 static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
409 {
410         bGPDspoint *pt;
411         Nurb *nu;
412         BPoint *bp;
413         int i;
414         
415         /* create new 'nurb' within the curve */
416         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
417         
418         nu->pntsu= gps->totpoints;
419         nu->pntsv= 1;
420         nu->orderu= gps->totpoints;
421         nu->flagu= CU_NURB_ENDPOINT;
422         nu->resolu= 32;
423         
424         nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints");
425         
426         /* add points */
427         for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) {
428                 float p3d[3];
429                 
430                 /* get coordinates to add at */
431                 gp_strokepoint_convertcoords(C, gps, pt, p3d);
432                 copy_v3_v3(bp->vec, p3d);
433                 
434                 /* set settings */
435                 bp->f1= SELECT;
436                 bp->radius = bp->weight = pt->pressure * gpl->thickness;
437         }
438         
439         /* add nurb to curve */
440         BLI_addtail(&cu->nurb, nu);
441 }
442
443 /* convert stroke to 3d bezier */
444 static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
445 {
446         bGPDspoint *pt;
447         Nurb *nu;
448         BezTriple *bezt;
449         int i, tot;
450         float p3d_cur[3], p3d_prev[3], p3d_next[3];
451
452         /* create new 'nurb' within the curve */
453         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
454
455         nu->pntsu= gps->totpoints;
456         nu->resolu= 12;
457         nu->resolv= 12;
458         nu->type= CU_BEZIER;
459         nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts");
460
461         tot= gps->totpoints;
462
463         /* get initial coordinates */
464         pt=gps->points;
465         if (tot) {
466                 gp_strokepoint_convertcoords(C, gps, pt, p3d_cur);
467                 if (tot > 1) {
468                         gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next);
469                 }
470         }
471
472         /* add points */
473         for (i=0, bezt=nu->bezt; i < tot; i++, pt++, bezt++) {
474                 float h1[3], h2[3];
475
476                 if (i) interp_v3_v3v3(h1, p3d_cur, p3d_prev, 0.3);
477                 else interp_v3_v3v3(h1, p3d_cur, p3d_next, -0.3);
478
479                 if (i < tot-1) interp_v3_v3v3(h2, p3d_cur, p3d_next, 0.3);
480                 else interp_v3_v3v3(h2, p3d_cur, p3d_prev, -0.3);
481
482                 copy_v3_v3(bezt->vec[0], h1);
483                 copy_v3_v3(bezt->vec[1], p3d_cur);
484                 copy_v3_v3(bezt->vec[2], h2);
485
486                 /* set settings */
487                 bezt->h1= bezt->h2= HD_FREE;
488                 bezt->f1= bezt->f2= bezt->f3= SELECT;
489                 bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f;
490
491                 /* shift coord vects */
492                 copy_v3_v3(p3d_prev, p3d_cur);
493                 copy_v3_v3(p3d_cur, p3d_next);
494
495                 if (i + 1 < tot) {
496                         gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next);
497                 }
498         }
499
500         /* must calculate handles or else we crash */
501         calchandlesNurb(nu);
502
503         /* add nurb to curve */
504         BLI_addtail(&cu->nurb, nu);
505 }
506
507 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
508 static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode)
509 {
510         Scene *scene= CTX_data_scene(C);
511         bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
512         bGPDstroke *gps;
513         Object *ob;
514         Curve *cu;
515         
516         /* error checking */
517         if (ELEM3(NULL, gpd, gpl, gpf))
518                 return;
519                 
520         /* only convert if there are any strokes on this layer's frame to convert */
521         if (gpf->strokes.first == NULL)
522                 return;
523
524         /* init the curve object (remove rotation and get curve data from it)
525          *      - must clear transforms set on object, as those skew our results
526          */
527         ob= add_object(scene, OB_CURVE);
528         ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
529         ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
530         cu= ob->data;
531         cu->flag |= CU_3D;
532         
533         /* rename object and curve to layer name */
534         rename_id((ID *)ob, gpl->info);
535         rename_id((ID *)cu, gpl->info);
536         
537         /* add points to curve */
538         for (gps= gpf->strokes.first; gps; gps= gps->next) {
539                 switch (mode) {
540                         case GP_STROKECONVERT_PATH: 
541                                 gp_stroke_to_path(C, gpl, gps, cu);
542                                 break;
543                         case GP_STROKECONVERT_CURVE:
544                                 gp_stroke_to_bezier(C, gpl, gps, cu);
545                                 break;
546                 }
547         }
548 }
549
550 /* --- */
551
552 static int gp_convert_poll (bContext *C)
553 {
554         bGPdata *gpd= gpencil_data_get_active(C);
555         ScrArea *sa= CTX_wm_area(C);
556         Scene *scene= CTX_data_scene(C);
557
558         /* only if there's valid data, and the current view is 3D View */
559         return ((sa && sa->spacetype == SPACE_VIEW3D) && gpencil_layer_getactive(gpd) && (scene->obedit == NULL));
560 }
561
562 static int gp_convert_layer_exec (bContext *C, wmOperator *op)
563 {
564         bGPdata *gpd= gpencil_data_get_active(C);
565         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
566         Scene *scene= CTX_data_scene(C);
567         View3D *v3d= CTX_wm_view3d(C);
568         float *fp= give_cursor(scene, v3d);
569         int mode= RNA_enum_get(op->ptr, "type");
570
571         /* check if there's data to work with */
572         if (gpd == NULL) {
573                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on.");
574                 return OPERATOR_CANCELLED;
575         }
576
577         /* initialise 3d-cursor correction globals */
578         initgrabz(CTX_wm_region_view3d(C), fp[0], fp[1], fp[2]);
579
580         /* handle conversion modes */
581         switch (mode) {
582                 case GP_STROKECONVERT_PATH:
583                 case GP_STROKECONVERT_CURVE:
584                         gp_layer_to_curve(C, gpd, gpl, mode);
585                         break;
586                         
587                 default: /* unsupoorted */
588                         BKE_report(op->reports, RPT_ERROR, "Unknown conversion option.");
589                         return OPERATOR_CANCELLED;
590         }
591
592         /* notifiers */
593         WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, NULL);
594         WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
595
596         /* done */
597         return OPERATOR_FINISHED;
598 }
599
600 void GPENCIL_OT_convert (wmOperatorType *ot)
601 {
602         /* identifiers */
603         ot->name= "Convert Grease Pencil";
604         ot->idname= "GPENCIL_OT_convert";
605         ot->description= "Convert the active Grease Pencil layer to a new Object";
606         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
607         
608         /* callbacks */
609         ot->invoke= WM_menu_invoke;
610         ot->exec= gp_convert_layer_exec;
611         ot->poll= gp_convert_poll;
612         
613         /* flags */
614         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
615         
616         /* properties */
617         ot->prop= RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
618 }
619
620 /* ************************************************ */