2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
19 * This is a new part of Blender
21 * Contributor(s): Joshua Leung
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/gpencil/gpencil_edit.c
38 #include "MEM_guardedalloc.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
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"
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"
65 #include "RNA_access.h"
66 #include "RNA_define.h"
68 #include "UI_view2d.h"
70 #include "ED_gpencil.h"
71 #include "ED_view3d.h"
73 #include "gpencil_intern.h"
75 /* ************************************************ */
76 /* Context Wrangling... */
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)
81 Scene *scene= CTX_data_scene(C);
82 ScrArea *sa= CTX_wm_area(C);
84 /* if there's an active area, check if the particular editor may
85 * have defined any special Grease Pencil context for editing...
88 switch (sa->spacetype) {
89 case SPACE_VIEW3D: /* 3D-View */
91 Object *ob= CTX_data_active_object(C);
93 // TODO: we can include other data-types such as bones later if need be...
95 /* just in case no active object */
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);
104 case SPACE_NODE: /* Nodes Editor */
106 SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
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;
115 /* even when there is no node-tree, don't allow this to flow to scene */
121 case SPACE_SEQ: /* Sequencer */
123 //SpaceSeq *sseq= (SpaceSeq *)CTX_wm_space_data(C);
125 /* return the GP data for the active strips/image/etc. */
129 case SPACE_IMAGE: /* Image/UV Editor */
131 SpaceImage *sima= (SpaceImage *)CTX_wm_space_data(C);
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);
140 default: /* unsupported space */
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;
150 /* Get the active Grease Pencil datablock */
151 bGPdata *gpencil_data_get_active (bContext *C)
153 bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
154 return (gpd_ptr) ? *(gpd_ptr) : NULL;
157 /* needed for offscreen rendering */
158 bGPdata *gpencil_data_get_active_v3d (Scene *scene)
160 bGPdata *gpd= scene->basact ? scene->basact->object->gpd : NULL;
161 return gpd ? gpd : scene->gpd;
164 /* ************************************************ */
165 /* Panel Operators */
167 /* poll callback for adding data/layers - special */
168 static int gp_add_poll (bContext *C)
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;
174 /* ******************* Add New Data ************************ */
176 /* add new datablock - wrapper around API */
177 static int gp_data_add_exec (bContext *C, wmOperator *op)
179 bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
181 if (gpd_ptr == NULL) {
182 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
183 return OPERATOR_CANCELLED;
186 /* just add new datablock now */
187 *gpd_ptr= gpencil_data_addnew("GPencil");
191 WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
193 return OPERATOR_FINISHED;
196 void GPENCIL_OT_data_add (wmOperatorType *ot)
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;
205 ot->exec= gp_data_add_exec;
206 ot->poll= gp_add_poll;
209 /* ******************* Unlink Data ************************ */
211 /* poll callback for adding data/layers - special */
212 static int gp_data_unlink_poll (bContext *C)
214 bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
216 /* if we have access to some active data, make sure there's a datablock before enabling this */
217 return (gpd_ptr && *gpd_ptr);
221 /* unlink datablock - wrapper around API */
222 static int gp_data_unlink_exec (bContext *C, wmOperator *op)
224 bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
226 if (gpd_ptr == NULL) {
227 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
228 return OPERATOR_CANCELLED;
231 /* just unlink datablock now, decreasing its user count */
232 bGPdata *gpd= (*gpd_ptr);
239 WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
241 return OPERATOR_FINISHED;
244 void GPENCIL_OT_data_unlink (wmOperatorType *ot)
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;
253 ot->exec= gp_data_unlink_exec;
254 ot->poll= gp_data_unlink_poll;
257 /* ******************* Add New Layer ************************ */
259 /* add new layer - wrapper around API */
260 static int gp_layer_add_exec (bContext *C, wmOperator *op)
262 bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
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;
269 if (*gpd_ptr == NULL)
270 *gpd_ptr= gpencil_data_addnew("GPencil");
272 /* add new layer now */
273 gpencil_layer_addnew(*gpd_ptr);
276 WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
278 return OPERATOR_FINISHED;
281 void GPENCIL_OT_layer_add (wmOperatorType *ot)
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;
290 ot->exec= gp_layer_add_exec;
291 ot->poll= gp_add_poll;
294 /* ******************* Delete Active Frame ************************ */
296 static int gp_actframe_delete_poll (bContext *C)
298 bGPdata *gpd= gpencil_data_get_active(C);
299 bGPDlayer *gpl= gpencil_layer_getactive(gpd);
301 /* only if there's an active layer with an active frame */
302 return (gpl && gpl->actframe);
305 /* delete active frame - wrapper around API calls */
306 static int gp_actframe_delete_exec (bContext *C, wmOperator *op)
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);
313 /* if there's no existing Grease-Pencil data there, add some */
315 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
316 return OPERATOR_CANCELLED;
318 if ELEM(NULL, gpl, gpf) {
319 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
320 return OPERATOR_CANCELLED;
324 gpencil_layer_delframe(gpl, gpf);
327 WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
329 return OPERATOR_FINISHED;
332 void GPENCIL_OT_active_frame_delete (wmOperatorType *ot)
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;
341 ot->exec= gp_actframe_delete_exec;
342 ot->poll= gp_actframe_delete_poll;
345 /* ************************************************ */
346 /* Grease Pencil to Data Operator */
348 /* defines for possible modes */
350 GP_STROKECONVERT_PATH = 1,
351 GP_STROKECONVERT_CURVE,
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}
363 /* convert the coordinates from the given stroke point into 3d-coordinates
364 * - assumes that the active space is the 3D-View
366 static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
368 Scene *scene= CTX_data_scene(C);
369 View3D *v3d= CTX_wm_view3d(C);
370 ARegion *ar= CTX_wm_region(C);
372 if (gps->flag & GP_STROKE_3DSPACE) {
373 /* directly use 3d-coordinates */
374 copy_v3_v3(p3d, &pt->x);
377 float *fp= give_cursor(scene, v3d);
380 /* get screen coordinate */
381 if (gps->flag & GP_STROKE_2DSPACE) {
383 View2D *v2d= &ar->v2d;
384 UI_view2d_view_to_region(v2d, pt->x, pt->y, mvali, mvali+1);
385 VECCOPY2D(mvalf, mvali);
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;
393 mvalf[0]= (float)pt->x / 100.0f * ar->winx;
394 mvalf[1]= (float)pt->y / 100.0f * ar->winy;
398 /* convert screen coordinate to 3d coordinates
399 * - method taken from editview.c - mouse_cursor()
401 ED_view3d_win_to_3d(ar, fp, mvalf, p3d);
407 /* convert stroke to 3d path */
408 static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
415 /* create new 'nurb' within the curve */
416 nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
418 nu->pntsu= gps->totpoints;
420 nu->orderu= gps->totpoints;
421 nu->flagu= CU_NURB_ENDPOINT;
424 nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints");
427 for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) {
430 /* get coordinates to add at */
431 gp_strokepoint_convertcoords(C, gps, pt, p3d, subrect);
432 copy_v3_v3(bp->vec, p3d);
436 bp->radius = bp->weight = pt->pressure * gpl->thickness;
439 /* add nurb to curve */
440 BLI_addtail(&cu->nurb, nu);
443 static int gp_camera_view_subrect(bContext *C, rctf *subrect)
445 View3D *v3d= CTX_wm_view3d(C);
446 ARegion *ar= CTX_wm_region(C);
449 RegionView3D *rv3d= ar->regiondata;
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 */
462 /* convert stroke to 3d bezier */
463 static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
469 float p3d_cur[3], p3d_prev[3], p3d_next[3];
471 /* create new 'nurb' within the curve */
472 nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
474 nu->pntsu= gps->totpoints;
478 nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts");
482 /* get initial coordinates */
485 gp_strokepoint_convertcoords(C, gps, pt, p3d_cur, subrect);
487 gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next, subrect);
492 for (i=0, bezt=nu->bezt; i < tot; i++, pt++, bezt++) {
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);
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);
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);
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;
510 /* shift coord vects */
511 copy_v3_v3(p3d_prev, p3d_cur);
512 copy_v3_v3(p3d_cur, p3d_next);
515 gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next, subrect);
519 /* must calculate handles or else we crash */
522 /* add nurb to curve */
523 BLI_addtail(&cu->nurb, nu);
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)
529 Scene *scene= CTX_data_scene(C);
530 bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
536 rctf subrect, *subrect_ptr= NULL;
539 if (ELEM3(NULL, gpd, gpl, gpf))
542 /* only convert if there are any strokes on this layer's frame to convert */
543 if (gpf->strokes.first == NULL)
546 /* initialize camera framing */
547 if(gp_camera_view_subrect(C, &subrect)) {
548 subrect_ptr= &subrect;
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
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;
560 /* rename object and curve to layer name */
561 rename_id((ID *)ob, gpl->info);
562 rename_id((ID *)cu, gpl->info);
564 /* add points to curve */
565 for (gps= gpf->strokes.first; gps; gps= gps->next) {
567 case GP_STROKECONVERT_PATH:
568 gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr);
570 case GP_STROKECONVERT_CURVE:
571 gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr);
579 static int gp_convert_poll (bContext *C)
581 bGPdata *gpd= gpencil_data_get_active(C);
582 ScrArea *sa= CTX_wm_area(C);
583 Scene *scene= CTX_data_scene(C);
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));
589 static int gp_convert_layer_exec (bContext *C, wmOperator *op)
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");
596 /* check if there's data to work with */
598 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on.");
599 return OPERATOR_CANCELLED;
602 /* handle conversion modes */
604 case GP_STROKECONVERT_PATH:
605 case GP_STROKECONVERT_CURVE:
606 gp_layer_to_curve(C, gpd, gpl, mode);
609 default: /* unsupoorted */
610 BKE_report(op->reports, RPT_ERROR, "Unknown conversion option.");
611 return OPERATOR_CANCELLED;
615 WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, NULL);
616 WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
619 return OPERATOR_FINISHED;
622 void GPENCIL_OT_convert (wmOperatorType *ot)
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;
631 ot->invoke= WM_menu_invoke;
632 ot->exec= gp_convert_layer_exec;
633 ot->poll= gp_convert_poll;
636 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
639 ot->prop= RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
642 /* ************************************************ */