Merging r39191 through r39250 from trunk into soc-2011-tomato
[blender.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         
1647         // if (event->type == NDOF_MOTION)
1648         //      return OPERATOR_PASS_THROUGH;
1649         // -------------------------------
1650         // [mce] Not quite what I was looking
1651         // for, but a good start! GP continues to
1652         // draw on the screen while the 3D mouse
1653         // moves the viewpoint. Problem is that
1654         // the stroke is converted to 3D only after
1655         // it is finished. This approach should work
1656         // better in tools that immediately apply
1657         // in 3D space.
1658
1659         //printf("\tGP - handle modal event...\n");
1660         
1661         /* exit painting mode (and/or end current stroke) */
1662         if (ELEM4(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) {
1663                 /* exit() ends the current stroke before cleaning up */
1664                 //printf("\t\tGP - end of paint op + end of stroke\n");
1665                 p->status= GP_STATUS_DONE;
1666                 estate = OPERATOR_FINISHED;
1667         }
1668         
1669         /* toggle painting mode upon mouse-button movement */
1670         if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE)) {
1671                 /* if painting, end stroke */
1672                 if (p->status == GP_STATUS_PAINTING) {
1673                         /* basically, this should be mouse-button up = end stroke 
1674                          * BUT what happens next depends on whether we 'painting sessions' is enabled
1675                          */
1676                         if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) {
1677                                 /* end stroke only, and then wait to resume painting soon */
1678                                 //printf("\t\tGP - end stroke only\n");
1679                                 gp_paint_cleanup(p);
1680                                 p->status= GP_STATUS_IDLING;
1681                                 
1682                                 /* we've just entered idling state, so this event was processed (but no others yet) */
1683                                 estate = OPERATOR_RUNNING_MODAL;
1684                         }
1685                         else {
1686                                 //printf("\t\tGP - end of stroke + op\n");
1687                                 p->status= GP_STATUS_DONE;
1688                                 estate = OPERATOR_FINISHED;
1689                         }
1690                 }
1691                 else {
1692                         /* not painting, so start stroke (this should be mouse-button down) */
1693                         
1694                         /* we must check that we're still within the area that we're set up to work from
1695                          * otherwise we could crash (see bug #20586)
1696                          */
1697                         if (CTX_wm_area(C) != p->sa) {
1698                                 //printf("\t\t\tGP - wrong area execution abort! \n");
1699                                 p->status= GP_STATUS_ERROR;
1700                                 estate = OPERATOR_CANCELLED;
1701                         }
1702                         else {
1703                                 //printf("\t\tGP - start stroke \n");
1704                                 p->status= GP_STATUS_PAINTING;
1705                                 
1706                                 /* we may need to set up paint env again if we're resuming */
1707                                 // XXX: watch it with the paintmode! in future, it'd be nice to allow changing paint-mode when in sketching-sessions
1708                                 // XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support
1709                                 gp_paint_initstroke(p, p->paintmode);
1710                                 
1711                                 if (p->status == GP_STATUS_ERROR) {
1712                                         estate = OPERATOR_CANCELLED;
1713                                 }
1714                         }
1715                 }
1716         }
1717         
1718         
1719         
1720         /* handle mode-specific events */
1721         if (p->status == GP_STATUS_PAINTING) {
1722                 /* handle painting mouse-movements? */
1723                 if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) 
1724                 {
1725                         /* handle drawing event */
1726                         //printf("\t\tGP - add point\n");
1727                         gpencil_draw_apply_event(op, event);
1728                         
1729                         /* finish painting operation if anything went wrong just now */
1730                         if (p->status == GP_STATUS_ERROR) {
1731                                 //printf("\t\t\t\tGP - add error done! \n");
1732                                 estate = OPERATOR_CANCELLED;
1733                         }
1734                         else {
1735                                 /* event handled, so just tag as running modal */
1736                                 //printf("\t\t\t\tGP - add point handled!\n");
1737                                 estate = OPERATOR_RUNNING_MODAL;
1738                         }
1739                 }
1740                 /* there shouldn't be any other events, but just in case there are, let's swallow them 
1741                  * (i.e. to prevent problems with with undo)
1742                  */
1743                 else {
1744                         /* swallow event to save ourselves trouble */
1745                         estate = OPERATOR_RUNNING_MODAL;
1746                 }
1747         }
1748         else if (p->status == GP_STATUS_IDLING) {
1749                 /* standard undo/redo shouldn't be allowed to execute or else it causes crashes, so catch it here */
1750                 // FIXME: this is a hardcoded hotkey that can't be changed
1751                 // TODO: catch redo as well, but how?
1752                 if (event->type == ZKEY && event->val == KM_RELEASE) {
1753                         /* oskey = cmd key on macs as they seem to use cmd-z for undo as well? */
1754                         if ((event->ctrl) || (event->oskey)) {
1755                                 /* just delete last stroke, which will look like undo to the end user */
1756                                 //printf("caught attempted undo event... deleting last stroke \n");
1757                                 gpencil_frame_delete_laststroke(p->gpl, p->gpf);
1758                                 /* undoing the last line can free p->gpf
1759                                  * note, could do this in a bit more of an elegant way then a search but it at least prevents a crash */
1760                                 if(BLI_findindex(&p->gpl->frames, p->gpf) == -1) {
1761                                         p->gpf= NULL;
1762                                 }
1763
1764                                 /* event handled, so force refresh */
1765                                 ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */
1766                                 estate = OPERATOR_RUNNING_MODAL; 
1767                         }
1768                 }
1769         }
1770         
1771         /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
1772         if(0==gpencil_area_exists(C, p->sa))
1773                 estate= OPERATOR_CANCELLED;
1774         else
1775                 /* update status indicators - cursor, header, etc. */
1776                 gpencil_draw_status_indicators(p);
1777         
1778         /* process last operations before exiting */
1779         switch (estate) {
1780                 case OPERATOR_FINISHED:
1781                         /* one last flush before we're done */
1782                         gpencil_draw_exit(C, op);
1783                         WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
1784                         break;
1785                         
1786                 case OPERATOR_CANCELLED:
1787                         gpencil_draw_exit(C, op);
1788                         break;
1789
1790                 case OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH:
1791                         /* event doesn't need to be handled */
1792                         //printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE);
1793                         break;
1794         }
1795         
1796         /* return status code */
1797         return estate;
1798 }
1799
1800 /* ------------------------------- */
1801
1802 static EnumPropertyItem prop_gpencil_drawmodes[] = {
1803         {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", ""},
1804         {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 0, "Draw Straight Lines", ""},
1805         {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", ""},
1806         {0, NULL, 0, NULL, NULL}
1807 };
1808
1809 void GPENCIL_OT_draw (wmOperatorType *ot)
1810 {
1811         /* identifiers */
1812         ot->name= "Grease Pencil Draw";
1813         ot->idname= "GPENCIL_OT_draw";
1814         ot->description= "Make annotations on the active data";
1815         
1816         /* api callbacks */
1817         ot->exec= gpencil_draw_exec;
1818         ot->invoke= gpencil_draw_invoke;
1819         ot->modal= gpencil_draw_modal;
1820         ot->cancel= gpencil_draw_cancel;
1821         ot->poll= gpencil_draw_poll;
1822         
1823         /* flags */
1824         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
1825         
1826         /* settings for drawing */
1827         RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to intepret mouse movements.");
1828         
1829         RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1830 }