Blender V2.61 - r43446

gpencil_paint.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
00019  * This is a new part of Blender
00020  *
00021  * Contributor(s): Joshua Leung
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include <stdio.h>
00032 #include <stddef.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <math.h>
00036 
00037 #include "MEM_guardedalloc.h"
00038 
00039 #include "BLI_blenlib.h"
00040 #include "BLI_math.h"
00041 #include "BLI_utildefines.h"
00042 
00043 #include "BKE_gpencil.h"
00044 #include "BKE_context.h"
00045 #include "BKE_global.h"
00046 #include "BKE_report.h"
00047 
00048 #include "DNA_object_types.h"
00049 #include "DNA_scene_types.h"
00050 #include "DNA_gpencil_types.h"
00051 #include "DNA_windowmanager_types.h"
00052 
00053 #include "UI_view2d.h"
00054 
00055 #include "ED_gpencil.h"
00056 #include "ED_screen.h"
00057 #include "ED_view3d.h"
00058 
00059 #include "RNA_access.h"
00060 
00061 #include "RNA_define.h"
00062 #include "WM_api.h"
00063 #include "WM_types.h"
00064 
00065 #include "gpencil_intern.h"
00066 
00067 /* ******************************************* */
00068 /* 'Globals' and Defines */
00069 
00070 /* Temporary 'Stroke' Operation data */
00071 typedef struct tGPsdata {
00072     Scene *scene;       /* current scene from context */
00073     
00074     wmWindow *win;      /* window where painting originated */
00075     ScrArea *sa;        /* area where painting originated */
00076     ARegion *ar;        /* region where painting originated */
00077     View2D *v2d;        /* needed for GP_STROKE_2DSPACE */
00078     rctf *subrect;      /* for using the camera rect within the 3d view */
00079     rctf subrect_data;
00080     
00081     
00082 #if 0 // XXX review this 2d image stuff...
00083     ImBuf *ibuf;        /* needed for GP_STROKE_2DIMAGE */
00084     struct IBufViewSettings {
00085         int offsx, offsy;           /* offsets */
00086         int sizex, sizey;           /* dimensions to use as scale-factor */
00087     } im2d_settings;    /* needed for GP_STROKE_2DIMAGE */
00088 #endif
00089     
00090     PointerRNA ownerPtr;/* pointer to owner of gp-datablock */
00091     bGPdata *gpd;       /* gp-datablock layer comes from */
00092     bGPDlayer *gpl;     /* layer we're working on */
00093     bGPDframe *gpf;     /* frame we're working on */
00094     
00095     short status;       /* current status of painting */
00096     short paintmode;    /* mode for painting */
00097     
00098     int mval[2];        /* current mouse-position */
00099     int mvalo[2];       /* previous recorded mouse-position */
00100     
00101     float pressure;     /* current stylus pressure */
00102     float opressure;    /* previous stylus pressure */
00103     
00104     short radius;       /* radius of influence for eraser */
00105     short flags;        /* flags that can get set during runtime */
00106 
00107     float imat[4][4];   /* inverted transformation matrix applying when converting coords from screen-space
00108                          * to region space */
00109 
00110     float custom_color[4]; /* custom color for (?) */
00111 } tGPsdata;
00112 
00113 /* values for tGPsdata->status */
00114 enum {
00115     GP_STATUS_IDLING = 0,   /* stroke isn't in progress yet */
00116     GP_STATUS_PAINTING,     /* a stroke is in progress */
00117     GP_STATUS_ERROR,        /* something wasn't correctly set up */
00118     GP_STATUS_DONE          /* painting done */
00119 };
00120 
00121 /* Return flags for adding points to stroke buffer */
00122 enum {
00123     GP_STROKEADD_INVALID    = -2,       /* error occurred - insufficient info to do so */
00124     GP_STROKEADD_OVERFLOW   = -1,       /* error occurred - cannot fit any more points */
00125     GP_STROKEADD_NORMAL,                /* point was successfully added */
00126     GP_STROKEADD_FULL                   /* cannot add any more points to buffer */
00127 };
00128 
00129 /* Runtime flags */
00130 enum {
00131     GP_PAINTFLAG_FIRSTRUN       = (1<<0),   /* operator just started */
00132     GP_PAINTFLAG_STROKEADDED    = (1<<1)    /* stroke was already added during draw session */
00133 };
00134 
00135 /* ------ */
00136 
00137 /* maximum sizes of gp-session buffer */
00138 #define GP_STROKE_BUFFER_MAX    5000
00139 
00140 /* Macros for accessing sensitivity thresholds... */
00141     /* minimum number of pixels mouse should move before new point created */
00142 #define MIN_MANHATTEN_PX    (U.gp_manhattendist)
00143     /* minimum length of new segment before new point can be added */
00144 #define MIN_EUCLIDEAN_PX    (U.gp_euclideandist)
00145 
00146 /* ------ */
00147 /* Forward defines for some functions... */
00148 
00149 static void gp_session_validatebuffer(tGPsdata *p);
00150 
00151 /* ******************************************* */
00152 /* Context Wrangling... */
00153 
00154 /* check if context is suitable for drawing */
00155 static int gpencil_draw_poll (bContext *C)
00156 {
00157     if (ED_operator_regionactive(C)) {
00158         /* check if current context can support GPencil data */
00159         if (gpencil_data_get_pointers(C, NULL) != NULL) {
00160             /* check if Grease Pencil isn't already running */
00161             if (ED_gpencil_session_active() == 0)
00162                 return 1;
00163             else
00164                 CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active");
00165         }
00166         else {
00167             CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
00168         }
00169     }
00170     else {
00171         CTX_wm_operator_poll_msg_set(C, "Active region not set");
00172     }
00173     
00174     return 0;
00175 }
00176 
00177 /* check if projecting strokes into 3d-geometry in the 3D-View */
00178 static int gpencil_project_check (tGPsdata *p)
00179 {
00180     bGPdata *gpd= p->gpd;
00181     return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (p->gpd->flag & (GP_DATA_DEPTH_VIEW | GP_DATA_DEPTH_STROKE)));
00182 }
00183 
00184 /* ******************************************* */
00185 /* Calculations/Conversions */
00186 
00187 /* Utilities --------------------------------- */
00188 
00189 /* get the reference point for stroke-point conversions */
00190 static void gp_get_3d_reference (tGPsdata *p, float *vec)
00191 {
00192     View3D *v3d= p->sa->spacedata.first;
00193     float *fp= give_cursor(p->scene, v3d);
00194     
00195     /* the reference point used depends on the owner... */
00196 #if 0 // XXX: disabled for now, since we can't draw relative to the owner yet
00197     if (p->ownerPtr.type == &RNA_Object) 
00198     {
00199         Object *ob= (Object *)p->ownerPtr.data;
00200         
00201         /* active Object 
00202          *  - use relative distance of 3D-cursor from object center 
00203          */
00204         sub_v3_v3v3(vec, fp, ob->loc);
00205     }
00206     else
00207 #endif  
00208     {
00209         /* use 3D-cursor */
00210         copy_v3_v3(vec, fp);
00211     }
00212 }
00213 
00214 /* Stroke Editing ---------------------------- */
00215 
00216 /* check if the current mouse position is suitable for adding a new point */
00217 static short gp_stroke_filtermval (tGPsdata *p, const int mval[2], int pmval[2])
00218 {
00219     int dx= abs(mval[0] - pmval[0]);
00220     int dy= abs(mval[1] - pmval[1]);
00221     
00222     /* if buffer is empty, just let this go through (i.e. so that dots will work) */
00223     if (p->gpd->sbuffer_size == 0)
00224         return 1;
00225     
00226     /* check if mouse moved at least certain distance on both axes (best case) 
00227      *  - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
00228      */
00229     else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX))
00230         return 1;
00231     
00232     /* check if the distance since the last point is significant enough 
00233      *  - prevents points being added too densely
00234      *  - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though
00235      */
00236     else if ((dx*dx + dy*dy) > MIN_EUCLIDEAN_PX*MIN_EUCLIDEAN_PX)
00237         return 1;
00238     
00239     /* mouse 'didn't move' */
00240     else
00241         return 0;
00242 }
00243 
00244 /* convert screen-coordinates to buffer-coordinates */
00245 // XXX this method needs a total overhaul!
00246 static void gp_stroke_convertcoords (tGPsdata *p, const int mval[2], float out[3], float *depth)
00247 {
00248     bGPdata *gpd= p->gpd;
00249     
00250     /* in 3d-space - pt->x/y/z are 3 side-by-side floats */
00251     if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) {
00252         if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) {
00253             /* projecting onto 3D-Geometry
00254              *  - nothing more needs to be done here, since view_autodist_simple() has already done it
00255              */
00256         }
00257         else {
00258             int mval_prj[2];
00259             float rvec[3], dvec[3];
00260             float mval_f[2];
00261 
00262             /* Current method just converts each point in screen-coordinates to
00263              * 3D-coordinates using the 3D-cursor as reference. In general, this
00264              * works OK, but it could of course be improved.
00265              *
00266              * TODO:
00267              *  - investigate using nearest point(s) on a previous stroke as
00268              *    reference point instead or as offset, for easier stroke matching
00269              */
00270             
00271             gp_get_3d_reference(p, rvec);
00272             
00273             /* method taken from editview.c - mouse_cursor() */
00274             project_int_noclip(p->ar, rvec, mval_prj);
00275 
00276             VECSUB2D(mval_f, mval_prj, mval);
00277             ED_view3d_win_to_delta(p->ar, mval_f, dvec);
00278             sub_v3_v3v3(out, rvec, dvec);
00279         }
00280     }
00281     
00282     /* 2d - on 'canvas' (assume that p->v2d is set) */
00283     else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) {
00284         UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]);
00285         mul_v3_m4v3(out, p->imat, out);
00286     }
00287     
00288 #if 0
00289     /* 2d - on image 'canvas' (assume that p->v2d is set) */
00290     else if (gpd->sbuffer_sflag & GP_STROKE_2DIMAGE) {
00291         int sizex, sizey, offsx, offsy;
00292         
00293         /* get stored settings 
00294          *  - assume that these have been set already (there are checks that set sane 'defaults' just in case)
00295          */
00296         sizex= p->im2d_settings.sizex;
00297         sizey= p->im2d_settings.sizey;
00298         offsx= p->im2d_settings.offsx;
00299         offsy= p->im2d_settings.offsy;
00300         
00301         /* calculate new points */
00302         out[0]= (float)(mval[0] - offsx) / (float)sizex;
00303         out[1]= (float)(mval[1] - offsy) / (float)sizey;
00304     }
00305 #endif
00306     
00307     /* 2d - relative to screen (viewport area) */
00308     else {
00309         if (p->subrect == NULL) { /* normal 3D view */
00310             out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100;
00311             out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100;
00312         }
00313         else { /* camera view, use subrect */
00314             out[0]= ((mval[0] - p->subrect->xmin) / ((p->subrect->xmax - p->subrect->xmin))) * 100;
00315             out[1]= ((mval[1] - p->subrect->ymin) / ((p->subrect->ymax - p->subrect->ymin))) * 100;
00316         }
00317     }
00318 }
00319 
00320 /* add current stroke-point to buffer (returns whether point was successfully added) */
00321 static short gp_stroke_addpoint (tGPsdata *p, const int mval[2], float pressure)
00322 {
00323     bGPdata *gpd= p->gpd;
00324     tGPspoint *pt;
00325     
00326     /* check painting mode */
00327     if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
00328         /* straight lines only - i.e. only store start and end point in buffer */
00329         if (gpd->sbuffer_size == 0) {
00330             /* first point in buffer (start point) */
00331             pt= (tGPspoint *)(gpd->sbuffer);
00332             
00333             /* store settings */
00334             copy_v2_v2_int(&pt->x, mval);
00335             pt->pressure= pressure;
00336             
00337             /* increment buffer size */
00338             gpd->sbuffer_size++;
00339         }
00340         else {
00341             /* normally, we just reset the endpoint to the latest value 
00342              *  - assume that pointers for this are always valid...
00343              */
00344             pt= ((tGPspoint *)(gpd->sbuffer) + 1);
00345             
00346             /* store settings */
00347             copy_v2_v2_int(&pt->x, mval);
00348             pt->pressure= pressure;
00349             
00350             /* if this is just the second point we've added, increment the buffer size
00351              * so that it will be drawn properly...
00352              * otherwise, just leave it alone, otherwise we get problems
00353              */
00354             if (gpd->sbuffer_size != 2)
00355                 gpd->sbuffer_size= 2;
00356         }
00357         
00358         /* can keep carrying on this way :) */
00359         return GP_STROKEADD_NORMAL;
00360     }
00361     else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
00362         /* check if still room in buffer */
00363         if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX)
00364             return GP_STROKEADD_OVERFLOW;
00365         
00366         /* get pointer to destination point */
00367         pt= ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size);
00368         
00369         /* store settings */
00370         copy_v2_v2_int(&pt->x, mval);
00371         pt->pressure= pressure;
00372         
00373         /* increment counters */
00374         gpd->sbuffer_size++;
00375         
00376         /* check if another operation can still occur */
00377         if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX)
00378             return GP_STROKEADD_FULL;
00379         else
00380             return GP_STROKEADD_NORMAL;
00381     }
00382     else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
00383         /* get pointer to destination point */
00384         pt= (tGPspoint *)(gpd->sbuffer);
00385 
00386         /* store settings */
00387         copy_v2_v2_int(&pt->x, mval);
00388         pt->pressure= pressure;
00389 
00390         /* if there's stroke for this poly line session add (or replace last) point
00391          * to stroke. This allows to draw lines more interactively (see new segment
00392          * during mouse slide, i.e.) 
00393          */
00394         if (p->flags & GP_PAINTFLAG_STROKEADDED) {
00395             bGPDstroke *gps= p->gpf->strokes.last;
00396             bGPDspoint *pts;
00397 
00398             /* first time point is adding to temporary buffer -- need to allocate new point in stroke */
00399             if (gpd->sbuffer_size == 0) {
00400                 gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint)*(gps->totpoints+1));
00401                 gps->totpoints++;
00402             }
00403 
00404             pts = &gps->points[gps->totpoints-1];
00405 
00406             /* special case for poly lines: normally, depth is needed only when creating new stroke from buffer,
00407              * but poly lines are converting to stroke instantly, so initialize depth buffer before converting coordinates 
00408              */
00409             if (gpencil_project_check(p)) {
00410                 View3D *v3d= p->sa->spacedata.first;
00411 
00412                 view3d_region_operator_needs_opengl(p->win, p->ar);
00413                 ED_view3d_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1:0);
00414             }
00415 
00416             /* convert screen-coordinates to appropriate coordinates (and store them) */
00417             gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL);
00418 
00419             /* copy pressure */
00420             pts->pressure= pt->pressure;
00421         }
00422 
00423         /* increment counters */
00424         if (gpd->sbuffer_size == 0)
00425             gpd->sbuffer_size++;
00426 
00427         return GP_STROKEADD_NORMAL;
00428     }
00429     
00430     /* return invalid state for now... */
00431     return GP_STROKEADD_INVALID;
00432 }
00433 
00434 
00435 /* temp struct for gp_stroke_smooth() */
00436 typedef struct tGpSmoothCo {
00437     int x;
00438     int y;
00439 } tGpSmoothCo;
00440 
00441 /* smooth a stroke (in buffer) before storing it */
00442 static void gp_stroke_smooth (tGPsdata *p)
00443 {
00444     bGPdata *gpd= p->gpd;
00445     tGpSmoothCo *smoothArray, *spc;
00446     int i=0, cmx=gpd->sbuffer_size;
00447     
00448     /* only smooth if smoothing is enabled, and we're not doing a straight line */
00449     if (!(U.gp_settings & GP_PAINT_DOSMOOTH) || ELEM(p->paintmode, GP_PAINTMODE_DRAW_STRAIGHT, GP_PAINTMODE_DRAW_POLY))
00450         return;
00451     
00452     /* don't try if less than 2 points in buffer */
00453     if ((cmx <= 2) || (gpd->sbuffer == NULL))
00454         return;
00455     
00456     /* create a temporary smoothing coordinates buffer, use to store calculated values to prevent sequential error */
00457     smoothArray = MEM_callocN(sizeof(tGpSmoothCo)*cmx, "gp_stroke_smooth smoothArray");
00458     
00459     /* first pass: calculate smoothing coordinates using weighted-averages */
00460     for (i=0, spc=smoothArray; i < gpd->sbuffer_size; i++, spc++) {
00461         const tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
00462         const tGPspoint *pb= (i-1 > 0)?(pc-1):(pc);
00463         const tGPspoint *pa= (i-2 > 0)?(pc-2):(pb);
00464         const tGPspoint *pd= (i+1 < cmx)?(pc+1):(pc);
00465         const tGPspoint *pe= (i+2 < cmx)?(pc+2):(pd);
00466         
00467         spc->x= (int)(0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x);
00468         spc->y= (int)(0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y);
00469     }
00470     
00471     /* second pass: apply smoothed coordinates */
00472     for (i=0, spc=smoothArray; i < gpd->sbuffer_size; i++, spc++) {
00473         tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
00474 
00475         copy_v2_v2_int(&pc->x, &spc->x);
00476     }
00477     
00478     /* free temp array */
00479     MEM_freeN(smoothArray);
00480 }
00481 
00482 /* simplify a stroke (in buffer) before storing it 
00483  *  - applies a reverse Chaikin filter
00484  *  - code adapted from etch-a-ton branch (editarmature_sketch.c)
00485  */
00486 static void gp_stroke_simplify (tGPsdata *p)
00487 {
00488     bGPdata *gpd= p->gpd;
00489     tGPspoint *old_points= (tGPspoint *)gpd->sbuffer;
00490     short num_points= gpd->sbuffer_size;
00491     short flag= gpd->sbuffer_sflag;
00492     short i, j;
00493     
00494     /* only simplify if simplification is enabled, and we're not doing a straight line */
00495     if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT))
00496         return;
00497     
00498     /* don't simplify if less than 4 points in buffer */
00499     if ((num_points <= 4) || (old_points == NULL))
00500         return;
00501         
00502     /* clear buffer (but don't free mem yet) so that we can write to it 
00503      *  - firstly set sbuffer to NULL, so a new one is allocated
00504      *  - secondly, reset flag after, as it gets cleared auto
00505      */
00506     gpd->sbuffer= NULL;
00507     gp_session_validatebuffer(p);
00508     gpd->sbuffer_sflag = flag;
00509     
00510 /* macro used in loop to get position of new point
00511  *  - used due to the mixture of datatypes in use here
00512  */
00513 #define GP_SIMPLIFY_AVPOINT(offs, sfac) \
00514     { \
00515         co[0] += (float)(old_points[offs].x * sfac); \
00516         co[1] += (float)(old_points[offs].y * sfac); \
00517         pressure += old_points[offs].pressure * sfac; \
00518     }
00519     
00520     for (i = 0, j = 0; i < num_points; i++)
00521     {
00522         if (i - j == 3)
00523         {
00524             float co[2], pressure;
00525             int mco[2];
00526             
00527             /* initialise values */
00528             co[0]= 0;
00529             co[1]= 0;
00530             pressure = 0;
00531             
00532             /* using macro, calculate new point */
00533             GP_SIMPLIFY_AVPOINT(j, -0.25f);
00534             GP_SIMPLIFY_AVPOINT(j+1, 0.75f);
00535             GP_SIMPLIFY_AVPOINT(j+2, 0.75f);
00536             GP_SIMPLIFY_AVPOINT(j+3, -0.25f);
00537             
00538             /* set values for adding */
00539             mco[0]= (int)co[0];
00540             mco[1]= (int)co[1];
00541             
00542             /* ignore return values on this... assume to be ok for now */
00543             gp_stroke_addpoint(p, mco, pressure);
00544             
00545             j += 2;
00546         }
00547     } 
00548     
00549     /* free old buffer */
00550     MEM_freeN(old_points);
00551 }
00552 
00553 
00554 /* make a new stroke from the buffer data */
00555 static void gp_stroke_newfrombuffer (tGPsdata *p)
00556 {
00557     bGPdata *gpd= p->gpd;
00558     bGPDstroke *gps;
00559     bGPDspoint *pt;
00560     tGPspoint *ptc;
00561     int i, totelem;
00562     /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */
00563     int depth_margin = (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 4 : 0;
00564     
00565     /* get total number of points to allocate space for 
00566      *  - drawing straight-lines only requires the endpoints
00567      */
00568     if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)
00569         totelem = (gpd->sbuffer_size >= 2) ? 2: gpd->sbuffer_size;
00570     else
00571         totelem = gpd->sbuffer_size;
00572     
00573     /* exit with error if no valid points from this stroke */
00574     if (totelem == 0) {
00575         if (G.f & G_DEBUG) 
00576             printf("Error: No valid points in stroke buffer to convert (tot=%d) \n", gpd->sbuffer_size);
00577         return;
00578     }
00579     
00580     /* special case for poly line -- for already added stroke during session
00581        coordinates are getting added to stroke immediatelly to allow more
00582        interactive behavior */
00583     if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
00584         if (p->flags & GP_PAINTFLAG_STROKEADDED)
00585             return;
00586     }
00587 
00588     /* allocate memory for a new stroke */
00589     gps= MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
00590     
00591     /* copy appropriate settings for stroke */
00592     gps->totpoints= totelem;
00593     gps->thickness= p->gpl->thickness;
00594     gps->flag= gpd->sbuffer_sflag;
00595     
00596     /* allocate enough memory for a continuous array for storage points */
00597     gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
00598 
00599     /* set pointer to first non-initialized point */
00600     pt= gps->points + (gps->totpoints - totelem);
00601 
00602     /* copy points from the buffer to the stroke */
00603     if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
00604         /* straight lines only -> only endpoints */
00605         {
00606             /* first point */
00607             ptc= gpd->sbuffer;
00608             
00609             /* convert screen-coordinates to appropriate coordinates (and store them) */
00610             gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
00611             
00612             /* copy pressure */
00613             pt->pressure= ptc->pressure;
00614             
00615             pt++;
00616         }
00617             
00618         if (totelem == 2) {
00619             /* last point if applicable */
00620             ptc= ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1);
00621             
00622             /* convert screen-coordinates to appropriate coordinates (and store them) */
00623             gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
00624             
00625             /* copy pressure */
00626             pt->pressure= ptc->pressure;
00627         }
00628     }
00629     else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
00630         /* first point */
00631         ptc= gpd->sbuffer;
00632 
00633         /* convert screen-coordinates to appropriate coordinates (and store them) */
00634         gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
00635 
00636         /* copy pressure */
00637         pt->pressure= ptc->pressure;
00638     }
00639     else {
00640         float *depth_arr= NULL;
00641         
00642         /* get an array of depths, far depths are blended */
00643         if (gpencil_project_check(p)) {
00644             int mval[2], mval_prev[2]= {0};
00645             int interp_depth = 0;
00646             int found_depth = 0;
00647             
00648             depth_arr= MEM_mallocN(sizeof(float) * gpd->sbuffer_size, "depth_points");
00649 
00650             for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size; i++, ptc++, pt++) {
00651                 copy_v2_v2_int(mval, &ptc->x);
00652 
00653                 if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr+i) == 0) &&
00654                     (i && (ED_view3d_autodist_depth_seg(p->ar, mval, mval_prev, depth_margin + 1, depth_arr+i) == 0))
00655                 ) {
00656                     interp_depth= TRUE;
00657                 }
00658                 else {
00659                     found_depth= TRUE;
00660                 }
00661 
00662                 copy_v2_v2_int(mval_prev, mval);
00663             }
00664             
00665             if (found_depth == FALSE) {
00666                 /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */
00667                 for (i=gpd->sbuffer_size-1; i >= 0; i--)
00668                     depth_arr[i] = 0.9999f;
00669             }
00670             else {
00671                 if (p->gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS) {
00672                     /* remove all info between the valid endpoints */
00673                     int first_valid = 0;
00674                     int last_valid = 0;
00675                     
00676                     for (i=0; i < gpd->sbuffer_size; i++) {
00677                         if (depth_arr[i] != FLT_MAX)
00678                             break;
00679                     }
00680                     first_valid= i;
00681                     
00682                     for (i=gpd->sbuffer_size-1; i >= 0; i--) {
00683                         if (depth_arr[i] != FLT_MAX)
00684                             break;
00685                     }
00686                     last_valid= i;
00687                     
00688                     /* invalidate non-endpoints, so only blend between first and last */
00689                     for (i=first_valid+1; i < last_valid; i++)
00690                         depth_arr[i]= FLT_MAX;
00691                     
00692                     interp_depth= TRUE;
00693                 }
00694                 
00695                 if (interp_depth) {
00696                     interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX);
00697                 }
00698             }
00699         }
00700         
00701         
00702         pt= gps->points;
00703         
00704         /* convert all points (normal behaviour) */
00705         for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) {
00706             /* convert screen-coordinates to appropriate coordinates (and store them) */
00707             gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr+i:NULL);
00708             
00709             /* copy pressure */
00710             pt->pressure= ptc->pressure;
00711         }
00712         
00713         if (depth_arr)
00714             MEM_freeN(depth_arr);
00715     }
00716     
00717     p->flags |= GP_PAINTFLAG_STROKEADDED;
00718 
00719     /* add stroke to frame */
00720     BLI_addtail(&p->gpf->strokes, gps);
00721 }
00722 
00723 /* --- 'Eraser' for 'Paint' Tool ------ */
00724 
00725 /* eraser tool - remove segment from stroke/split stroke (after lasso inside) */
00726 static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i)
00727 {
00728     bGPDspoint *pt_tmp= gps->points;
00729     bGPDstroke *gsn = NULL;
00730 
00731     /* if stroke only had two points, get rid of stroke */
00732     if (gps->totpoints == 2) {
00733         /* free stroke points, then stroke */
00734         MEM_freeN(pt_tmp);
00735         BLI_freelinkN(&gpf->strokes, gps);
00736         
00737         /* nothing left in stroke, so stop */
00738         return 1;
00739     }
00740 
00741     /* if last segment, just remove segment from the stroke */
00742     else if (i == gps->totpoints - 2) {
00743         /* allocate new points array, and assign most of the old stroke there */
00744         gps->totpoints--;
00745         gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
00746         memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*gps->totpoints);
00747         
00748         /* free temp buffer */
00749         MEM_freeN(pt_tmp);
00750         
00751         /* nothing left in stroke, so stop */
00752         return 1;
00753     }
00754 
00755     /* if first segment, just remove segment from the stroke */
00756     else if (i == 0) {
00757         /* allocate new points array, and assign most of the old stroke there */
00758         gps->totpoints--;
00759         gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
00760         memcpy(gps->points, pt_tmp + 1, sizeof(bGPDspoint)*gps->totpoints);
00761         
00762         /* free temp buffer */
00763         MEM_freeN(pt_tmp);
00764         
00765         /* no break here, as there might still be stuff to remove in this stroke */
00766         return 0;
00767     }
00768 
00769     /* segment occurs in 'middle' of stroke, so split */
00770     else {
00771         /* duplicate stroke, and assign 'later' data to that stroke */
00772         gsn= MEM_dupallocN(gps);
00773         gsn->prev= gsn->next= NULL;
00774         BLI_insertlinkafter(&gpf->strokes, gps, gsn);
00775         
00776         gsn->totpoints= gps->totpoints - i;
00777         gsn->points= MEM_callocN(sizeof(bGPDspoint)*gsn->totpoints, "gp_stroke_points");
00778         memcpy(gsn->points, pt_tmp + i, sizeof(bGPDspoint)*gsn->totpoints);
00779         
00780         /* adjust existing stroke  */
00781         gps->totpoints= i;
00782         gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
00783         memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*i);
00784         
00785         /* free temp buffer */
00786         MEM_freeN(pt_tmp);
00787         
00788         /* nothing left in stroke, so stop */
00789         return 1;
00790     }
00791 }
00792 
00793 /* eraser tool - check if part of stroke occurs within last segment drawn by eraser */
00794 static short gp_stroke_eraser_strokeinside (int mval[], int UNUSED(mvalo[]), short rad, short x0, short y0, short x1, short y1)
00795 {
00796     /* simple within-radius check for now */
00797     if (edge_inside_circle(mval[0], mval[1], rad, x0, y0, x1, y1))
00798         return 1;
00799     
00800     /* not inside */
00801     return 0;
00802 } 
00803 
00804 /* eraser tool - evaluation per stroke */
00805 // TODO: this could really do with some optimisation (KD-Tree/BVH?)
00806 static void gp_stroke_eraser_dostroke (tGPsdata *p, int mval[], int mvalo[], short rad, rcti *rect, bGPDframe *gpf, bGPDstroke *gps)
00807 {
00808     bGPDspoint *pt1, *pt2;
00809     int x0=0, y0=0, x1=0, y1=0;
00810     int xyval[2];
00811     int i;
00812     
00813     if (gps->totpoints == 0) {
00814         /* just free stroke */
00815         if (gps->points) 
00816             MEM_freeN(gps->points);
00817         BLI_freelinkN(&gpf->strokes, gps);
00818     }
00819     else if (gps->totpoints == 1) {
00820         /* get coordinates */
00821         if (gps->flag & GP_STROKE_3DSPACE) {
00822             project_int(p->ar, &gps->points->x, xyval);
00823             x0= xyval[0];
00824             y0= xyval[1];
00825         }
00826         else if (gps->flag & GP_STROKE_2DSPACE) {           
00827             UI_view2d_view_to_region(p->v2d, gps->points->x, gps->points->y, &x0, &y0);
00828         }
00829 #if 0
00830         else if (gps->flag & GP_STROKE_2DIMAGE) {           
00831             int offsx, offsy, sizex, sizey;
00832             
00833             /* get stored settings */
00834             sizex= p->im2d_settings.sizex;
00835             sizey= p->im2d_settings.sizey;
00836             offsx= p->im2d_settings.offsx;
00837             offsy= p->im2d_settings.offsy;
00838             
00839             /* calculate new points */
00840             x0= (int)((gps->points->x * sizex) + offsx);
00841             y0= (int)((gps->points->y * sizey) + offsy);
00842         }
00843 #endif
00844         else {
00845             if (p->subrect == NULL) { /* normal 3D view */
00846                 x0= (int)(gps->points->x / 100 * p->ar->winx);
00847                 y0= (int)(gps->points->y / 100 * p->ar->winy);
00848             }
00849             else { /* camera view, use subrect */
00850                 x0= (int)((gps->points->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin;
00851                 y0= (int)((gps->points->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin;
00852             }
00853         }
00854         
00855         /* do boundbox check first */
00856         if (BLI_in_rcti(rect, x0, y0)) {
00857             /* only check if point is inside */
00858             if ( ((x0-mval[0])*(x0-mval[0]) + (y0-mval[1])*(y0-mval[1])) <= rad*rad ) {
00859                 /* free stroke */
00860                 MEM_freeN(gps->points);
00861                 BLI_freelinkN(&gpf->strokes, gps);
00862             }
00863         }
00864     }
00865     else {  
00866         /* loop over the points in the stroke, checking for intersections 
00867          *  - an intersection will require the stroke to be split
00868          */
00869         for (i=0; (i+1) < gps->totpoints; i++) {
00870             /* get points to work with */
00871             pt1= gps->points + i;
00872             pt2= gps->points + i + 1;
00873             
00874             /* get coordinates */
00875             if (gps->flag & GP_STROKE_3DSPACE) {
00876                 project_int(p->ar, &pt1->x, xyval);
00877                 x0= xyval[0];
00878                 y0= xyval[1];
00879                 
00880                 project_int(p->ar, &pt2->x, xyval);
00881                 x1= xyval[0];
00882                 y1= xyval[1];
00883             }
00884             else if (gps->flag & GP_STROKE_2DSPACE) {
00885                 UI_view2d_view_to_region(p->v2d, pt1->x, pt1->y, &x0, &y0);
00886                 
00887                 UI_view2d_view_to_region(p->v2d, pt2->x, pt2->y, &x1, &y1);
00888             }
00889 #if 0
00890             else if (gps->flag & GP_STROKE_2DIMAGE) {
00891                 int offsx, offsy, sizex, sizey;
00892                 
00893                 /* get stored settings */
00894                 sizex= p->im2d_settings.sizex;
00895                 sizey= p->im2d_settings.sizey;
00896                 offsx= p->im2d_settings.offsx;
00897                 offsy= p->im2d_settings.offsy;
00898                 
00899                 /* calculate new points */
00900                 x0= (int)((pt1->x * sizex) + offsx);
00901                 y0= (int)((pt1->y * sizey) + offsy);
00902                 
00903                 x1= (int)((pt2->x * sizex) + offsx);
00904                 y1= (int)((pt2->y * sizey) + offsy);
00905             }
00906 #endif
00907             else {
00908                 if(p->subrect == NULL) { /* normal 3D view */
00909                     x0= (int)(pt1->x / 100 * p->ar->winx);
00910                     y0= (int)(pt1->y / 100 * p->ar->winy);
00911                     x1= (int)(pt2->x / 100 * p->ar->winx);
00912                     y1= (int)(pt2->y / 100 * p->ar->winy);
00913                 }
00914                 else { /* camera view, use subrect */ 
00915                     x0= (int)((pt1->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin;
00916                     y0= (int)((pt1->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin;
00917                     x1= (int)((pt2->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin;
00918                     y1= (int)((pt2->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin;
00919                 }
00920             }
00921             
00922             /* check that point segment of the boundbox of the eraser stroke */
00923             if (BLI_in_rcti(rect, x0, y0) || BLI_in_rcti(rect, x1, y1)) {
00924                 /* check if point segment of stroke had anything to do with
00925                  * eraser region  (either within stroke painted, or on its lines)
00926                  *  - this assumes that linewidth is irrelevant
00927                  */
00928                 if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) {
00929                     /* if function returns true, break this loop (as no more point to check) */
00930                     if (gp_stroke_eraser_splitdel(gpf, gps, i))
00931                         break;
00932                 }
00933             }
00934         }
00935     }
00936 }
00937 
00938 /* erase strokes which fall under the eraser strokes */
00939 static void gp_stroke_doeraser (tGPsdata *p)
00940 {
00941     bGPDframe *gpf= p->gpf;
00942     bGPDstroke *gps, *gpn;
00943     rcti rect;
00944     
00945     /* rect is rectangle of eraser */
00946     rect.xmin= p->mval[0] - p->radius;
00947     rect.ymin= p->mval[1] - p->radius;
00948     rect.xmax= p->mval[0] + p->radius;
00949     rect.ymax= p->mval[1] + p->radius;
00950     
00951     /* loop over strokes, checking segments for intersections */
00952     for (gps= gpf->strokes.first; gps; gps= gpn) {
00953         gpn= gps->next;
00954         gp_stroke_eraser_dostroke(p, p->mval, p->mvalo, p->radius, &rect, gpf, gps);
00955     }
00956 }
00957 
00958 /* ******************************************* */
00959 /* Sketching Operator */
00960 
00961 /* clear the session buffers (call this before AND after a paint operation) */
00962 static void gp_session_validatebuffer (tGPsdata *p)
00963 {
00964     bGPdata *gpd= p->gpd;
00965     
00966     /* clear memory of buffer (or allocate it if starting a new session) */
00967     if (gpd->sbuffer) {
00968         //printf("\t\tGP - reset sbuffer\n");
00969         memset(gpd->sbuffer, 0, sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX);
00970     }
00971     else {
00972         //printf("\t\tGP - allocate sbuffer\n");
00973         gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
00974     }
00975     
00976     /* reset indices */
00977     gpd->sbuffer_size = 0;
00978     
00979     /* reset flags */
00980     gpd->sbuffer_sflag= 0;
00981 }
00982 
00983 /* (re)init new painting data */
00984 static int gp_session_initdata (bContext *C, tGPsdata *p)
00985 {
00986     bGPdata **gpd_ptr = NULL;
00987     ScrArea *curarea= CTX_wm_area(C);
00988     ARegion *ar= CTX_wm_region(C);
00989     
00990     /* make sure the active view (at the starting time) is a 3d-view */
00991     if (curarea == NULL) {
00992         p->status= GP_STATUS_ERROR;
00993         if (G.f & G_DEBUG) 
00994             printf("Error: No active view for painting \n");
00995         return 0;
00996     }
00997     
00998     /* pass on current scene and window */
00999     p->scene= CTX_data_scene(C);
01000     p->win= CTX_wm_window(C);
01001 
01002     unit_m4(p->imat);
01003     
01004     switch (curarea->spacetype) {
01005         /* supported views first */
01006         case SPACE_VIEW3D:
01007         {
01008             // View3D *v3d= curarea->spacedata.first;
01009             // RegionView3D *rv3d= ar->regiondata;
01010             
01011             /* set current area 
01012              *  - must verify that region data is 3D-view (and not something else)
01013              */
01014             p->sa= curarea;
01015             p->ar= ar;
01016             
01017             if (ar->regiondata == NULL) {
01018                 p->status= GP_STATUS_ERROR;
01019                 if (G.f & G_DEBUG)
01020                     printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable \n");
01021                 return 0;
01022             }
01023 
01024 #if 0 // XXX will this sort of antiquated stuff be restored?
01025             /* check that gpencil data is allowed to be drawn */
01026             if ((v3d->flag2 & V3D_DISPGP)==0) {
01027                 p->status= GP_STATUS_ERROR;
01028                 if (G.f & G_DEBUG) 
01029                     printf("Error: In active view, Grease Pencil not shown \n");
01030                 return 0;
01031             }
01032 #endif
01033         }
01034             break;
01035 
01036         case SPACE_NODE:
01037         {
01038             //SpaceNode *snode= curarea->spacedata.first;
01039             
01040             /* set current area */
01041             p->sa= curarea;
01042             p->ar= ar;
01043             p->v2d= &ar->v2d;
01044             
01045 #if 0 // XXX will this sort of antiquated stuff be restored?
01046             /* check that gpencil data is allowed to be drawn */
01047             if ((snode->flag & SNODE_DISPGP)==0) {
01048                 p->status= GP_STATUS_ERROR;
01049                 if (G.f & G_DEBUG) 
01050                     printf("Error: In active view, Grease Pencil not shown \n");
01051                 return 0;
01052             }
01053 #endif
01054         }
01055             break;
01056 #if 0 // XXX these other spaces will come over time...
01057         case SPACE_SEQ:
01058         {
01059             SpaceSeq *sseq= curarea->spacedata.first;
01060             
01061             /* set current area */
01062             p->sa= curarea;
01063             p->ar= ar;
01064             p->v2d= &ar->v2d;
01065             
01066             /* check that gpencil data is allowed to be drawn */
01067             if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
01068                 p->status= GP_STATUS_ERROR;
01069                 if (G.f & G_DEBUG) 
01070                     printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil \n");
01071                 return 0;
01072             }
01073             if ((sseq->flag & SEQ_DRAW_GPENCIL)==0) {
01074                 p->status= GP_STATUS_ERROR;
01075                 if (G.f & G_DEBUG) 
01076                     printf("Error: In active view, Grease Pencil not shown \n");
01077                 return 0;
01078             }
01079         }
01080             break;  
01081 #endif
01082         case SPACE_IMAGE:
01083         {
01084             //SpaceImage *sima= curarea->spacedata.first;
01085             
01086             /* set the current area */
01087             p->sa= curarea;
01088             p->ar= ar;
01089             p->v2d= &ar->v2d;
01090             //p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
01091             
01092 #if 0 // XXX disabled for now
01093             /* check that gpencil data is allowed to be drawn */
01094             if ((sima->flag & SI_DISPGP)==0) {
01095                 p->status= GP_STATUS_ERROR;
01096                 if (G.f & G_DEBUG)
01097                     printf("Error: In active view, Grease Pencil not shown \n");
01098                 return 0;
01099             }
01100 #endif
01101         }
01102             break;
01103         case SPACE_CLIP:
01104         {
01105             SpaceClip *sc= curarea->spacedata.first;
01106 
01107             /* set the current area */
01108             p->sa= curarea;
01109             p->ar= ar;
01110             p->v2d= &ar->v2d;
01111             //p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
01112 
01113             invert_m4_m4(p->imat, sc->unistabmat);
01114 
01115             /* custom color for new layer */
01116             p->custom_color[0]= 1.0f;
01117             p->custom_color[1]= 0.0f;
01118             p->custom_color[2]= 0.5f;
01119             p->custom_color[3]= 0.9f;
01120 
01121             /* check that gpencil data is allowed to be drawn */
01122             if ((sc->flag & SC_SHOW_GPENCIL)==0) {
01123                 p->status= GP_STATUS_ERROR;
01124                 if (G.f & G_DEBUG)
01125                     printf("Error: In active view, Grease Pencil not shown \n");
01126                 return 0;
01127             }
01128         }
01129             break;
01130 
01131         /* unsupported views */
01132         default:
01133         {
01134             p->status= GP_STATUS_ERROR;
01135             if (G.f & G_DEBUG) 
01136                 printf("Error: Active view not appropriate for Grease Pencil drawing \n");
01137             return 0;
01138         }
01139             break;
01140     }
01141     
01142     /* get gp-data */
01143     gpd_ptr= gpencil_data_get_pointers(C, &p->ownerPtr);
01144     if (gpd_ptr == NULL) {
01145         p->status= GP_STATUS_ERROR;
01146         if (G.f & G_DEBUG)
01147             printf("Error: Current context doesn't allow for any Grease Pencil data \n");
01148         return 0;
01149     }
01150     else {
01151         /* if no existing GPencil block exists, add one */
01152         if (*gpd_ptr == NULL)
01153             *gpd_ptr= gpencil_data_addnew("GPencil");
01154         p->gpd= *gpd_ptr;
01155     }
01156     
01157     if (ED_gpencil_session_active()==0) {
01158         /* initialize undo stack,
01159            also, existing undo stack would make buffer drawn */
01160         gpencil_undo_init(p->gpd);
01161     }
01162     
01163     /* clear out buffer (stored in gp-data), in case something contaminated it */
01164     gp_session_validatebuffer(p);
01165     
01166 #if 0
01167     /* set 'default' im2d_settings just in case something that uses this doesn't set it */
01168     p->im2d_settings.sizex= 1;
01169     p->im2d_settings.sizey= 1;
01170 #endif
01171 
01172     return 1;
01173 }
01174 
01175 /* init new painting session */
01176 static tGPsdata *gp_session_initpaint (bContext *C)
01177 {
01178     tGPsdata *p = NULL;
01179 
01180     /* create new context data */
01181     p= MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data");
01182 
01183     gp_session_initdata(C, p);
01184     
01185     /* return context data for running paint operator */
01186     return p;
01187 }
01188 
01189 /* cleanup after a painting session */
01190 static void gp_session_cleanup (tGPsdata *p)
01191 {
01192     bGPdata *gpd= (p) ? p->gpd : NULL;
01193     
01194     /* error checking */
01195     if (gpd == NULL)
01196         return;
01197     
01198     /* free stroke buffer */
01199     if (gpd->sbuffer) {
01200         //printf("\t\tGP - free sbuffer\n");
01201         MEM_freeN(gpd->sbuffer);
01202         gpd->sbuffer= NULL;
01203     }
01204     
01205     /* clear flags */
01206     gpd->sbuffer_size= 0;
01207     gpd->sbuffer_sflag= 0;
01208 }
01209 
01210 /* init new stroke */
01211 static void gp_paint_initstroke (tGPsdata *p, short paintmode)
01212 {   
01213     /* get active layer (or add a new one if non-existent) */
01214     p->gpl= gpencil_layer_getactive(p->gpd);
01215     if (p->gpl == NULL) {
01216         p->gpl= gpencil_layer_addnew(p->gpd);
01217 
01218         if(p->custom_color[3])
01219             copy_v3_v3(p->gpl->color, p->custom_color);
01220     }
01221     if (p->gpl->flag & GP_LAYER_LOCKED) {
01222         p->status= GP_STATUS_ERROR;
01223         if (G.f & G_DEBUG)
01224             printf("Error: Cannot paint on locked layer \n");
01225         return;
01226     }
01227         
01228     /* get active frame (add a new one if not matching frame) */
01229     p->gpf= gpencil_layer_getframe(p->gpl, p->scene->r.cfra, 1);
01230     if (p->gpf == NULL) {
01231         p->status= GP_STATUS_ERROR;
01232         if (G.f & G_DEBUG) 
01233             printf("Error: No frame created (gpencil_paint_init) \n");
01234         return;
01235     }
01236     else
01237         p->gpf->flag |= GP_FRAME_PAINT;
01238     
01239     /* set 'eraser' for this stroke if using eraser */
01240     p->paintmode= paintmode;
01241     if (p->paintmode == GP_PAINTMODE_ERASER)
01242         p->gpd->sbuffer_sflag |= GP_STROKE_ERASER;
01243         
01244     /* set 'initial run' flag, which is only used to denote when a new stroke is starting */
01245     p->flags |= GP_PAINTFLAG_FIRSTRUN;
01246     
01247 
01248     /* when drawing in the camera view, in 2D space, set the subrect */
01249     if (!(p->gpd->flag & GP_DATA_VIEWALIGN)) {
01250         if (p->sa->spacetype == SPACE_VIEW3D) {
01251             View3D *v3d= p->sa->spacedata.first;
01252             RegionView3D *rv3d= p->ar->regiondata;
01253 
01254             /* for camera view set the subrect */
01255             if (rv3d->persp == RV3D_CAMOB) {
01256                 ED_view3d_calc_camera_border(p->scene, p->ar, v3d, rv3d, &p->subrect_data, TRUE); /* no shift */
01257                 p->subrect= &p->subrect_data;
01258             }
01259         }
01260     }
01261 
01262     /* check if points will need to be made in view-aligned space */
01263     if (p->gpd->flag & GP_DATA_VIEWALIGN) {
01264         switch (p->sa->spacetype) {
01265             case SPACE_VIEW3D:
01266             {
01267                 RegionView3D *rv3d= p->ar->regiondata;
01268                 float rvec[3];
01269                 
01270                 /* get reference point for 3d space placement */
01271                 gp_get_3d_reference(p, rvec);
01272                 initgrabz(rv3d, rvec[0], rvec[1], rvec[2]);
01273                 
01274                 p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE;
01275             }
01276                 break;
01277             
01278             case SPACE_NODE:
01279             {
01280                 p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
01281             }
01282                 break;
01283 #if 0 // XXX other spacetypes to be restored in due course
01284             case SPACE_SEQ:
01285             {
01286                 SpaceSeq *sseq= (SpaceSeq *)p->sa->spacedata.first;
01287                 int rectx, recty;
01288                 float zoom, zoomx, zoomy;
01289                 
01290                 /* set draw 2d-stroke flag */
01291                 p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE;
01292                 
01293                 /* calculate zoom factor */
01294                 zoom= (float)(SEQ_ZOOM_FAC(sseq->zoom));
01295                 if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
01296                     zoomx = zoom * (p->scene->r.xasp / p->scene->r.yasp);
01297                     zoomy = zoom;
01298                 } 
01299                 else
01300                     zoomx = zoomy = zoom;
01301                 
01302                 /* calculate rect size to use to calculate the size of the drawing area
01303                  *  - We use the size of the output image not the size of the ibuf being shown
01304                  *    as it is too messy getting the ibuf (and could be too slow). This should be
01305                  *    a reasonable for most cases anyway.
01306                  */
01307                 rectx= (p->scene->r.size * p->scene->r.xsch) / 100;
01308                 recty= (p->scene->r.size * p->scene->r.ysch) / 100; 
01309                 
01310                 /* set offset and scale values for opertations to use */
01311                 p->im2d_settings.sizex= (int)(zoomx * rectx);
01312                 p->im2d_settings.sizey= (int)(zoomy * recty);
01313                 p->im2d_settings.offsx= (int)((p->sa->winx-p->im2d_settings.sizex)/2 + sseq->xof);
01314                 p->im2d_settings.offsy= (int)((p->sa->winy-p->im2d_settings.sizey)/2 + sseq->yof);
01315             }
01316                 break;
01317 #endif
01318             case SPACE_IMAGE:
01319             {
01320                 SpaceImage *sima= (SpaceImage *)p->sa->spacedata.first;
01321                 
01322                 /* only set these flags if the image editor doesn't have an image active,
01323                  * otherwise user will be confused by strokes not appearing after they're drawn
01324                  *
01325                  * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint!
01326                  */
01327                 if ELEM(NULL, sima, sima->image) {
01328                     /* make strokes be drawn in screen space */
01329                     p->gpd->sbuffer_sflag &= ~GP_STROKE_2DSPACE;
01330                     p->gpd->flag &= ~GP_DATA_VIEWALIGN;
01331                 }   
01332                 else
01333                     p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
01334             }
01335                 break;
01336                 
01337             case SPACE_CLIP:
01338             {
01339                 p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
01340             }
01341                 break;
01342         }
01343     }
01344 }
01345 
01346 /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
01347 static void gp_paint_strokeend (tGPsdata *p)
01348 {
01349     /* for surface sketching, need to set the right OpenGL context stuff so that 
01350      * the conversions will project the values correctly...
01351      */
01352     if (gpencil_project_check(p)) {
01353         View3D *v3d= p->sa->spacedata.first;
01354         
01355         /* need to restore the original projection settings before packing up */
01356         view3d_region_operator_needs_opengl(p->win, p->ar);
01357         ED_view3d_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1:0);
01358     }
01359     
01360     /* check if doing eraser or not */
01361     if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) {
01362         /* smooth stroke before transferring? */
01363         gp_stroke_smooth(p);
01364         
01365         /* simplify stroke before transferring? */
01366         gp_stroke_simplify(p);
01367         
01368         /* transfer stroke to frame */
01369         gp_stroke_newfrombuffer(p);
01370     }
01371     
01372     /* clean up buffer now */
01373     gp_session_validatebuffer(p);
01374 }
01375 
01376 /* finish off stroke painting operation */
01377 static void gp_paint_cleanup (tGPsdata *p)
01378 {
01379     /* p->gpd==NULL happens when stroke failed to initialize,
01380           for example. when GP is hidden in current space (sergey) */
01381     if (p->gpd) {
01382         /* finish off a stroke */
01383         gp_paint_strokeend(p);
01384     }
01385     
01386     /* "unlock" frame */
01387     if (p->gpf)
01388         p->gpf->flag &= ~GP_FRAME_PAINT;
01389 }
01390 
01391 /* ------------------------------- */
01392 
01393 static void gpencil_draw_exit (bContext *C, wmOperator *op)
01394 {
01395     tGPsdata *p= op->customdata;
01396     
01397     /* clear undo stack */
01398     gpencil_undo_finish();
01399     
01400     /* restore cursor to indicate end of drawing */
01401     WM_cursor_restore(CTX_wm_window(C));
01402     
01403     /* don't assume that operator data exists at all */
01404     if (p) {
01405         /* check size of buffer before cleanup, to determine if anything happened here */
01406         if (p->paintmode == GP_PAINTMODE_ERASER) {
01407             // TODO clear radial cursor thing
01408             // XXX draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0);
01409         }
01410         
01411         /* cleanup */
01412         gp_paint_cleanup(p);
01413         gp_session_cleanup(p);
01414         
01415         /* finally, free the temp data */
01416         MEM_freeN(p);   
01417     }
01418     
01419     op->customdata= NULL;
01420 }
01421 
01422 static int gpencil_draw_cancel (bContext *C, wmOperator *op)
01423 {
01424     /* this is just a wrapper around exit() */
01425     gpencil_draw_exit(C, op);
01426     return OPERATOR_CANCELLED;
01427 }
01428 
01429 /* ------------------------------- */
01430 
01431 
01432 static int gpencil_draw_init (bContext *C, wmOperator *op)
01433 {
01434     tGPsdata *p;
01435     int paintmode= RNA_enum_get(op->ptr, "mode");
01436     
01437     /* check context */
01438     p= op->customdata= gp_session_initpaint(C);
01439     if ((p == NULL) || (p->status == GP_STATUS_ERROR)) {
01440         /* something wasn't set correctly in context */
01441         gpencil_draw_exit(C, op);
01442         return 0;
01443     }
01444     
01445     /* init painting data */
01446     gp_paint_initstroke(p, paintmode);
01447     if (p->status == GP_STATUS_ERROR) {
01448         gpencil_draw_exit(C, op);
01449         return 0;
01450     }
01451     
01452     /* radius for eraser circle is defined in userprefs now */
01453     p->radius= U.gp_eraser;
01454     
01455     /* everything is now setup ok */
01456     return 1;
01457 }
01458 
01459 /* ------------------------------- */
01460 
01461 /* update UI indicators of status, including cursor and header prints */
01462 static void gpencil_draw_status_indicators (tGPsdata *p)
01463 {
01464     /* header prints */
01465     switch (p->status) {
01466         case GP_STATUS_PAINTING:
01467             /* only print this for paint-sessions, otherwise it gets annoying */
01468             if (GPENCIL_SKETCH_SESSIONS_ON(p->scene))
01469                 ED_area_headerprint(p->sa, "Grease Pencil: Drawing/erasing stroke... Release to end stroke");
01470             break;
01471         
01472         case GP_STATUS_IDLING:
01473             /* print status info */
01474             switch (p->paintmode) {
01475                 case GP_PAINTMODE_ERASER:
01476                     ED_area_headerprint(p->sa, "Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | ESC/Enter to end");
01477                     break;
01478                 case GP_PAINTMODE_DRAW_STRAIGHT:
01479                     ED_area_headerprint(p->sa, "Grease Pencil Line Session: Hold and drag LMB to draw | ESC/Enter to end");
01480                     break;
01481                 case GP_PAINTMODE_DRAW:
01482                     ED_area_headerprint(p->sa, "Grease Pencil Freehand Session: Hold and drag LMB to draw | ESC/Enter to end");
01483                     break;
01484                     
01485                 default: /* unhandled future cases */
01486                     ED_area_headerprint(p->sa, "Grease Pencil Session: ESC/Enter to end");
01487                     break;
01488             }
01489             break;
01490             
01491         case GP_STATUS_ERROR:
01492         case GP_STATUS_DONE:
01493             /* clear status string */
01494             ED_area_headerprint(p->sa, NULL);
01495             break;
01496     }
01497 }
01498 
01499 /* ------------------------------- */
01500 
01501 /* create a new stroke point at the point indicated by the painting context */
01502 static void gpencil_draw_apply (wmOperator *op, tGPsdata *p)
01503 {
01504     /* handle drawing/erasing -> test for erasing first */
01505     if (p->paintmode == GP_PAINTMODE_ERASER) {
01506         /* do 'live' erasing now */
01507         gp_stroke_doeraser(p);
01508         
01509         /* store used values */
01510         p->mvalo[0]= p->mval[0];
01511         p->mvalo[1]= p->mval[1];
01512         p->opressure= p->pressure;
01513     }
01514     /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */
01515     else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) {
01516         /* try to add point */
01517         short ok= gp_stroke_addpoint(p, p->mval, p->pressure);
01518         
01519         /* handle errors while adding point */
01520         if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
01521             /* finish off old stroke */
01522             gp_paint_strokeend(p);
01523             
01524             /* start a new stroke, starting from previous point */
01525             gp_stroke_addpoint(p, p->mvalo, p->opressure);
01526             gp_stroke_addpoint(p, p->mval, p->pressure);
01527         }
01528         else if (ok == GP_STROKEADD_INVALID) {
01529             /* the painting operation cannot continue... */
01530             BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
01531             p->status = GP_STATUS_ERROR;
01532             
01533             if (G.f & G_DEBUG) 
01534                 printf("Error: Grease-Pencil Paint - Add Point Invalid \n");
01535             return;
01536         }
01537         
01538         /* store used values */
01539         p->mvalo[0]= p->mval[0];
01540         p->mvalo[1]= p->mval[1];
01541         p->opressure= p->pressure;
01542     }
01543 }
01544 
01545 /* handle draw event */
01546 static void gpencil_draw_apply_event (wmOperator *op, wmEvent *event)
01547 {
01548     tGPsdata *p= op->customdata;
01549     PointerRNA itemptr;
01550     float mousef[2];
01551     int tablet=0;
01552 
01553     /* convert from window-space to area-space mouse coordintes */
01554     // NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding...
01555     p->mval[0]= event->mval[0] + 1;
01556     p->mval[1]= event->mval[1] + 1;
01557 
01558     /* handle pressure sensitivity (which is supplied by tablets) */
01559     if (event->custom == EVT_DATA_TABLET) {
01560         wmTabletData *wmtab= event->customdata;
01561         
01562         tablet= (wmtab->Active != EVT_TABLET_NONE);
01563         p->pressure= wmtab->Pressure;
01564         
01565         //if (wmtab->Active == EVT_TABLET_ERASER)
01566             // TODO... this should get caught by the keymaps which call drawing in the first place
01567     }
01568     else
01569         p->pressure= 1.0f;
01570     
01571     /* fill in stroke data (not actually used directly by gpencil_draw_apply) */
01572     RNA_collection_add(op->ptr, "stroke", &itemptr);
01573     
01574     mousef[0]= p->mval[0];
01575     mousef[1]= p->mval[1];
01576     RNA_float_set_array(&itemptr, "mouse", mousef);
01577     RNA_float_set(&itemptr, "pressure", p->pressure);
01578     RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN));
01579     
01580     /* special exception for start of strokes (i.e. maybe for just a dot) */
01581     if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
01582         p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
01583         
01584         p->mvalo[0]= p->mval[0];
01585         p->mvalo[1]= p->mval[1];
01586         p->opressure= p->pressure;
01587         
01588         /* special exception here for too high pressure values on first touch in
01589          *  windows for some tablets, then we just skip first touch ..  
01590          */
01591         if (tablet && (p->pressure >= 0.99f))
01592             return;
01593     }
01594     
01595     /* apply the current latest drawing point */
01596     gpencil_draw_apply(op, p);
01597     
01598     /* force refresh */
01599     ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */
01600 }
01601 
01602 /* ------------------------------- */
01603 
01604 /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */
01605 static int gpencil_draw_exec (bContext *C, wmOperator *op)
01606 {
01607     tGPsdata *p = NULL;
01608     
01609     //printf("GPencil - Starting Re-Drawing \n");
01610     
01611     /* try to initialise context data needed while drawing */
01612     if (!gpencil_draw_init(C, op)) {
01613         if (op->customdata) MEM_freeN(op->customdata);
01614         //printf("\tGP - no valid data \n");
01615         return OPERATOR_CANCELLED;
01616     }
01617     else
01618         p= op->customdata;
01619     
01620     //printf("\tGP - Start redrawing stroke \n");
01621     
01622     /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
01623      * setting the relevant values in context at each step, then applying
01624      */
01625     RNA_BEGIN(op->ptr, itemptr, "stroke") 
01626     {
01627         float mousef[2];
01628         
01629         //printf("\t\tGP - stroke elem \n");
01630         
01631         /* get relevant data for this point from stroke */
01632         RNA_float_get_array(&itemptr, "mouse", mousef);
01633         p->mval[0] = (int)mousef[0];
01634         p->mval[1] = (int)mousef[1];
01635         p->pressure= RNA_float_get(&itemptr, "pressure");
01636         
01637         if (RNA_boolean_get(&itemptr, "is_start")) {
01638             /* if first-run flag isn't set already (i.e. not true first stroke),
01639              * then we must terminate the previous one first before continuing
01640              */
01641             if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
01642                 // TODO: both of these ops can set error-status, but we probably don't need to worry
01643                 gp_paint_strokeend(p);
01644                 gp_paint_initstroke(p, p->paintmode);
01645             }
01646         }
01647         
01648         /* if first run, set previous data too */
01649         if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
01650             p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
01651             
01652             p->mvalo[0]= p->mval[0];
01653             p->mvalo[1]= p->mval[1];
01654             p->opressure= p->pressure;
01655         }
01656         
01657         /* apply this data as necessary now (as per usual) */
01658         gpencil_draw_apply(op, p);
01659     }
01660     RNA_END;
01661     
01662     //printf("\tGP - done \n");
01663     
01664     /* cleanup */
01665     gpencil_draw_exit(C, op);
01666     
01667     /* refreshes */
01668     WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
01669     
01670     /* done */
01671     return OPERATOR_FINISHED;
01672 }
01673 
01674 /* ------------------------------- */
01675 
01676 /* start of interactive drawing part of operator */
01677 static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event)
01678 {
01679     tGPsdata *p = NULL;
01680     wmWindow *win= CTX_wm_window(C);
01681     
01682     if (G.f & G_DEBUG)
01683         printf("GPencil - Starting Drawing \n");
01684     
01685     /* try to initialise context data needed while drawing */
01686     if (!gpencil_draw_init(C, op)) {
01687         if (op->customdata) 
01688             MEM_freeN(op->customdata);
01689         if (G.f & G_DEBUG)
01690             printf("\tGP - no valid data \n");
01691         return OPERATOR_CANCELLED;
01692     }
01693     else
01694         p= op->customdata;
01695     
01696     // TODO: set any additional settings that we can take from the events?
01697     // TODO? if tablet is erasing, force eraser to be on?
01698     
01699     // TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway...
01700     
01701     /* if eraser is on, draw radial aid */
01702     if (p->paintmode == GP_PAINTMODE_ERASER) {
01703         // TODO: this involves mucking around with radial control, so we leave this for now..
01704     }
01705     
01706     /* set cursor */
01707     if (p->paintmode == GP_PAINTMODE_ERASER)
01708         WM_cursor_modal(win, BC_CROSSCURSOR); // XXX need a better cursor
01709     else
01710         WM_cursor_modal(win, BC_PAINTBRUSHCURSOR);
01711     
01712     /* special hack: if there was an initial event, then we were invoked via a hotkey, and 
01713      * painting should start immediately. Otherwise, this was called from a toolbar, in which
01714      * case we should wait for the mouse to be clicked.
01715      */
01716     if (event->type) {
01717         /* hotkey invoked - start drawing */
01718         //printf("\tGP - set first spot\n");
01719         p->status= GP_STATUS_PAINTING;
01720         
01721         /* handle the initial drawing - i.e. for just doing a simple dot */
01722         gpencil_draw_apply_event(op, event);
01723     }
01724     else {
01725         /* toolbar invoked - don't start drawing yet... */
01726         //printf("\tGP - hotkey invoked... waiting for click-drag\n");
01727     }
01728     
01729     WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL, NULL);
01730     /* add a modal handler for this operator, so that we can then draw continuous strokes */
01731     WM_event_add_modal_handler(C, op);
01732     return OPERATOR_RUNNING_MODAL;
01733 }
01734 
01735 /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
01736 static int gpencil_area_exists(bContext *C, ScrArea *satest)
01737 {
01738     bScreen *sc= CTX_wm_screen(C);
01739     ScrArea *sa;
01740     
01741     for (sa= sc->areabase.first; sa; sa= sa->next) {
01742         if (sa==satest)
01743             return 1;
01744     }
01745     
01746     return 0;
01747 }
01748 
01749 static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
01750 {
01751     tGPsdata *p= op->customdata;
01752 
01753     /* we must check that we're still within the area that we're set up to work from
01754      * otherwise we could crash (see bug #20586)
01755      */
01756     if (CTX_wm_area(C) != p->sa) {
01757         printf("\t\t\tGP - wrong area execution abort! \n");
01758         p->status= GP_STATUS_ERROR;
01759     }
01760 
01761     //printf("\t\tGP - start stroke \n");
01762 
01763     /* we may need to set up paint env again if we're resuming */
01764     // XXX: watch it with the paintmode! in future, it'd be nice to allow changing paint-mode when in sketching-sessions
01765     // XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support
01766 
01767     if (gp_session_initdata(C, p))
01768         gp_paint_initstroke(p, p->paintmode);
01769 
01770     if(p->status != GP_STATUS_ERROR)
01771         p->status= GP_STATUS_PAINTING;
01772 
01773     return op->customdata;
01774 }
01775 
01776 static void gpencil_stroke_end(wmOperator *op)
01777 {
01778     tGPsdata *p= op->customdata;
01779 
01780     gp_paint_cleanup(p);
01781 
01782     gpencil_undo_push(p->gpd);
01783 
01784     gp_session_cleanup(p);
01785 
01786     p->status= GP_STATUS_IDLING;
01787 
01788     p->gpd= NULL;
01789     p->gpl= NULL;
01790     p->gpf= NULL;
01791 }
01792 
01793 /* events handling during interactive drawing part of operator */
01794 static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
01795 {
01796     tGPsdata *p= op->customdata;
01797     int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */
01798     
01799     // if (event->type == NDOF_MOTION)
01800     //  return OPERATOR_PASS_THROUGH;
01801     // -------------------------------
01802     // [mce] Not quite what I was looking
01803     // for, but a good start! GP continues to
01804     // draw on the screen while the 3D mouse
01805     // moves the viewpoint. Problem is that
01806     // the stroke is converted to 3D only after
01807     // it is finished. This approach should work
01808     // better in tools that immediately apply
01809     // in 3D space.
01810 
01811     //printf("\tGP - handle modal event...\n");
01812     
01813     /* exit painting mode (and/or end current stroke) */
01814     if (ELEM4(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) {
01815         /* exit() ends the current stroke before cleaning up */
01816         //printf("\t\tGP - end of paint op + end of stroke\n");
01817         p->status= GP_STATUS_DONE;
01818         estate = OPERATOR_FINISHED;
01819     }
01820     
01821     /* toggle painting mode upon mouse-button movement */
01822     if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE)) {
01823         /* if painting, end stroke */
01824         if (p->status == GP_STATUS_PAINTING) {
01825             int sketch= 0;
01826             /* basically, this should be mouse-button up = end stroke 
01827              * BUT what happens next depends on whether we 'painting sessions' is enabled
01828              */
01829             sketch |= GPENCIL_SKETCH_SESSIONS_ON(p->scene);
01830             /* polyline drawing is also 'sketching' -- all knots should be added during one session */
01831             sketch |= p->paintmode == GP_PAINTMODE_DRAW_POLY;
01832 
01833             if (sketch) {
01834                 /* end stroke only, and then wait to resume painting soon */
01835                 //printf("\t\tGP - end stroke only\n");
01836                 gpencil_stroke_end(op);
01837                 
01838                 /* we've just entered idling state, so this event was processed (but no others yet) */
01839                 estate = OPERATOR_RUNNING_MODAL;
01840 
01841                 /* stroke could be smoothed, send notifier to refresh screen */
01842                 WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL);
01843             }
01844             else {
01845                 //printf("\t\tGP - end of stroke + op\n");
01846                 p->status= GP_STATUS_DONE;
01847                 estate = OPERATOR_FINISHED;
01848             }
01849         }
01850         else if (event->val == KM_PRESS) {
01851             /* not painting, so start stroke (this should be mouse-button down) */
01852             p= gpencil_stroke_begin(C, op);
01853 
01854             if (p->status == GP_STATUS_ERROR) {
01855                 estate = OPERATOR_CANCELLED;
01856             }
01857         } 
01858         else {
01859             p->status = GP_STATUS_IDLING;
01860         }
01861     }
01862     
01863     /* handle mode-specific events */
01864     if (p->status == GP_STATUS_PAINTING) {
01865         /* handle painting mouse-movements? */
01866         if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) 
01867         {
01868             /* handle drawing event */
01869             //printf("\t\tGP - add point\n");
01870             gpencil_draw_apply_event(op, event);
01871             
01872             /* finish painting operation if anything went wrong just now */
01873             if (p->status == GP_STATUS_ERROR) {
01874                 printf("\t\t\t\tGP - add error done! \n");
01875                 estate = OPERATOR_CANCELLED;
01876             }
01877             else {
01878                 /* event handled, so just tag as running modal */
01879                 //printf("\t\t\t\tGP - add point handled!\n");
01880                 estate = OPERATOR_RUNNING_MODAL;
01881             }
01882         }
01883         /* there shouldn't be any other events, but just in case there are, let's swallow them 
01884          * (i.e. to prevent problems with with undo)
01885          */
01886         else {
01887             /* swallow event to save ourselves trouble */
01888             estate = OPERATOR_RUNNING_MODAL;
01889         }
01890     }
01891     
01892     /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
01893     if (0==gpencil_area_exists(C, p->sa))
01894         estate= OPERATOR_CANCELLED;
01895     else
01896         /* update status indicators - cursor, header, etc. */
01897         gpencil_draw_status_indicators(p);
01898     
01899     /* process last operations before exiting */
01900     switch (estate) {
01901         case OPERATOR_FINISHED:
01902             /* one last flush before we're done */
01903             gpencil_draw_exit(C, op);
01904             WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
01905             break;
01906             
01907         case OPERATOR_CANCELLED:
01908             gpencil_draw_exit(C, op);
01909             break;
01910 
01911         case OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH:
01912             /* event doesn't need to be handled */
01913             //printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE);
01914             break;
01915     }
01916     
01917     /* return status code */
01918     return estate;
01919 }
01920 
01921 /* ------------------------------- */
01922 
01923 static EnumPropertyItem prop_gpencil_drawmodes[] = {
01924     {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", ""},
01925     {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 0, "Draw Straight Lines", ""},
01926     {GP_PAINTMODE_DRAW_POLY, "DRAW_POLY", 0, "Draw Poly Line", ""},
01927     {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", ""},
01928     {0, NULL, 0, NULL, NULL}
01929 };
01930 
01931 void GPENCIL_OT_draw (wmOperatorType *ot)
01932 {
01933     /* identifiers */
01934     ot->name= "Grease Pencil Draw";
01935     ot->idname= "GPENCIL_OT_draw";
01936     ot->description= "Make annotations on the active data";
01937     
01938     /* api callbacks */
01939     ot->exec= gpencil_draw_exec;
01940     ot->invoke= gpencil_draw_invoke;
01941     ot->modal= gpencil_draw_modal;
01942     ot->cancel= gpencil_draw_cancel;
01943     ot->poll= gpencil_draw_poll;
01944     
01945     /* flags */
01946     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
01947     
01948     /* settings for drawing */
01949     RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to intepret mouse movements");
01950     
01951     RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
01952 }