fb72ec8f3b4b1a7833c698e0c8eb33cd701f703d
[blender-staging.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
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <math.h>
34
35 #include "MEM_guardedalloc.h"
36
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40
41 #include "DNA_curve_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_node_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_view3d_types.h"
48 #include "DNA_gpencil_types.h"
49
50 #include "BKE_armature.h"
51 #include "BKE_blender.h"
52 #include "BKE_context.h"
53 #include "BKE_curve.h"
54 #include "BKE_global.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_image.h"
57 #include "BKE_library.h"
58 #include "BKE_object.h"
59 #include "BKE_report.h"
60 #include "BKE_utildefines.h"
61
62 #include "BIF_gl.h"
63 #include "BIF_glutil.h"
64
65 #include "WM_api.h"
66 #include "WM_types.h"
67
68 #include "RNA_access.h"
69 #include "RNA_define.h"
70
71 #include "UI_view2d.h"
72
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         
205         /* callbacks */
206         ot->exec= gp_data_add_exec;
207         ot->poll= gp_add_poll;
208 }
209
210 /* ******************* Unlink Data ************************ */
211
212 /* poll callback for adding data/layers - special */
213 static int gp_data_unlink_poll (bContext *C)
214 {
215         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
216         
217         /* if we have access to some active data, make sure there's a datablock before enabling this */
218         return (gpd_ptr && *gpd_ptr);
219 }
220
221
222 /* unlink datablock - wrapper around API */
223 static int gp_data_unlink_exec (bContext *C, wmOperator *op)
224 {
225         bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
226         
227         if (gpd_ptr == NULL) {
228                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
229                 return OPERATOR_CANCELLED;
230         }
231         else {
232                 /* just unlink datablock now, decreasing its user count */
233                 bGPdata *gpd= (*gpd_ptr);
234                 
235                 gpd->id.us--;
236                 *gpd_ptr= NULL;
237         }
238         
239         /* notifiers */
240         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
241         
242         return OPERATOR_FINISHED;
243 }
244
245 void GPENCIL_OT_data_unlink (wmOperatorType *ot)
246 {
247         /* identifiers */
248         ot->name= "Grease Pencil Unlink";
249         ot->idname= "GPENCIL_OT_data_unlink";
250         ot->description= "Unlink active Grease Pencil datablock";
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         
288         /* callbacks */
289         ot->exec= gp_layer_add_exec;
290         ot->poll= gp_add_poll;
291 }
292
293 /* ******************* Delete Active Frame ************************ */
294
295 static int gp_actframe_delete_poll (bContext *C)
296 {
297         bGPdata *gpd= gpencil_data_get_active(C);
298         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
299         
300         /* only if there's an active layer with an active frame */
301         return (gpl && gpl->actframe);
302 }
303
304 /* delete active frame - wrapper around API calls */
305 static int gp_actframe_delete_exec (bContext *C, wmOperator *op)
306 {
307         Scene *scene= CTX_data_scene(C);
308         bGPdata *gpd= gpencil_data_get_active(C);
309         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
310         bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
311         
312         /* if there's no existing Grease-Pencil data there, add some */
313         if (gpd == NULL) {
314                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
315                 return OPERATOR_CANCELLED;
316         }
317         if ELEM(NULL, gpl, gpf) {
318                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
319                 return OPERATOR_CANCELLED;
320         }
321         
322         /* delete it... */
323         gpencil_layer_delframe(gpl, gpf);
324         
325         /* notifiers */
326         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
327         
328         return OPERATOR_FINISHED;
329 }
330
331 void GPENCIL_OT_active_frame_delete (wmOperatorType *ot)
332 {
333         /* identifiers */
334         ot->name= "Delete Active Frame";
335         ot->idname= "GPENCIL_OT_active_frame_delete";
336         ot->description= "Delete the active frame for the active Grease Pencil datablock";
337         
338         /* callbacks */
339         ot->exec= gp_actframe_delete_exec;
340         ot->poll= gp_actframe_delete_poll;
341 }
342
343 /* ************************************************ */
344 /* Grease Pencil to Data Operator */
345
346 /* defines for possible modes */
347 enum {
348         GP_STROKECONVERT_PATH = 1,
349         GP_STROKECONVERT_CURVE,
350 };
351
352 /* RNA enum define */
353 static EnumPropertyItem prop_gpencil_convertmodes[] = {
354         {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
355         {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
356         {0, NULL, 0, NULL, NULL}
357 };
358
359 /* --- */
360
361 /* convert the coordinates from the given stroke point into 3d-coordinates 
362  *      - assumes that the active space is the 3D-View
363  */
364 static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3])
365 {
366         Scene *scene= CTX_data_scene(C);
367         View3D *v3d= CTX_wm_view3d(C);
368         ARegion *ar= CTX_wm_region(C);
369         
370         if (gps->flag & GP_STROKE_3DSPACE) {
371                 /* directly use 3d-coordinates */
372                 copy_v3_v3(p3d, &pt->x);
373         }
374         else {
375                 float *fp= give_cursor(scene, v3d);
376                 float dvec[3];
377                 short mval[2];
378                 int mx, my;
379                 
380                 /* get screen coordinate */
381                 if (gps->flag & GP_STROKE_2DSPACE) {
382                         View2D *v2d= &ar->v2d;
383                         UI_view2d_view_to_region(v2d, pt->x, pt->y, &mx, &my);
384                 }
385                 else {
386                         mx= (int)(pt->x / 100 * ar->winx);
387                         my= (int)(pt->y / 100 * ar->winy);
388                 }
389                 mval[0]= (short)mx;
390                 mval[1]= (short)my;
391                 
392                 /* convert screen coordinate to 3d coordinates 
393                  *      - method taken from editview.c - mouse_cursor() 
394                  */
395                 project_short_noclip(ar, fp, mval);
396                 window_to_3d(ar, dvec, mval[0]-mx, mval[1]-my);
397                 sub_v3_v3v3(p3d, fp, dvec);
398         }
399 }
400
401 /* --- */
402
403 /* convert stroke to 3d path */
404 static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
405 {
406         bGPDspoint *pt;
407         Nurb *nu;
408         BPoint *bp;
409         int i;
410         
411         /* create new 'nurb' within the curve */
412         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
413         
414         nu->pntsu= gps->totpoints;
415         nu->pntsv= 1;
416         nu->orderu= gps->totpoints;
417         nu->flagu= CU_NURB_ENDPOINT;
418         nu->resolu= 32;
419         
420         nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints");
421         
422         /* add points */
423         for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) {
424                 float p3d[3];
425                 
426                 /* get coordinates to add at */
427                 gp_strokepoint_convertcoords(C, gps, pt, p3d);
428                 copy_v3_v3(bp->vec, p3d);
429                 
430                 /* set settings */
431                 bp->f1= SELECT;
432                 bp->radius = bp->weight = pt->pressure * gpl->thickness;
433         }
434         
435         /* add nurb to curve */
436         BLI_addtail(&cu->nurb, nu);
437 }
438
439 /* convert stroke to 3d bezier */
440 static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
441 {
442         bGPDspoint *pt;
443         Nurb *nu;
444         BezTriple *bezt;
445         int i, tot;
446         float p3d_cur[3], p3d_prev[3], p3d_next[3];
447
448         /* create new 'nurb' within the curve */
449         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
450
451         nu->pntsu= gps->totpoints;
452         nu->resolu= 12;
453         nu->resolv= 12;
454         nu->type= CU_BEZIER;
455         nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts");
456
457         tot= gps->totpoints;
458
459         /* get initial coordinates */
460         pt=gps->points;
461         if (tot) {
462                 gp_strokepoint_convertcoords(C, gps, pt, p3d_cur);
463                 if (tot > 1) {
464                         gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next);
465                 }
466         }
467
468         /* add points */
469         for (i=0, bezt=nu->bezt; i < tot; i++, pt++, bezt++) {
470                 float h1[3], h2[3];
471
472                 if (i) interp_v3_v3v3(h1, p3d_cur, p3d_prev, 0.3);
473                 else interp_v3_v3v3(h1, p3d_cur, p3d_next, -0.3);
474
475                 if (i < tot-1) interp_v3_v3v3(h2, p3d_cur, p3d_next, 0.3);
476                 else interp_v3_v3v3(h2, p3d_cur, p3d_prev, -0.3);
477
478                 copy_v3_v3(bezt->vec[0], h1);
479                 copy_v3_v3(bezt->vec[1], p3d_cur);
480                 copy_v3_v3(bezt->vec[2], h2);
481
482                 /* set settings */
483                 bezt->h1= bezt->h2= HD_FREE;
484                 bezt->f1= bezt->f2= bezt->f3= SELECT;
485                 bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f;
486
487                 /* shift coord vects */
488                 copy_v3_v3(p3d_prev, p3d_cur);
489                 copy_v3_v3(p3d_cur, p3d_next);
490
491                 if (i < tot) {
492                         gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next);
493                 }
494         }
495
496         /* must calculate handles or else we crash */
497         calchandlesNurb(nu);
498
499         /* add nurb to curve */
500         BLI_addtail(&cu->nurb, nu);
501 }
502
503 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
504 static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode)
505 {
506         Scene *scene= CTX_data_scene(C);
507         bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
508         bGPDstroke *gps;
509         Object *ob;
510         Curve *cu;
511         
512         /* error checking */
513         if (ELEM3(NULL, gpd, gpl, gpf))
514                 return;
515                 
516         /* only convert if there are any strokes on this layer's frame to convert */
517         if (gpf->strokes.first == NULL)
518                 return;
519
520         /* init the curve object (remove rotation and get curve data from it)
521          *      - must clear transforms set on object, as those skew our results
522          */
523         ob= add_object(scene, OB_CURVE);
524         ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
525         ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
526         cu= ob->data;
527         cu->flag |= CU_3D;
528         
529         /* rename object and curve to layer name */
530         rename_id((ID *)ob, gpl->info);
531         rename_id((ID *)cu, gpl->info);
532         
533         /* add points to curve */
534         for (gps= gpf->strokes.first; gps; gps= gps->next) {
535                 switch (mode) {
536                         case GP_STROKECONVERT_PATH: 
537                                 gp_stroke_to_path(C, gpl, gps, cu);
538                                 break;
539                         case GP_STROKECONVERT_CURVE:
540                                 gp_stroke_to_bezier(C, gpl, gps, cu);
541                                 break;
542                 }
543         }
544 }
545
546 /* --- */
547
548 static int gp_convert_poll (bContext *C)
549 {
550         bGPdata *gpd= gpencil_data_get_active(C);
551         ScrArea *sa= CTX_wm_area(C);
552         
553         /* only if there's valid data, and the current view is 3D View */
554         return ((sa->spacetype == SPACE_VIEW3D) && gpencil_layer_getactive(gpd));
555 }
556
557 static int gp_convert_layer_exec (bContext *C, wmOperator *op)
558 {
559         bGPdata *gpd= gpencil_data_get_active(C);
560         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
561         Scene *scene= CTX_data_scene(C);
562         View3D *v3d= CTX_wm_view3d(C);
563         float *fp= give_cursor(scene, v3d);
564         int mode= RNA_enum_get(op->ptr, "type");
565
566         /* check if there's data to work with */
567         if (gpd == NULL) {
568                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on.");
569                 return OPERATOR_CANCELLED;
570         }
571
572         /* initialise 3d-cursor correction globals */
573         initgrabz(CTX_wm_region_view3d(C), fp[0], fp[1], fp[2]);
574
575         /* handle conversion modes */
576         switch (mode) {
577                 case GP_STROKECONVERT_PATH:
578                 case GP_STROKECONVERT_CURVE:
579                         gp_layer_to_curve(C, gpd, gpl, mode);
580                         break;
581                         
582                 default: /* unsupoorted */
583                         BKE_report(op->reports, RPT_ERROR, "Unknown conversion option.");
584                         return OPERATOR_CANCELLED;
585         }
586
587         /* notifiers */
588         WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, NULL);
589         WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
590
591         /* done */
592         return OPERATOR_FINISHED;
593 }
594
595 void GPENCIL_OT_convert (wmOperatorType *ot)
596 {
597         /* identifiers */
598         ot->name= "Convert Grease Pencil";
599         ot->idname= "GPENCIL_OT_convert";
600         ot->description= "Convert the active Grease Pencil layer to a new Object";
601         
602         /* callbacks */
603         ot->invoke= WM_menu_invoke;
604         ot->exec= gp_convert_layer_exec;
605         ot->poll= gp_convert_poll;
606         
607         /* flags */
608         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
609         
610         /* properties */
611         ot->prop= RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
612 }
613
614 /* ************************************************ */