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