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