Blender V2.61 - r43446
|
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 }