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