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