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