Merging r39251 through r39311 from trunk into soc-2011-tomato
[blender-staging.git] / source / blender / editors / gpencil / gpencil_paint.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_paint.c
29  *  \ingroup edgpencil
30  */
31
32
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <math.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_math.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_gpencil.h"
46 #include "BKE_context.h"
47 #include "BKE_global.h"
48 #include "BKE_report.h"
49
50 #include "DNA_object_types.h"
51 #include "DNA_scene_types.h"
52 #include "DNA_gpencil_types.h"
53 #include "DNA_windowmanager_types.h"
54
55 #include "UI_view2d.h"
56
57 #include "ED_gpencil.h"
58 #include "ED_screen.h"
59 #include "ED_view3d.h"
60
61 #include "RNA_access.h"
62
63 #include "RNA_define.h"
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "gpencil_intern.h"
68
69 /* ******************************************* */
70 /* 'Globals' and Defines */
71
72 /* Temporary 'Stroke' Operation data */
73 typedef struct tGPsdata {
74         Scene *scene;       /* current scene from context */
75         
76         wmWindow *win;          /* window where painting originated */
77         ScrArea *sa;            /* area where painting originated */
78         ARegion *ar;        /* region where painting originated */
79         View2D *v2d;            /* needed for GP_STROKE_2DSPACE */
80         rctf *subrect;          /* for using the camera rect within the 3d view */
81         rctf subrect_data;
82         
83         
84 #if 0 // XXX review this 2d image stuff...
85         ImBuf *ibuf;            /* needed for GP_STROKE_2DIMAGE */
86         struct IBufViewSettings {
87                 int offsx, offsy;                       /* offsets */
88                 int sizex, sizey;                       /* dimensions to use as scale-factor */
89         } im2d_settings;        /* needed for GP_STROKE_2DIMAGE */
90 #endif
91         
92         PointerRNA ownerPtr;/* pointer to owner of gp-datablock */
93         bGPdata *gpd;           /* gp-datablock layer comes from */
94         bGPDlayer *gpl;         /* layer we're working on */
95         bGPDframe *gpf;         /* frame we're working on */
96         
97         short status;           /* current status of painting */
98         short paintmode;        /* mode for painting */
99         
100         int mval[2];            /* current mouse-position */
101         int mvalo[2];           /* previous recorded mouse-position */
102         
103         float pressure;         /* current stylus pressure */
104         float opressure;        /* previous stylus pressure */
105         
106         short radius;           /* radius of influence for eraser */
107         short flags;            /* flags that can get set during runtime */
108 } tGPsdata;
109
110 /* values for tGPsdata->status */
111 enum {
112         GP_STATUS_IDLING = 0,   /* stroke isn't in progress yet */
113         GP_STATUS_PAINTING,             /* a stroke is in progress */
114         GP_STATUS_ERROR,                /* something wasn't correctly set up */
115         GP_STATUS_DONE                  /* painting done */
116 };
117
118 /* Return flags for adding points to stroke buffer */
119 enum {
120         GP_STROKEADD_INVALID    = -2,           /* error occurred - insufficient info to do so */
121         GP_STROKEADD_OVERFLOW   = -1,           /* error occurred - cannot fit any more points */
122         GP_STROKEADD_NORMAL,                            /* point was successfully added */
123         GP_STROKEADD_FULL                                       /* cannot add any more points to buffer */
124 };
125
126 /* Runtime flags */
127 enum {
128         GP_PAINTFLAG_FIRSTRUN           = (1<<0),       /* operator just started */
129 };
130
131 /* ------ */
132
133 /* maximum sizes of gp-session buffer */
134 #define GP_STROKE_BUFFER_MAX    5000
135
136 /* Macros for accessing sensitivity thresholds... */
137         /* minimum number of pixels mouse should move before new point created */
138 #define MIN_MANHATTEN_PX        (U.gp_manhattendist)
139         /* minimum length of new segment before new point can be added */
140 #define MIN_EUCLIDEAN_PX        (U.gp_euclideandist)
141
142 /* ------ */
143 /* Forward defines for some functions... */
144
145 static void gp_session_validatebuffer(tGPsdata *p);
146
147 /* ******************************************* */
148 /* Context Wrangling... */
149
150 /* check if context is suitable for drawing */
151 static int gpencil_draw_poll (bContext *C)
152 {
153         if (ED_operator_regionactive(C)) {
154                 /* check if current context can support GPencil data */
155                 if (gpencil_data_get_pointers(C, NULL) != NULL) {
156                         /* check if Grease Pencil isn't already running */
157                         if ((G.f & G_GREASEPENCIL) == 0)
158                                 return 1;
159                         else
160                                 CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active");
161                 }
162                 else {
163                         CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
164                 }
165         }
166         else {
167                 CTX_wm_operator_poll_msg_set(C, "Active region not set");
168         }
169         
170         return 0;
171 }
172
173 /* check if projecting strokes into 3d-geometry in the 3D-View */
174 static int gpencil_project_check (tGPsdata *p)
175 {
176         bGPdata *gpd= p->gpd;
177         return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (p->gpd->flag & (GP_DATA_DEPTH_VIEW | GP_DATA_DEPTH_STROKE)));
178 }
179
180 /* ******************************************* */
181 /* Calculations/Conversions */
182
183 /* Utilities --------------------------------- */
184
185 /* get the reference point for stroke-point conversions */
186 static void gp_get_3d_reference (tGPsdata *p, float *vec)
187 {
188         View3D *v3d= p->sa->spacedata.first;
189         float *fp= give_cursor(p->scene, v3d);
190         
191         /* the reference point used depends on the owner... */
192 #if 0 // XXX: disabled for now, since we can't draw relative to the owner yet
193         if (p->ownerPtr.type == &RNA_Object) 
194         {
195                 Object *ob= (Object *)p->ownerPtr.data;
196                 
197                 /* active Object 
198                  *      - use relative distance of 3D-cursor from object center 
199                  */
200                 sub_v3_v3v3(vec, fp, ob->loc);
201         }
202         else
203 #endif  
204         {
205                 /* use 3D-cursor */
206                 copy_v3_v3(vec, fp);
207         }
208 }
209
210 /* Stroke Editing ---------------------------- */
211
212 /* check if the current mouse position is suitable for adding a new point */
213 static short gp_stroke_filtermval (tGPsdata *p, const int mval[2], int pmval[2])
214 {
215         int dx= abs(mval[0] - pmval[0]);
216         int dy= abs(mval[1] - pmval[1]);
217         
218         /* if buffer is empty, just let this go through (i.e. so that dots will work) */
219         if (p->gpd->sbuffer_size == 0)
220                 return 1;
221         
222         /* check if mouse moved at least certain distance on both axes (best case) 
223          *      - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
224          */
225         else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX))
226                 return 1;
227         
228         /* check if the distance since the last point is significant enough 
229          *      - prevents points being added too densely
230          *      - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though
231          */
232         else if ((dx*dx + dy*dy) > MIN_EUCLIDEAN_PX*MIN_EUCLIDEAN_PX)
233                 return 1;
234         
235         /* mouse 'didn't move' */
236         else
237                 return 0;
238 }
239
240 /* convert screen-coordinates to buffer-coordinates */
241 // XXX this method needs a total overhaul!
242 static void gp_stroke_convertcoords (tGPsdata *p, const int mval[2], float out[3], float *depth)
243 {
244         bGPdata *gpd= p->gpd;
245         
246         /* in 3d-space - pt->x/y/z are 3 side-by-side floats */
247         if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) {
248                 if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) {
249                         /* projecting onto 3D-Geometry
250                          *      - nothing more needs to be done here, since view_autodist_simple() has already done it
251                          */
252                 }
253                 else {
254                         int mval_prj[2];
255                         float rvec[3], dvec[3];
256                         float mval_f[2];
257
258                         /* Current method just converts each point in screen-coordinates to
259                          * 3D-coordinates using the 3D-cursor as reference. In general, this
260                          * works OK, but it could of course be improved.
261                          *
262                          * TODO:
263                          *      - investigate using nearest point(s) on a previous stroke as
264                          *        reference point instead or as offset, for easier stroke matching
265                          */
266                         
267                         gp_get_3d_reference(p, rvec);
268                         
269                         /* method taken from editview.c - mouse_cursor() */
270                         project_int_noclip(p->ar, rvec, mval_prj);
271
272                         VECSUB2D(mval_f, mval_prj, mval);
273                         ED_view3d_win_to_delta(p->ar, mval_f, dvec);
274                         sub_v3_v3v3(out, rvec, dvec);
275                 }
276         }
277         
278         /* 2d - on 'canvas' (assume that p->v2d is set) */
279         else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) {
280                 UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]);
281         }
282         
283 #if 0
284         /* 2d - on image 'canvas' (assume that p->v2d is set) */
285         else if (gpd->sbuffer_sflag & GP_STROKE_2DIMAGE) {
286                 int sizex, sizey, offsx, offsy;
287                 
288                 /* get stored settings 
289                  *      - assume that these have been set already (there are checks that set sane 'defaults' just in case)
290                  */
291                 sizex= p->im2d_settings.sizex;
292                 sizey= p->im2d_settings.sizey;
293                 offsx= p->im2d_settings.offsx;
294                 offsy= p->im2d_settings.offsy;
295                 
296                 /* calculate new points */
297                 out[0]= (float)(mval[0] - offsx) / (float)sizex;
298                 out[1]= (float)(mval[1] - offsy) / (float)sizey;
299         }
300 #endif
301         
302         /* 2d - relative to screen (viewport area) */
303         else {
304                 if (p->subrect == NULL) { /* normal 3D view */
305                         out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100;
306                         out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100;
307                 }
308                 else { /* camera view, use subrect */
309                         out[0]= ((mval[0] - p->subrect->xmin) / ((p->subrect->xmax - p->subrect->xmin))) * 100;
310                         out[1]= ((mval[1] - p->subrect->ymin) / ((p->subrect->ymax - p->subrect->ymin))) * 100;
311                 }
312         }
313 }
314
315 /* add current stroke-point to buffer (returns whether point was successfully added) */
316 static short gp_stroke_addpoint (tGPsdata *p, const int mval[2], float pressure)
317 {
318         bGPdata *gpd= p->gpd;
319         tGPspoint *pt;
320         
321         /* check painting mode */
322         if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
323                 /* straight lines only - i.e. only store start and end point in buffer */
324                 if (gpd->sbuffer_size == 0) {
325                         /* first point in buffer (start point) */
326                         pt= (tGPspoint *)(gpd->sbuffer);
327                         
328                         /* store settings */
329                         pt->x= mval[0];
330                         pt->y= mval[1];
331                         pt->pressure= pressure;
332                         
333                         /* increment buffer size */
334                         gpd->sbuffer_size++;
335                 }
336                 else {
337                         /* normally, we just reset the endpoint to the latest value 
338                          *      - assume that pointers for this are always valid...
339                          */
340                         pt= ((tGPspoint *)(gpd->sbuffer) + 1);
341                         
342                         /* store settings */
343                         pt->x= mval[0];
344                         pt->y= mval[1];
345                         pt->pressure= pressure;
346                         
347                         /* if this is just the second point we've added, increment the buffer size
348                          * so that it will be drawn properly...
349                          * otherwise, just leave it alone, otherwise we get problems
350                          */
351                         if (gpd->sbuffer_size != 2)
352                                 gpd->sbuffer_size= 2;
353                 }
354                 
355                 /* can keep carrying on this way :) */
356                 return GP_STROKEADD_NORMAL;
357         }
358         else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
359                 /* check if still room in buffer */
360                 if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX)
361                         return GP_STROKEADD_OVERFLOW;
362                 
363                 /* get pointer to destination point */
364                 pt= ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size);
365                 
366                 /* store settings */
367                 pt->x= mval[0];
368                 pt->y= mval[1];
369                 pt->pressure= pressure;
370                 
371                 /* increment counters */
372                 gpd->sbuffer_size++;
373                 
374                 /* check if another operation can still occur */
375                 if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX)
376                         return GP_STROKEADD_FULL;
377                 else
378                         return GP_STROKEADD_NORMAL;
379         }
380         
381         /* return invalid state for now... */
382         return GP_STROKEADD_INVALID;
383 }
384
385
386 /* temp struct for gp_stroke_smooth() */
387 typedef struct tGpSmoothCo {
388         int x;
389         int y;
390 } tGpSmoothCo;
391
392 /* smooth a stroke (in buffer) before storing it */
393 static void gp_stroke_smooth (tGPsdata *p)
394 {
395         bGPdata *gpd= p->gpd;
396         tGpSmoothCo *smoothArray, *spc;
397         int i=0, cmx=gpd->sbuffer_size;
398         
399         /* only smooth if smoothing is enabled, and we're not doing a straight line */
400         if (!(U.gp_settings & GP_PAINT_DOSMOOTH) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT))
401                 return;
402         
403         /* don't try if less than 2 points in buffer */
404         if ((cmx <= 2) || (gpd->sbuffer == NULL))
405                 return;
406         
407         /* create a temporary smoothing coordinates buffer, use to store calculated values to prevent sequential error */
408         smoothArray = MEM_callocN(sizeof(tGpSmoothCo)*cmx, "gp_stroke_smooth smoothArray");
409         
410         /* first pass: calculate smoothing coordinates using weighted-averages */
411         for (i=0, spc=smoothArray; i < gpd->sbuffer_size; i++, spc++) {
412                 const tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
413                 const tGPspoint *pb= (i-1 > 0)?(pc-1):(pc);
414                 const tGPspoint *pa= (i-2 > 0)?(pc-2):(pb);
415                 const tGPspoint *pd= (i+1 < cmx)?(pc+1):(pc);
416                 const tGPspoint *pe= (i+2 < cmx)?(pc+2):(pd);
417                 
418                 spc->x= (int)(0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x);
419                 spc->y= (int)(0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y);
420         }
421         
422         /* second pass: apply smoothed coordinates */
423         for (i=0, spc=smoothArray; i < gpd->sbuffer_size; i++, spc++) {
424                 tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
425                 
426                 pc->x = spc->x;
427                 pc->y = spc->y;
428         }
429         
430         /* free temp array */
431         MEM_freeN(smoothArray);
432 }
433
434 /* simplify a stroke (in buffer) before storing it 
435  *      - applies a reverse Chaikin filter
436  *      - code adapted from etch-a-ton branch (editarmature_sketch.c)
437  */
438 static void gp_stroke_simplify (tGPsdata *p)
439 {
440         bGPdata *gpd= p->gpd;
441         tGPspoint *old_points= (tGPspoint *)gpd->sbuffer;
442         short num_points= gpd->sbuffer_size;
443         short flag= gpd->sbuffer_sflag;
444         short i, j;
445         
446         /* only simplify if simplification is enabled, and we're not doing a straight line */
447         if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT))
448                 return;
449         
450         /* don't simplify if less than 4 points in buffer */
451         if ((num_points <= 4) || (old_points == NULL))
452                 return;
453                 
454         /* clear buffer (but don't free mem yet) so that we can write to it 
455          *      - firstly set sbuffer to NULL, so a new one is allocated
456          *      - secondly, reset flag after, as it gets cleared auto
457          */
458         gpd->sbuffer= NULL;
459         gp_session_validatebuffer(p);
460         gpd->sbuffer_sflag = flag;
461         
462 /* macro used in loop to get position of new point
463  *      - used due to the mixture of datatypes in use here
464  */
465 #define GP_SIMPLIFY_AVPOINT(offs, sfac) \
466         { \
467                 co[0] += (float)(old_points[offs].x * sfac); \
468                 co[1] += (float)(old_points[offs].y * sfac); \
469                 pressure += old_points[offs].pressure * sfac; \
470         }
471         
472         for (i = 0, j = 0; i < num_points; i++)
473         {
474                 if (i - j == 3)
475                 {
476                         float co[2], pressure;
477                         int mco[2];
478                         
479                         /* initialise values */
480                         co[0]= 0;
481                         co[1]= 0;
482                         pressure = 0;
483                         
484                         /* using macro, calculate new point */
485                         GP_SIMPLIFY_AVPOINT(j, -0.25f);
486                         GP_SIMPLIFY_AVPOINT(j+1, 0.75f);
487                         GP_SIMPLIFY_AVPOINT(j+2, 0.75f);
488                         GP_SIMPLIFY_AVPOINT(j+3, -0.25f);
489                         
490                         /* set values for adding */
491                         mco[0]= (int)co[0];
492                         mco[1]= (int)co[1];
493                         
494                         /* ignore return values on this... assume to be ok for now */
495                         gp_stroke_addpoint(p, mco, pressure);
496                         
497                         j += 2;
498                 }
499         } 
500         
501         /* free old buffer */
502         MEM_freeN(old_points);
503 }
504
505
506 /* make a new stroke from the buffer data */
507 static void gp_stroke_newfrombuffer (tGPsdata *p)
508 {
509         bGPdata *gpd= p->gpd;
510         bGPDstroke *gps;
511         bGPDspoint *pt;
512         tGPspoint *ptc;
513         int i, totelem;
514         /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */
515         int depth_margin = (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 4 : 0;
516         
517         /* get total number of points to allocate space for 
518          *      - drawing straight-lines only requires the endpoints
519          */
520         if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)
521                 totelem = (gpd->sbuffer_size >= 2) ? 2: gpd->sbuffer_size;
522         else
523                 totelem = gpd->sbuffer_size;
524         
525         /* exit with error if no valid points from this stroke */
526         if (totelem == 0) {
527                 if (G.f & G_DEBUG) 
528                         printf("Error: No valid points in stroke buffer to convert (tot=%d) \n", gpd->sbuffer_size);
529                 return;
530         }
531         
532         /* allocate memory for a new stroke */
533         gps= MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
534         
535         /* allocate enough memory for a continuous array for storage points */
536         pt= gps->points= MEM_callocN(sizeof(bGPDspoint)*totelem, "gp_stroke_points");
537         
538         /* copy appropriate settings for stroke */
539         gps->totpoints= totelem;
540         gps->thickness= p->gpl->thickness;
541         gps->flag= gpd->sbuffer_sflag;
542         
543         /* copy points from the buffer to the stroke */
544         if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
545                 /* straight lines only -> only endpoints */
546                 {
547                         /* first point */
548                         ptc= gpd->sbuffer;
549                         
550                         /* convert screen-coordinates to appropriate coordinates (and store them) */
551                         gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
552                         
553                         /* copy pressure */
554                         pt->pressure= ptc->pressure;
555                         
556                         pt++;
557                 }
558                         
559                 if (totelem == 2) {
560                         /* last point if applicable */
561                         ptc= ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1);
562                         
563                         /* convert screen-coordinates to appropriate coordinates (and store them) */
564                         gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
565                         
566                         /* copy pressure */
567                         pt->pressure= ptc->pressure;
568                 }
569         }
570         else {
571                 float *depth_arr= NULL;
572                 
573                 /* get an array of depths, far depths are blended */
574                 if (gpencil_project_check(p)) {
575                         int mval[2], mval_prev[2]= {0};
576                         int interp_depth = 0;
577                         int found_depth = 0;
578                         
579                         depth_arr= MEM_mallocN(sizeof(float) * gpd->sbuffer_size, "depth_points");
580
581                         for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size; i++, ptc++, pt++) {
582                                 mval[0]= ptc->x; mval[1]= ptc->y;
583
584                                 if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr+i) == 0) &&
585                                         (i && (ED_view3d_autodist_depth_seg(p->ar, mval, mval_prev, depth_margin + 1, depth_arr+i) == 0))
586                                 ) {
587                                         interp_depth= TRUE;
588                                 }
589                                 else {
590                                         found_depth= TRUE;
591                                 }
592
593                                 VECCOPY2D(mval_prev, mval);
594                         }
595                         
596                         if (found_depth == FALSE) {
597                                 /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */
598                                 for (i=gpd->sbuffer_size-1; i >= 0; i--)
599                                         depth_arr[i] = 0.9999f;
600                         }
601                         else {
602                                 if (p->gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS) {
603                                         /* remove all info between the valid endpoints */
604                                         int first_valid = 0;
605                                         int last_valid = 0;
606                                         
607                                         for (i=0; i < gpd->sbuffer_size; i++) {
608                                                 if (depth_arr[i] != FLT_MAX)
609                                                         break;
610                                         }
611                                         first_valid= i;
612                                         
613                                         for (i=gpd->sbuffer_size-1; i >= 0; i--) {
614                                                 if (depth_arr[i] != FLT_MAX)
615                                                         break;
616                                         }
617                                         last_valid= i;
618                                         
619                                         /* invalidate non-endpoints, so only blend between first and last */
620                                         for (i=first_valid+1; i < last_valid; i++)
621                                                 depth_arr[i]= FLT_MAX;
622                                         
623                                         interp_depth= TRUE;
624                                 }
625                                 
626                                 if (interp_depth) {
627                                         interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX);
628                                 }
629                         }
630                 }
631                 
632                 
633                 pt= gps->points;
634                 
635                 /* convert all points (normal behaviour) */
636                 for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) {
637                         /* convert screen-coordinates to appropriate coordinates (and store them) */
638                         gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr+i:NULL);
639                         
640                         /* copy pressure */
641                         pt->pressure= ptc->pressure;
642                 }
643                 
644                 if (depth_arr)
645                         MEM_freeN(depth_arr);
646         }
647         
648         /* add stroke to frame */
649         BLI_addtail(&p->gpf->strokes, gps);
650 }
651
652 /* --- 'Eraser' for 'Paint' Tool ------ */
653
654 /* eraser tool - remove segment from stroke/split stroke (after lasso inside) */
655 static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i)
656 {
657         bGPDspoint *pt_tmp= gps->points;
658         bGPDstroke *gsn = NULL;
659
660         /* if stroke only had two points, get rid of stroke */
661         if (gps->totpoints == 2) {
662                 /* free stroke points, then stroke */
663                 MEM_freeN(pt_tmp);
664                 BLI_freelinkN(&gpf->strokes, gps);
665                 
666                 /* nothing left in stroke, so stop */
667                 return 1;
668         }
669
670         /* if last segment, just remove segment from the stroke */
671         else if (i == gps->totpoints - 2) {
672                 /* allocate new points array, and assign most of the old stroke there */
673                 gps->totpoints--;
674                 gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
675                 memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*gps->totpoints);
676                 
677                 /* free temp buffer */
678                 MEM_freeN(pt_tmp);
679                 
680                 /* nothing left in stroke, so stop */
681                 return 1;
682         }
683
684         /* if first segment, just remove segment from the stroke */
685         else if (i == 0) {
686                 /* allocate new points array, and assign most of the old stroke there */
687                 gps->totpoints--;
688                 gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
689                 memcpy(gps->points, pt_tmp + 1, sizeof(bGPDspoint)*gps->totpoints);
690                 
691                 /* free temp buffer */
692                 MEM_freeN(pt_tmp);
693                 
694                 /* no break here, as there might still be stuff to remove in this stroke */
695                 return 0;
696         }
697
698         /* segment occurs in 'middle' of stroke, so split */
699         else {
700                 /* duplicate stroke, and assign 'later' data to that stroke */
701                 gsn= MEM_dupallocN(gps);
702                 gsn->prev= gsn->next= NULL;
703                 BLI_insertlinkafter(&gpf->strokes, gps, gsn);
704                 
705                 gsn->totpoints= gps->totpoints - i;
706                 gsn->points= MEM_callocN(sizeof(bGPDspoint)*gsn->totpoints, "gp_stroke_points");
707                 memcpy(gsn->points, pt_tmp + i, sizeof(bGPDspoint)*gsn->totpoints);
708                 
709                 /* adjust existing stroke  */
710                 gps->totpoints= i;
711                 gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
712                 memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*i);
713                 
714                 /* free temp buffer */
715                 MEM_freeN(pt_tmp);
716                 
717                 /* nothing left in stroke, so stop */
718                 return 1;
719         }
720 }
721
722 /* eraser tool - check if part of stroke occurs within last segment drawn by eraser */
723 static short gp_stroke_eraser_strokeinside (int mval[], int UNUSED(mvalo[]), short rad, short x0, short y0, short x1, short y1)
724 {
725         /* simple within-radius check for now */
726         if (edge_inside_circle(mval[0], mval[1], rad, x0, y0, x1, y1))
727                 return 1;
728         
729         /* not inside */
730         return 0;
731
732
733 /* eraser tool - evaluation per stroke */
734 // TODO: this could really do with some optimisation (KD-Tree/BVH?)
735 static void gp_stroke_eraser_dostroke (tGPsdata *p, int mval[], int mvalo[], short rad, rcti *rect, bGPDframe *gpf, bGPDstroke *gps)
736 {
737         bGPDspoint *pt1, *pt2;
738         int x0=0, y0=0, x1=0, y1=0;
739         int xyval[2];
740         int i;
741         
742         if (gps->totpoints == 0) {
743                 /* just free stroke */
744                 if (gps->points) 
745                         MEM_freeN(gps->points);
746                 BLI_freelinkN(&gpf->strokes, gps);
747         }
748         else if (gps->totpoints == 1) {
749                 /* get coordinates */
750                 if (gps->flag & GP_STROKE_3DSPACE) {
751                         project_int(p->ar, &gps->points->x, xyval);
752                         x0= xyval[0];
753                         y0= xyval[1];
754                 }
755                 else if (gps->flag & GP_STROKE_2DSPACE) {                       
756                         UI_view2d_view_to_region(p->v2d, gps->points->x, gps->points->y, &x0, &y0);
757                 }
758 #if 0
759                 else if (gps->flag & GP_STROKE_2DIMAGE) {                       
760                         int offsx, offsy, sizex, sizey;
761                         
762                         /* get stored settings */
763                         sizex= p->im2d_settings.sizex;
764                         sizey= p->im2d_settings.sizey;
765                         offsx= p->im2d_settings.offsx;
766                         offsy= p->im2d_settings.offsy;
767                         
768                         /* calculate new points */
769                         x0= (int)((gps->points->x * sizex) + offsx);
770                         y0= (int)((gps->points->y * sizey) + offsy);
771                 }
772 #endif
773                 else {
774                         if (p->subrect == NULL) { /* normal 3D view */
775                                 x0= (int)(gps->points->x / 100 * p->ar->winx);
776                                 y0= (int)(gps->points->y / 100 * p->ar->winy);
777                         }
778                         else { /* camera view, use subrect */
779                                 x0= (int)((gps->points->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin;
780                                 y0= (int)((gps->points->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin;
781                         }
782                 }
783                 
784                 /* do boundbox check first */
785                 if (BLI_in_rcti(rect, x0, y0)) {
786                         /* only check if point is inside */
787                         if ( ((x0-mval[0])*(x0-mval[0]) + (y0-mval[1])*(y0-mval[1])) <= rad*rad ) {
788                                 /* free stroke */
789                                 MEM_freeN(gps->points);
790                                 BLI_freelinkN(&gpf->strokes, gps);
791                         }
792                 }
793         }
794         else {  
795                 /* loop over the points in the stroke, checking for intersections 
796                  *      - an intersection will require the stroke to be split
797                  */
798                 for (i=0; (i+1) < gps->totpoints; i++) {
799                         /* get points to work with */
800                         pt1= gps->points + i;
801                         pt2= gps->points + i + 1;
802                         
803                         /* get coordinates */
804                         if (gps->flag & GP_STROKE_3DSPACE) {
805                                 project_int(p->ar, &pt1->x, xyval);
806                                 x0= xyval[0];
807                                 y0= xyval[1];
808                                 
809                                 project_int(p->ar, &pt2->x, xyval);
810                                 x1= xyval[0];
811                                 y1= xyval[1];
812                         }
813                         else if (gps->flag & GP_STROKE_2DSPACE) {
814                                 UI_view2d_view_to_region(p->v2d, pt1->x, pt1->y, &x0, &y0);
815                                 
816                                 UI_view2d_view_to_region(p->v2d, pt2->x, pt2->y, &x1, &y1);
817                         }
818 #if 0
819                         else if (gps->flag & GP_STROKE_2DIMAGE) {
820                                 int offsx, offsy, sizex, sizey;
821                                 
822                                 /* get stored settings */
823                                 sizex= p->im2d_settings.sizex;
824                                 sizey= p->im2d_settings.sizey;
825                                 offsx= p->im2d_settings.offsx;
826                                 offsy= p->im2d_settings.offsy;
827                                 
828                                 /* calculate new points */
829                                 x0= (int)((pt1->x * sizex) + offsx);
830                                 y0= (int)((pt1->y * sizey) + offsy);
831                                 
832                                 x1= (int)((pt2->x * sizex) + offsx);
833                                 y1= (int)((pt2->y * sizey) + offsy);
834                         }
835 #endif
836                         else {
837                                 if(p->subrect == NULL) { /* normal 3D view */
838                                         x0= (int)(pt1->x / 100 * p->ar->winx);
839                                         y0= (int)(pt1->y / 100 * p->ar->winy);
840                                         x1= (int)(pt2->x / 100 * p->ar->winx);
841                                         y1= (int)(pt2->y / 100 * p->ar->winy);
842                                 }
843                                 else { /* camera view, use subrect */ 
844                                         x0= (int)((pt1->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin;
845                                         y0= (int)((pt1->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin;
846                                         x1= (int)((pt2->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin;
847                                         y1= (int)((pt2->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin;
848                                 }
849                         }
850                         
851                         /* check that point segment of the boundbox of the eraser stroke */
852                         if (BLI_in_rcti(rect, x0, y0) || BLI_in_rcti(rect, x1, y1)) {
853                                 /* check if point segment of stroke had anything to do with
854                                  * eraser region  (either within stroke painted, or on its lines)
855                                  *      - this assumes that linewidth is irrelevant
856                                  */
857                                 if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) {
858                                         /* if function returns true, break this loop (as no more point to check) */
859                                         if (gp_stroke_eraser_splitdel(gpf, gps, i))
860                                                 break;
861                                 }
862                         }
863                 }
864         }
865 }
866
867 /* erase strokes which fall under the eraser strokes */
868 static void gp_stroke_doeraser (tGPsdata *p)
869 {
870         bGPDframe *gpf= p->gpf;
871         bGPDstroke *gps, *gpn;
872         rcti rect;
873         
874         /* rect is rectangle of eraser */
875         rect.xmin= p->mval[0] - p->radius;
876         rect.ymin= p->mval[1] - p->radius;
877         rect.xmax= p->mval[0] + p->radius;
878         rect.ymax= p->mval[1] + p->radius;
879         
880         /* loop over strokes, checking segments for intersections */
881         for (gps= gpf->strokes.first; gps; gps= gpn) {
882                 gpn= gps->next;
883                 gp_stroke_eraser_dostroke(p, p->mval, p->mvalo, p->radius, &rect, gpf, gps);
884         }
885 }
886
887 /* ******************************************* */
888 /* Sketching Operator */
889
890 /* clear the session buffers (call this before AND after a paint operation) */
891 static void gp_session_validatebuffer (tGPsdata *p)
892 {
893         bGPdata *gpd= p->gpd;
894         
895         /* clear memory of buffer (or allocate it if starting a new session) */
896         if (gpd->sbuffer)
897                 memset(gpd->sbuffer, 0, sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX);
898         else
899                 gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
900         
901         /* reset indices */
902         gpd->sbuffer_size = 0;
903         
904         /* reset flags */
905         gpd->sbuffer_sflag= 0;
906 }
907
908 /* init new painting session */
909 static tGPsdata *gp_session_initpaint (bContext *C)
910 {
911         tGPsdata *p = NULL;
912         bGPdata **gpd_ptr = NULL;
913         ScrArea *curarea= CTX_wm_area(C);
914         ARegion *ar= CTX_wm_region(C);
915         
916         /* make sure the active view (at the starting time) is a 3d-view */
917         if (curarea == NULL) {
918                 if (G.f & G_DEBUG) 
919                         printf("Error: No active view for painting \n");
920                 return NULL;
921         }
922         
923         /* create new context data */
924         p= MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data");
925         
926         /* pass on current scene and window */
927         p->scene= CTX_data_scene(C);
928         p->win= CTX_wm_window(C);
929         
930         switch (curarea->spacetype) {
931                 /* supported views first */
932                 case SPACE_VIEW3D:
933                 {
934                         // View3D *v3d= curarea->spacedata.first;
935                         // RegionView3D *rv3d= ar->regiondata;
936                         
937                         /* set current area 
938                          *      - must verify that region data is 3D-view (and not something else)
939                          */
940                         p->sa= curarea;
941                         p->ar= ar;
942                         
943                         if (ar->regiondata == NULL) {
944                                 p->status= GP_STATUS_ERROR;
945                                 if (G.f & G_DEBUG)
946                                         printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable \n");
947                                 return p;
948                         }
949
950 #if 0 // XXX will this sort of antiquated stuff be restored?
951                         /* check that gpencil data is allowed to be drawn */
952                         if ((v3d->flag2 & V3D_DISPGP)==0) {
953                                 p->status= GP_STATUS_ERROR;
954                                 if (G.f & G_DEBUG) 
955                                         printf("Error: In active view, Grease Pencil not shown \n");
956                                 return p;
957                         }
958 #endif
959                 }
960                         break;
961
962                 case SPACE_NODE:
963                 {
964                         //SpaceNode *snode= curarea->spacedata.first;
965                         
966                         /* set current area */
967                         p->sa= curarea;
968                         p->ar= ar;
969                         p->v2d= &ar->v2d;
970                         
971 #if 0 // XXX will this sort of antiquated stuff be restored?
972                         /* check that gpencil data is allowed to be drawn */
973                         if ((snode->flag & SNODE_DISPGP)==0) {
974                                 p->status= GP_STATUS_ERROR;
975                                 if (G.f & G_DEBUG) 
976                                         printf("Error: In active view, Grease Pencil not shown \n");
977                                 return;
978                         }
979 #endif
980                 }
981                         break;
982 #if 0 // XXX these other spaces will come over time...
983                 case SPACE_SEQ:
984                 {
985                         SpaceSeq *sseq= curarea->spacedata.first;
986                         
987                         /* set current area */
988                         p->sa= curarea;
989                         p->ar= ar;
990                         p->v2d= &ar->v2d;
991                         
992                         /* check that gpencil data is allowed to be drawn */
993                         if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
994                                 p->status= GP_STATUS_ERROR;
995                                 if (G.f & G_DEBUG) 
996                                         printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil \n");
997                                 return;
998                         }
999                         if ((sseq->flag & SEQ_DRAW_GPENCIL)==0) {
1000                                 p->status= GP_STATUS_ERROR;
1001                                 if (G.f & G_DEBUG) 
1002                                         printf("Error: In active view, Grease Pencil not shown \n");
1003                                 return;
1004                         }
1005                 }
1006                         break;  
1007 #endif
1008                 case SPACE_IMAGE:
1009                 {
1010                         //SpaceImage *sima= curarea->spacedata.first;
1011                         
1012                         /* set the current area */
1013                         p->sa= curarea;
1014                         p->ar= ar;
1015                         p->v2d= &ar->v2d;
1016                         //p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
1017                         
1018 #if 0 // XXX disabled for now
1019                         /* check that gpencil data is allowed to be drawn */
1020                         if ((sima->flag & SI_DISPGP)==0) {
1021                                 p->status= GP_STATUS_ERROR;
1022                                 if (G.f & G_DEBUG)
1023                                         printf("Error: In active view, Grease Pencil not shown \n");
1024                                 return p;
1025                         }
1026 #endif
1027                 }
1028                         break;
1029                 case SPACE_CLIP:
1030                 {
1031                         //SpaceClip *sc= curarea->spacedata.first;
1032                         
1033                         /* set the current area */
1034                         p->sa= curarea;
1035                         p->ar= ar;
1036                         p->v2d= &ar->v2d;
1037                         //p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
1038                         
1039 #if 0 // XXX disabled for now
1040                         /* check that gpencil data is allowed to be drawn */
1041                         if ((sc->flag & SC_DISPGP)==0) {
1042                                 p->status= GP_STATUS_ERROR;
1043                                 if (G.f & G_DEBUG)
1044                                         printf("Error: In active view, Grease Pencil not shown \n");
1045                                 return p;
1046                         }
1047 #endif
1048                 }
1049                         break;
1050                         
1051                 /* unsupported views */
1052                 default:
1053                 {
1054                         p->status= GP_STATUS_ERROR;
1055                         if (G.f & G_DEBUG) 
1056                                 printf("Error: Active view not appropriate for Grease Pencil drawing \n");
1057                         return p;
1058                 }
1059                         break;
1060         }
1061         
1062         /* get gp-data */
1063         gpd_ptr= gpencil_data_get_pointers(C, &p->ownerPtr);
1064         if (gpd_ptr == NULL) {
1065                 p->status= GP_STATUS_ERROR;
1066                 if (G.f & G_DEBUG)
1067                         printf("Error: Current context doesn't allow for any Grease Pencil data \n");
1068                 return p;
1069         }
1070         else {
1071                 /* if no existing GPencil block exists, add one */
1072                 if (*gpd_ptr == NULL)
1073                         *gpd_ptr= gpencil_data_addnew("GPencil");
1074                 p->gpd= *gpd_ptr;
1075         }
1076         
1077         /* set edit flags - so that buffer will get drawn */
1078         G.f |= G_GREASEPENCIL;
1079         
1080         /* clear out buffer (stored in gp-data), in case something contaminated it */
1081         gp_session_validatebuffer(p);
1082         
1083 #if 0
1084         /* set 'default' im2d_settings just in case something that uses this doesn't set it */
1085         p->im2d_settings.sizex= 1;
1086         p->im2d_settings.sizey= 1;
1087 #endif
1088         
1089         /* return context data for running paint operator */
1090         return p;
1091 }
1092
1093 /* cleanup after a painting session */
1094 static void gp_session_cleanup (tGPsdata *p)
1095 {
1096         bGPdata *gpd= (p) ? p->gpd : NULL;
1097         
1098         /* error checking */
1099         if (gpd == NULL)
1100                 return;
1101         
1102         /* free stroke buffer */
1103         if (gpd->sbuffer) {
1104                 MEM_freeN(gpd->sbuffer);
1105                 gpd->sbuffer= NULL;
1106         }
1107         
1108         /* clear flags */
1109         gpd->sbuffer_size= 0;
1110         gpd->sbuffer_sflag= 0;
1111 }
1112
1113 /* init new stroke */
1114 static void gp_paint_initstroke (tGPsdata *p, short paintmode)
1115 {       
1116         /* get active layer (or add a new one if non-existent) */
1117         p->gpl= gpencil_layer_getactive(p->gpd);
1118         if (p->gpl == NULL)
1119                 p->gpl= gpencil_layer_addnew(p->gpd);
1120         if (p->gpl->flag & GP_LAYER_LOCKED) {
1121                 p->status= GP_STATUS_ERROR;
1122                 if (G.f & G_DEBUG)
1123                         printf("Error: Cannot paint on locked layer \n");
1124                 return;
1125         }
1126                 
1127         /* get active frame (add a new one if not matching frame) */
1128         p->gpf= gpencil_layer_getframe(p->gpl, p->scene->r.cfra, 1);
1129         if (p->gpf == NULL) {
1130                 p->status= GP_STATUS_ERROR;
1131                 if (G.f & G_DEBUG) 
1132                         printf("Error: No frame created (gpencil_paint_init) \n");
1133                 return;
1134         }
1135         else
1136                 p->gpf->flag |= GP_FRAME_PAINT;
1137         
1138         /* set 'eraser' for this stroke if using eraser */
1139         p->paintmode= paintmode;
1140         if (p->paintmode == GP_PAINTMODE_ERASER)
1141                 p->gpd->sbuffer_sflag |= GP_STROKE_ERASER;
1142                 
1143         /* set 'initial run' flag, which is only used to denote when a new stroke is starting */
1144         p->flags |= GP_PAINTFLAG_FIRSTRUN;
1145         
1146
1147         /* when drawing in the camera view, in 2D space, set the subrect */
1148         if (!(p->gpd->flag & GP_DATA_VIEWALIGN)) {
1149                 if (p->sa->spacetype == SPACE_VIEW3D) {
1150                         View3D *v3d= p->sa->spacedata.first;
1151                         RegionView3D *rv3d= p->ar->regiondata;
1152
1153                         /* for camera view set the subrect */
1154                         if (rv3d->persp == RV3D_CAMOB) {
1155                                 ED_view3d_calc_camera_border(p->scene, p->ar, v3d, rv3d, &p->subrect_data, -1); /* negative shift */
1156                                 p->subrect= &p->subrect_data;
1157                         }
1158                 }
1159         }
1160
1161         /* check if points will need to be made in view-aligned space */
1162         if (p->gpd->flag & GP_DATA_VIEWALIGN) {
1163                 switch (p->sa->spacetype) {
1164                         case SPACE_VIEW3D:
1165                         {
1166                                 RegionView3D *rv3d= p->ar->regiondata;
1167                                 float rvec[3];
1168                                 
1169                                 /* get reference point for 3d space placement */
1170                                 gp_get_3d_reference(p, rvec);
1171                                 initgrabz(rv3d, rvec[0], rvec[1], rvec[2]);
1172                                 
1173                                 p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE;
1174                         }
1175                                 break;
1176                         
1177                         case SPACE_NODE:
1178                         {
1179                                 p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
1180                         }
1181                                 break;
1182 #if 0 // XXX other spacetypes to be restored in due course
1183                         case SPACE_SEQ:
1184                         {
1185                                 SpaceSeq *sseq= (SpaceSeq *)p->sa->spacedata.first;
1186                                 int rectx, recty;
1187                                 float zoom, zoomx, zoomy;
1188                                 
1189                                 /* set draw 2d-stroke flag */
1190                                 p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE;
1191                                 
1192                                 /* calculate zoom factor */
1193                                 zoom= (float)(SEQ_ZOOM_FAC(sseq->zoom));
1194                                 if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
1195                                         zoomx = zoom * (p->scene->r.xasp / p->scene->r.yasp);
1196                                         zoomy = zoom;
1197                                 } 
1198                                 else
1199                                         zoomx = zoomy = zoom;
1200                                 
1201                                 /* calculate rect size to use to calculate the size of the drawing area
1202                                  *      - We use the size of the output image not the size of the ibuf being shown
1203                                  *        as it is too messy getting the ibuf (and could be too slow). This should be
1204                                  *        a reasonable for most cases anyway.
1205                                  */
1206                                 rectx= (p->scene->r.size * p->scene->r.xsch) / 100;
1207                                 recty= (p->scene->r.size * p->scene->r.ysch) / 100; 
1208                                 
1209                                 /* set offset and scale values for opertations to use */
1210                                 p->im2d_settings.sizex= (int)(zoomx * rectx);
1211                                 p->im2d_settings.sizey= (int)(zoomy * recty);
1212                                 p->im2d_settings.offsx= (int)((p->sa->winx-p->im2d_settings.sizex)/2 + sseq->xof);
1213                                 p->im2d_settings.offsy= (int)((p->sa->winy-p->im2d_settings.sizey)/2 + sseq->yof);
1214                         }
1215                                 break;
1216 #endif
1217                         case SPACE_IMAGE:
1218                         {
1219                                 SpaceImage *sima= (SpaceImage *)p->sa->spacedata.first;
1220                                 
1221                                 /* only set these flags if the image editor doesn't have an image active,
1222                                  * otherwise user will be confused by strokes not appearing after they're drawn
1223                                  *
1224                                  * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint!
1225                                  */
1226                                 if ELEM(NULL, sima, sima->image) {
1227                                         /* make strokes be drawn in screen space */
1228                                         p->gpd->sbuffer_sflag &= ~GP_STROKE_2DSPACE;
1229                                         p->gpd->flag &= ~GP_DATA_VIEWALIGN;
1230                                 }       
1231                                 else
1232                                         p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
1233                         }
1234                                 break;
1235                                 
1236                         case SPACE_CLIP:
1237                         {
1238                                 p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
1239                         }
1240                                 break;
1241                 }
1242         }
1243 }
1244
1245 /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
1246 static void gp_paint_strokeend (tGPsdata *p)
1247 {
1248         /* for surface sketching, need to set the right OpenGL context stuff so that 
1249          * the conversions will project the values correctly...
1250          */
1251         if (gpencil_project_check(p)) {
1252                 View3D *v3d= p->sa->spacedata.first;
1253                 
1254                 /* need to restore the original projection settings before packing up */
1255                 view3d_region_operator_needs_opengl(p->win, p->ar);
1256                 ED_view3d_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1:0);
1257         }
1258         
1259         /* check if doing eraser or not */
1260         if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) {
1261                 /* smooth stroke before transferring? */
1262                 gp_stroke_smooth(p);
1263                 
1264                 /* simplify stroke before transferring? */
1265                 gp_stroke_simplify(p);
1266                 
1267                 /* transfer stroke to frame */
1268                 gp_stroke_newfrombuffer(p);
1269         }
1270         
1271         /* clean up buffer now */
1272         gp_session_validatebuffer(p);
1273 }
1274
1275 /* finish off stroke painting operation */
1276 static void gp_paint_cleanup (tGPsdata *p)
1277 {
1278         /* finish off a stroke */
1279         gp_paint_strokeend(p);
1280         
1281         /* "unlock" frame */
1282         if (p->gpf)
1283                 p->gpf->flag &= ~GP_FRAME_PAINT;
1284 }
1285
1286 /* ------------------------------- */
1287
1288 static void gpencil_draw_exit (bContext *C, wmOperator *op)
1289 {
1290         tGPsdata *p= op->customdata;
1291         
1292         /* clear edit flags */
1293         G.f &= ~G_GREASEPENCIL;
1294         
1295         /* restore cursor to indicate end of drawing */
1296         WM_cursor_restore(CTX_wm_window(C));
1297         
1298         /* don't assume that operator data exists at all */
1299         if (p) {
1300                 /* check size of buffer before cleanup, to determine if anything happened here */
1301                 if (p->paintmode == GP_PAINTMODE_ERASER) {
1302                         // TODO clear radial cursor thing
1303                         // XXX draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0);
1304                 }
1305                 
1306                 /* cleanup */
1307                 gp_paint_cleanup(p);
1308                 gp_session_cleanup(p);
1309                 
1310                 /* finally, free the temp data */
1311                 MEM_freeN(p);   
1312         }
1313         
1314         op->customdata= NULL;
1315 }
1316
1317 static int gpencil_draw_cancel (bContext *C, wmOperator *op)
1318 {
1319         /* this is just a wrapper around exit() */
1320         gpencil_draw_exit(C, op);
1321         return OPERATOR_CANCELLED;
1322 }
1323
1324 /* ------------------------------- */
1325
1326
1327 static int gpencil_draw_init (bContext *C, wmOperator *op)
1328 {
1329         tGPsdata *p;
1330         int paintmode= RNA_enum_get(op->ptr, "mode");
1331         
1332         /* check context */
1333         p= op->customdata= gp_session_initpaint(C);
1334         if ((p == NULL) || (p->status == GP_STATUS_ERROR)) {
1335                 /* something wasn't set correctly in context */
1336                 gpencil_draw_exit(C, op);
1337                 return 0;
1338         }
1339         
1340         /* init painting data */
1341         gp_paint_initstroke(p, paintmode);
1342         if (p->status == GP_STATUS_ERROR) {
1343                 gpencil_draw_exit(C, op);
1344                 return 0;
1345         }
1346         
1347         /* radius for eraser circle is defined in userprefs now */
1348         p->radius= U.gp_eraser;
1349         
1350         /* everything is now setup ok */
1351         return 1;
1352 }
1353
1354 /* ------------------------------- */
1355
1356 /* update UI indicators of status, including cursor and header prints */
1357 static void gpencil_draw_status_indicators (tGPsdata *p)
1358 {
1359         /* header prints */
1360         switch (p->status) {
1361                 case GP_STATUS_PAINTING:
1362                         /* only print this for paint-sessions, otherwise it gets annoying */
1363                         if (GPENCIL_SKETCH_SESSIONS_ON(p->scene))
1364                                 ED_area_headerprint(p->sa, "Grease Pencil: Drawing/erasing stroke... Release to end stroke");
1365                         break;
1366                 
1367                 case GP_STATUS_IDLING:
1368                         /* print status info */
1369                         switch (p->paintmode) {
1370                                 case GP_PAINTMODE_ERASER:
1371                                         ED_area_headerprint(p->sa, "Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | ESC/Enter to end");
1372                                         break;
1373                                 case GP_PAINTMODE_DRAW_STRAIGHT:
1374                                         ED_area_headerprint(p->sa, "Grease Pencil Line Session: Hold and drag LMB to draw | ESC/Enter to end");
1375                                         break;
1376                                 case GP_PAINTMODE_DRAW:
1377                                         ED_area_headerprint(p->sa, "Grease Pencil Freehand Session: Hold and drag LMB to draw | ESC/Enter to end");
1378                                         break;
1379                                         
1380                                 default: /* unhandled future cases */
1381                                         ED_area_headerprint(p->sa, "Grease Pencil Session: ESC/Enter to end");
1382                                         break;
1383                         }
1384                         break;
1385                         
1386                 case GP_STATUS_ERROR:
1387                 case GP_STATUS_DONE:
1388                         /* clear status string */
1389                         ED_area_headerprint(p->sa, NULL);
1390                         break;
1391         }
1392 }
1393
1394 /* ------------------------------- */
1395
1396 /* create a new stroke point at the point indicated by the painting context */
1397 static void gpencil_draw_apply (wmOperator *op, tGPsdata *p)
1398 {
1399         /* handle drawing/erasing -> test for erasing first */
1400         if (p->paintmode == GP_PAINTMODE_ERASER) {
1401                 /* do 'live' erasing now */
1402                 gp_stroke_doeraser(p);
1403                 
1404                 /* store used values */
1405                 p->mvalo[0]= p->mval[0];
1406                 p->mvalo[1]= p->mval[1];
1407                 p->opressure= p->pressure;
1408         }
1409         /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */
1410         else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) {
1411                 /* try to add point */
1412                 short ok= gp_stroke_addpoint(p, p->mval, p->pressure);
1413                 
1414                 /* handle errors while adding point */
1415                 if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
1416                         /* finish off old stroke */
1417                         gp_paint_strokeend(p);
1418                         
1419                         /* start a new stroke, starting from previous point */
1420                         gp_stroke_addpoint(p, p->mvalo, p->opressure);
1421                         ok= gp_stroke_addpoint(p, p->mval, p->pressure);
1422                 }
1423                 else if (ok == GP_STROKEADD_INVALID) {
1424                         /* the painting operation cannot continue... */
1425                         BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
1426                         p->status = GP_STATUS_ERROR;
1427                         
1428                         if (G.f & G_DEBUG) 
1429                                 printf("Error: Grease-Pencil Paint - Add Point Invalid \n");
1430                         return;
1431                 }
1432                 
1433                 /* store used values */
1434                 p->mvalo[0]= p->mval[0];
1435                 p->mvalo[1]= p->mval[1];
1436                 p->opressure= p->pressure;
1437         }
1438 }
1439
1440 /* handle draw event */
1441 static void gpencil_draw_apply_event (wmOperator *op, wmEvent *event)
1442 {
1443         tGPsdata *p= op->customdata;
1444         PointerRNA itemptr;
1445         float mousef[2];
1446         int tablet=0;
1447
1448         /* convert from window-space to area-space mouse coordintes */
1449         // NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding...
1450         p->mval[0]= event->mval[0] + 1;
1451         p->mval[1]= event->mval[1] + 1;
1452
1453         /* handle pressure sensitivity (which is supplied by tablets) */
1454         if (event->custom == EVT_DATA_TABLET) {
1455                 wmTabletData *wmtab= event->customdata;
1456                 
1457                 tablet= (wmtab->Active != EVT_TABLET_NONE);
1458                 p->pressure= wmtab->Pressure;
1459                 
1460                 //if (wmtab->Active == EVT_TABLET_ERASER)
1461                         // TODO... this should get caught by the keymaps which call drawing in the first place
1462         }
1463         else
1464                 p->pressure= 1.0f;
1465         
1466         /* fill in stroke data (not actually used directly by gpencil_draw_apply) */
1467         RNA_collection_add(op->ptr, "stroke", &itemptr);
1468         
1469         mousef[0]= p->mval[0];
1470         mousef[1]= p->mval[1];
1471         RNA_float_set_array(&itemptr, "mouse", mousef);
1472         RNA_float_set(&itemptr, "pressure", p->pressure);
1473         RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN));
1474         
1475         /* special exception for start of strokes (i.e. maybe for just a dot) */
1476         if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
1477                 p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
1478                 
1479                 p->mvalo[0]= p->mval[0];
1480                 p->mvalo[1]= p->mval[1];
1481                 p->opressure= p->pressure;
1482                 
1483                 /* special exception here for too high pressure values on first touch in
1484                  *  windows for some tablets, then we just skip first touch ..  
1485                  */
1486                 if (tablet && (p->pressure >= 0.99f))
1487                         return;
1488         }
1489         
1490         /* apply the current latest drawing point */
1491         gpencil_draw_apply(op, p);
1492         
1493         /* force refresh */
1494         ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */
1495 }
1496
1497 /* ------------------------------- */
1498
1499 /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */
1500 static int gpencil_draw_exec (bContext *C, wmOperator *op)
1501 {
1502         tGPsdata *p = NULL;
1503         
1504         //printf("GPencil - Starting Re-Drawing \n");
1505         
1506         /* try to initialise context data needed while drawing */
1507         if (!gpencil_draw_init(C, op)) {
1508                 if (op->customdata) MEM_freeN(op->customdata);
1509                 //printf("\tGP - no valid data \n");
1510                 return OPERATOR_CANCELLED;
1511         }
1512         else
1513                 p= op->customdata;
1514         
1515         //printf("\tGP - Start redrawing stroke \n");
1516         
1517         /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
1518          * setting the relevant values in context at each step, then applying
1519          */
1520         RNA_BEGIN(op->ptr, itemptr, "stroke") 
1521         {
1522                 float mousef[2];
1523                 
1524                 //printf("\t\tGP - stroke elem \n");
1525                 
1526                 /* get relevant data for this point from stroke */
1527                 RNA_float_get_array(&itemptr, "mouse", mousef);
1528                 p->mval[0] = (int)mousef[0];
1529                 p->mval[1] = (int)mousef[1];
1530                 p->pressure= RNA_float_get(&itemptr, "pressure");
1531                 
1532                 if (RNA_boolean_get(&itemptr, "is_start")) {
1533                         /* if first-run flag isn't set already (i.e. not true first stroke),
1534                          * then we must terminate the previous one first before continuing
1535                          */
1536                         if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
1537                                 // TODO: both of these ops can set error-status, but we probably don't need to worry
1538                                 gp_paint_strokeend(p);
1539                                 gp_paint_initstroke(p, p->paintmode);
1540                         }
1541                 }
1542                 
1543                 /* if first run, set previous data too */
1544                 if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
1545                         p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
1546                         
1547                         p->mvalo[0]= p->mval[0];
1548                         p->mvalo[1]= p->mval[1];
1549                         p->opressure= p->pressure;
1550                 }
1551                 
1552                 /* apply this data as necessary now (as per usual) */
1553                 gpencil_draw_apply(op, p);
1554         }
1555         RNA_END;
1556         
1557         //printf("\tGP - done \n");
1558         
1559         /* cleanup */
1560         gpencil_draw_exit(C, op);
1561         
1562         /* refreshes */
1563         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
1564         
1565         /* done */
1566         return OPERATOR_FINISHED;
1567 }
1568
1569 /* ------------------------------- */
1570
1571 /* start of interactive drawing part of operator */
1572 static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event)
1573 {
1574         tGPsdata *p = NULL;
1575         wmWindow *win= CTX_wm_window(C);
1576         
1577         if (G.f & G_DEBUG)
1578                 printf("GPencil - Starting Drawing \n");
1579         
1580         /* try to initialise context data needed while drawing */
1581         if (!gpencil_draw_init(C, op)) {
1582                 if (op->customdata) 
1583                         MEM_freeN(op->customdata);
1584                 if (G.f & G_DEBUG)
1585                         printf("\tGP - no valid data \n");
1586                 return OPERATOR_CANCELLED;
1587         }
1588         else
1589                 p= op->customdata;
1590         
1591         // TODO: set any additional settings that we can take from the events?
1592         // TODO? if tablet is erasing, force eraser to be on?
1593         
1594         // TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway...
1595         
1596         /* if eraser is on, draw radial aid */
1597         if (p->paintmode == GP_PAINTMODE_ERASER) {
1598                 // TODO: this involves mucking around with radial control, so we leave this for now..
1599         }
1600         
1601         /* set cursor */
1602         if (p->paintmode == GP_PAINTMODE_ERASER)
1603                 WM_cursor_modal(win, BC_CROSSCURSOR); // XXX need a better cursor
1604         else
1605                 WM_cursor_modal(win, BC_PAINTBRUSHCURSOR);
1606         
1607         /* special hack: if there was an initial event, then we were invoked via a hotkey, and 
1608          * painting should start immediately. Otherwise, this was called from a toolbar, in which
1609          * case we should wait for the mouse to be clicked.
1610          */
1611         if (event->type) {
1612                 /* hotkey invoked - start drawing */
1613                 //printf("\tGP - set first spot\n");
1614                 p->status= GP_STATUS_PAINTING;
1615                 
1616                 /* handle the initial drawing - i.e. for just doing a simple dot */
1617                 gpencil_draw_apply_event(op, event);
1618         }
1619         else {
1620                 /* toolbar invoked - don't start drawing yet... */
1621                 //printf("\tGP - hotkey invoked... waiting for click-drag\n");
1622         }
1623         
1624         /* add a modal handler for this operator, so that we can then draw continuous strokes */
1625         WM_event_add_modal_handler(C, op);
1626         return OPERATOR_RUNNING_MODAL;
1627 }
1628
1629 /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
1630 static int gpencil_area_exists(bContext *C, ScrArea *satest)
1631 {
1632         bScreen *sc= CTX_wm_screen(C);
1633         ScrArea *sa;
1634         
1635         for(sa= sc->areabase.first; sa; sa= sa->next)
1636                 if(sa==satest)
1637                         return 1;
1638         return 0;
1639 }
1640
1641 /* events handling during interactive drawing part of operator */
1642 static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
1643 {
1644         tGPsdata *p= op->customdata;
1645         //int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */
1646         /* currently, grease pencil conflicts with such operators as undo and set object mode
1647            which makes behavior of operator totally unpredictable and crash for some cases.
1648            the only way to solve this proper is to ger rid of pointers to data which can
1649            chage stored in operator custom data (sergey) */
1650         int estate = OPERATOR_RUNNING_MODAL;
1651         
1652         // if (event->type == NDOF_MOTION)
1653         //      return OPERATOR_PASS_THROUGH;
1654         // -------------------------------
1655         // [mce] Not quite what I was looking
1656         // for, but a good start! GP continues to
1657         // draw on the screen while the 3D mouse
1658         // moves the viewpoint. Problem is that
1659         // the stroke is converted to 3D only after
1660         // it is finished. This approach should work
1661         // better in tools that immediately apply
1662         // in 3D space.
1663
1664         //printf("\tGP - handle modal event...\n");
1665         
1666         /* exit painting mode (and/or end current stroke) */
1667         if (ELEM4(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) {
1668                 /* exit() ends the current stroke before cleaning up */
1669                 //printf("\t\tGP - end of paint op + end of stroke\n");
1670                 p->status= GP_STATUS_DONE;
1671                 estate = OPERATOR_FINISHED;
1672         }
1673         
1674         /* toggle painting mode upon mouse-button movement */
1675         if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE)) {
1676                 /* if painting, end stroke */
1677                 if (p->status == GP_STATUS_PAINTING) {
1678                         /* basically, this should be mouse-button up = end stroke 
1679                          * BUT what happens next depends on whether we 'painting sessions' is enabled
1680                          */
1681                         if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) {
1682                                 /* end stroke only, and then wait to resume painting soon */
1683                                 //printf("\t\tGP - end stroke only\n");
1684                                 gp_paint_cleanup(p);
1685                                 p->status= GP_STATUS_IDLING;
1686                                 
1687                                 /* we've just entered idling state, so this event was processed (but no others yet) */
1688                                 estate = OPERATOR_RUNNING_MODAL;
1689                         }
1690                         else {
1691                                 //printf("\t\tGP - end of stroke + op\n");
1692                                 p->status= GP_STATUS_DONE;
1693                                 estate = OPERATOR_FINISHED;
1694                         }
1695                 }
1696                 else {
1697                         /* not painting, so start stroke (this should be mouse-button down) */
1698                         
1699                         /* we must check that we're still within the area that we're set up to work from
1700                          * otherwise we could crash (see bug #20586)
1701                          */
1702                         if (CTX_wm_area(C) != p->sa) {
1703                                 //printf("\t\t\tGP - wrong area execution abort! \n");
1704                                 p->status= GP_STATUS_ERROR;
1705                                 estate = OPERATOR_CANCELLED;
1706                         }
1707                         else {
1708                                 //printf("\t\tGP - start stroke \n");
1709                                 p->status= GP_STATUS_PAINTING;
1710                                 
1711                                 /* we may need to set up paint env again if we're resuming */
1712                                 // XXX: watch it with the paintmode! in future, it'd be nice to allow changing paint-mode when in sketching-sessions
1713                                 // XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support
1714                                 gp_paint_initstroke(p, p->paintmode);
1715                                 
1716                                 if (p->status == GP_STATUS_ERROR) {
1717                                         estate = OPERATOR_CANCELLED;
1718                                 }
1719                         }
1720                 }
1721         }
1722         
1723         
1724         
1725         /* handle mode-specific events */
1726         if (p->status == GP_STATUS_PAINTING) {
1727                 /* handle painting mouse-movements? */
1728                 if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) 
1729                 {
1730                         /* handle drawing event */
1731                         //printf("\t\tGP - add point\n");
1732                         gpencil_draw_apply_event(op, event);
1733                         
1734                         /* finish painting operation if anything went wrong just now */
1735                         if (p->status == GP_STATUS_ERROR) {
1736                                 //printf("\t\t\t\tGP - add error done! \n");
1737                                 estate = OPERATOR_CANCELLED;
1738                         }
1739                         else {
1740                                 /* event handled, so just tag as running modal */
1741                                 //printf("\t\t\t\tGP - add point handled!\n");
1742                                 estate = OPERATOR_RUNNING_MODAL;
1743                         }
1744                 }
1745                 /* there shouldn't be any other events, but just in case there are, let's swallow them 
1746                  * (i.e. to prevent problems with with undo)
1747                  */
1748                 else {
1749                         /* swallow event to save ourselves trouble */
1750                         estate = OPERATOR_RUNNING_MODAL;
1751                 }
1752         }
1753         else if (p->status == GP_STATUS_IDLING) {
1754                 /* standard undo/redo shouldn't be allowed to execute or else it causes crashes, so catch it here */
1755                 // FIXME: this is a hardcoded hotkey that can't be changed
1756                 // TODO: catch redo as well, but how?
1757                 if (event->type == ZKEY && event->val == KM_RELEASE) {
1758                         /* oskey = cmd key on macs as they seem to use cmd-z for undo as well? */
1759                         if ((event->ctrl) || (event->oskey)) {
1760                                 /* just delete last stroke, which will look like undo to the end user */
1761                                 //printf("caught attempted undo event... deleting last stroke \n");
1762                                 gpencil_frame_delete_laststroke(p->gpl, p->gpf);
1763                                 /* undoing the last line can free p->gpf
1764                                  * note, could do this in a bit more of an elegant way then a search but it at least prevents a crash */
1765                                 if(BLI_findindex(&p->gpl->frames, p->gpf) == -1) {
1766                                         p->gpf= NULL;
1767                                 }
1768
1769                                 /* event handled, so force refresh */
1770                                 ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */
1771                                 estate = OPERATOR_RUNNING_MODAL; 
1772                         }
1773                 }
1774         }
1775         
1776         /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
1777         if(0==gpencil_area_exists(C, p->sa))
1778                 estate= OPERATOR_CANCELLED;
1779         else
1780                 /* update status indicators - cursor, header, etc. */
1781                 gpencil_draw_status_indicators(p);
1782         
1783         /* process last operations before exiting */
1784         switch (estate) {
1785                 case OPERATOR_FINISHED:
1786                         /* one last flush before we're done */
1787                         gpencil_draw_exit(C, op);
1788                         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
1789                         break;
1790                         
1791                 case OPERATOR_CANCELLED:
1792                         gpencil_draw_exit(C, op);
1793                         break;
1794
1795                 case OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH:
1796                         /* event doesn't need to be handled */
1797                         //printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE);
1798                         break;
1799         }
1800         
1801         /* return status code */
1802         return estate;
1803 }
1804
1805 /* ------------------------------- */
1806
1807 static EnumPropertyItem prop_gpencil_drawmodes[] = {
1808         {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", ""},
1809         {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 0, "Draw Straight Lines", ""},
1810         {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", ""},
1811         {0, NULL, 0, NULL, NULL}
1812 };
1813
1814 void GPENCIL_OT_draw (wmOperatorType *ot)
1815 {
1816         /* identifiers */
1817         ot->name= "Grease Pencil Draw";
1818         ot->idname= "GPENCIL_OT_draw";
1819         ot->description= "Make annotations on the active data";
1820         
1821         /* api callbacks */
1822         ot->exec= gpencil_draw_exec;
1823         ot->invoke= gpencil_draw_invoke;
1824         ot->modal= gpencil_draw_modal;
1825         ot->cancel= gpencil_draw_cancel;
1826         ot->poll= gpencil_draw_poll;
1827         
1828         /* flags */
1829         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
1830         
1831         /* settings for drawing */
1832         RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to intepret mouse movements.");
1833         
1834         RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1835 }