2.5 - Grease Pencil Version 2 (Crude rebirth)
[blender-staging.git] / source / blender / editors / gpencil / gpencil_edit.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008, Blender Foundation
21  * This is a new part of Blender
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27  
28 #if 0 // XXX COMPILE GUARDS FOR OLD CODE
29  
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <math.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "IMB_imbuf.h"
39 #include "IMB_imbuf_types.h"
40
41 #include "BLI_arithb.h"
42 #include "BLI_blenlib.h"
43
44 #include "DNA_listBase.h"
45 #include "DNA_armature_types.h"
46 #include "DNA_curve_types.h"
47 #include "DNA_gpencil_types.h"
48 #include "DNA_object_types.h"
49 #include "DNA_scene_types.h"
50 #include "DNA_screen_types.h"
51 #include "DNA_space_types.h"
52 #include "DNA_userdef_types.h"
53 #include "DNA_vec_types.h"
54 #include "DNA_view3d_types.h"
55
56 #include "BKE_armature.h"
57 #include "BKE_blender.h"
58 #include "BKE_context.h"
59 #include "BKE_curve.h"
60 #include "BKE_global.h"
61 #include "BKE_gpencil.h"
62 #include "BKE_image.h"
63 #include "BKE_library.h"
64 #include "BKE_utildefines.h"
65
66 #include "BIF_gl.h"
67 #include "BIF_glutil.h"
68
69 #include "WM_api.h"
70 #include "WM_types.h"
71
72 #include "UI_view2d.h"
73
74 #include "ED_armature.h"
75 #include "ED_gpencil.h"
76 #include "ED_sequencer.h"
77 #include "ED_view3d.h"
78
79 #include "PIL_time.h"                   /* sleep                                */
80
81 #include "gpencil_intern.h"
82
83 /* XXX */
84 static void BIF_undo_push() {}
85 static void error() {}
86 static int pupmenu() {return 0;}
87 static void add_object_draw() {}
88 static int get_activedevice() {return 0;}
89 #define L_MOUSE 0
90 #define R_MOUSE 0
91
92 /* ************************************************** */
93 /* XXX - OLD DEPRECEATED CODE... */
94
95 /* ----------- GP-Datablock API ------------- */
96
97 /* get the appropriate bGPdata from the active/given context */
98 // XXX region or region data?
99 bGPdata *gpencil_data_getactive (ScrArea *sa)
100 {
101         ScrArea *curarea= NULL; // XXX
102         
103         /* error checking */
104         if ((sa == NULL) && (curarea == NULL))
105                 return NULL;
106         if (sa == NULL)
107                 sa= curarea;
108                 
109         /* handle depending on spacetype */
110         switch (sa->spacetype) {
111                 case SPACE_VIEW3D:
112                 {
113                         View3D *v3d= sa->spacedata.first;
114                         return v3d->gpd;
115                 }
116                         break;
117                 case SPACE_NODE:
118                 {
119                         SpaceNode *snode= sa->spacedata.first;
120                         return snode->gpd;
121                 }
122                         break;
123                 case SPACE_SEQ:
124                 {
125                         SpaceSeq *sseq= sa->spacedata.first;
126                         
127                         /* only applicable for image modes */
128                         if (sseq->mainb != SEQ_DRAW_SEQUENCE)
129                                 return sseq->gpd;
130                 }
131                         break;
132                 case SPACE_IMAGE:
133                 {
134                         SpaceImage *sima= sa->spacedata.first;
135                         return sima->gpd;
136                 }
137                         break;
138         }
139         
140         /* nothing found */
141         return NULL;
142 }
143
144 /* set bGPdata for the active/given context, and return success/fail */
145 short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd)
146 {
147         ScrArea *curarea= NULL; // XXX
148         
149         /* error checking */
150         if ((sa == NULL) && (curarea == NULL))
151                 return 0;
152         if (gpd == NULL)
153                 return 0;
154         if (sa == NULL)
155                 sa= curarea;
156         
157         /* handle depending on spacetype */
158         // TODO: someday we should have multi-user data, so no need to loose old data
159         switch (sa->spacetype) {
160                 case SPACE_VIEW3D:
161                 {
162                         View3D *v3d= sa->spacedata.first;
163                         
164                         /* free the existing block */
165                         if (v3d->gpd)
166                                 free_gpencil_data(v3d->gpd);
167                         v3d->gpd= gpd;
168                         
169                         return 1;
170                 }
171                         break;
172                 case SPACE_NODE:
173                 {
174                         SpaceNode *snode= sa->spacedata.first;
175                         
176                         /* free the existing block */
177                         if (snode->gpd)
178                                 free_gpencil_data(snode->gpd);
179                         snode->gpd= gpd;
180                         
181                         /* set special settings */
182                         gpd->flag |= GP_DATA_VIEWALIGN;
183                         
184                         return 1;
185                 }
186                         break;
187                 case SPACE_SEQ:
188                 {
189                         SpaceSeq *sseq= sa->spacedata.first;
190                         
191                         /* only applicable if right mode */
192                         if (sseq->mainb != SEQ_DRAW_SEQUENCE) {
193                                 /* free the existing block */
194                                 if (sseq->gpd)
195                                         free_gpencil_data(sseq->gpd);
196                                 sseq->gpd= gpd;
197                                 
198                                 return 1;
199                         }
200                 }
201                         break;
202                 case SPACE_IMAGE:
203                 {
204                         SpaceImage *sima= sa->spacedata.first;
205                         
206                         if (sima->gpd)
207                                 free_gpencil_data(sima->gpd);
208                         sima->gpd= gpd;
209                         
210                         return 1;
211                 }
212                         break;
213         }
214         
215         /* failed to add */
216         return 0;
217 }
218
219 /* return the ScrArea that has the given GP-datablock
220  *      - assumes that only searching in current screen
221  *      - is based on GP-datablocks only being able to 
222  *        exist for one area at a time (i.e. not multiuser)
223  */
224 ScrArea *gpencil_data_findowner (bGPdata *gpd)
225 {
226         bScreen *curscreen= NULL; // XXX
227         ScrArea *sa;
228         
229         /* error checking */
230         if (gpd == NULL)
231                 return NULL;
232                 
233         /* loop over all scrareas for current screen, and check if that area has this gpd */
234         for (sa= curscreen->areabase.first; sa; sa= sa->next) {
235                 /* use get-active func to see if match */
236                 if (gpencil_data_getactive(sa) == gpd)
237                         return sa;
238         }
239         
240         /* not found */
241         return NULL;
242 }
243
244 /* ************************************************** */
245 /* GREASE-PENCIL EDITING - Tools */
246
247 /* --------- Data Deletion ---------- */
248
249 /* delete the last stroke on the active layer */
250 void gpencil_delete_laststroke (bGPdata *gpd, int cfra)
251 {
252         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
253         bGPDframe *gpf= gpencil_layer_getframe(gpl, cfra, 0);
254         
255         gpencil_frame_delete_laststroke(gpl, gpf);
256 }
257
258 /* delete the active frame */
259 void gpencil_delete_actframe (bGPdata *gpd, int cfra)
260 {
261         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
262         bGPDframe *gpf= gpencil_layer_getframe(gpl, cfra, 0);
263         
264         gpencil_layer_delframe(gpl, gpf);
265 }
266
267
268
269 /* delete various grase-pencil elements 
270  *      mode:   1 - last stroke
271  *                      2 - active frame
272  *                      3 - active layer
273  */
274 void gpencil_delete_operation (int cfra, short mode)
275 {
276         bGPdata *gpd;
277         
278         /* get datablock to work on */
279         gpd= gpencil_data_getactive(NULL);
280         if (gpd == NULL) return;
281         
282         switch (mode) {
283                 case 1: /* last stroke */
284                         gpencil_delete_laststroke(gpd, cfra);
285                         break;
286                 case 2: /* active frame */
287                         gpencil_delete_actframe(gpd, cfra);
288                         break;
289                 case 3: /* active layer */
290                         gpencil_layer_delactive(gpd);
291                         break;
292         }
293         
294         /* redraw and undo-push */
295         BIF_undo_push("GPencil Delete");
296 }
297
298 /* display a menu for deleting different grease-pencil elements */
299 void gpencil_delete_menu (void)
300 {
301         bGPdata *gpd= gpencil_data_getactive(NULL);
302         int cfra= 0; // XXX
303         short mode;
304         
305         /* only show menu if it will be relevant */
306         if (gpd == NULL) return;
307         
308         mode= pupmenu("Grease Pencil Erase...%t|Last Stroke%x1|Active Frame%x2|Active Layer%x3");
309         if (mode <= 0) return;
310         
311         gpencil_delete_operation(cfra, mode);
312 }
313
314 /* --------- Data Conversion ---------- */
315
316 /* convert the coordinates from the given stroke point into 3d-coordinates */
317 static void gp_strokepoint_convertcoords (bGPDstroke *gps, bGPDspoint *pt, float p3d[3])
318 {
319         ARegion *ar= NULL; // XXX
320         
321         if (gps->flag & GP_STROKE_3DSPACE) {
322                 /* directly use 3d-coordinates */
323                 VecCopyf(p3d, &pt->x);
324         }
325         else {
326                 short mval[2];
327                 int mx=0, my=0;
328                 float *fp= give_cursor(NULL, NULL); // XXX should be scene, v3d
329                 float dvec[3];
330                 
331                 /* get screen coordinate */
332                 if (gps->flag & GP_STROKE_2DSPACE) {
333                         // XXX
334                         // View2D *v2d= spacelink_get_view2d(curarea->spacedata.first);
335                         // UI_view2d_view_to_region(v2d, pt->x, pt->y, &mx, &my);
336                 }
337                 else {
338                         // XXX
339                         // mx= (short)(pt->x / 1000 * curarea->winx);
340                         // my= (short)(pt->y / 1000 * curarea->winy);
341                 }
342                 
343                 /* convert screen coordinate to 3d coordinates 
344                  *      - method taken from editview.c - mouse_cursor() 
345                  */
346                 project_short_noclip(ar, fp, mval);
347                 window_to_3d_delta(ar, dvec, mval[0]-mx, mval[1]-my);
348                 VecSubf(p3d, fp, dvec);
349         }
350 }
351
352 /* --- */
353
354 /* convert stroke to 3d path */
355 static void gp_stroke_to_path (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
356 {
357         bGPDspoint *pt;
358         Nurb *nu;
359         BPoint *bp;
360         int i;
361         
362         /* create new 'nurb' within the curve */
363         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
364         
365         nu->pntsu= gps->totpoints;
366         nu->pntsv= 1;
367         nu->orderu= gps->totpoints;
368         nu->flagu= 2;   /* endpoint */
369         nu->resolu= 32;
370         
371         nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints");
372         
373         /* add points */
374         for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) {
375                 float p3d[3];
376                 
377                 /* get coordinates to add at */
378                 gp_strokepoint_convertcoords(gps, pt, p3d);
379                 VecCopyf(bp->vec, p3d);
380                 
381                 /* set settings */
382                 bp->f1= SELECT;
383                 bp->radius = bp->weight = pt->pressure * gpl->thickness;
384         }
385         
386         /* add nurb to curve */
387         BLI_addtail(&cu->nurb, nu);
388 }
389
390 /* convert stroke to 3d bezier */
391 static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
392 {
393         bGPDspoint *pt;
394         Nurb *nu;
395         BezTriple *bezt;
396         int i;
397         
398         /* create new 'nurb' within the curve */
399         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
400         
401         nu->pntsu= gps->totpoints;
402         nu->resolu= 12;
403         nu->resolv= 12;
404         nu->type= CU_BEZIER;
405         nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts");
406         
407         /* add points */
408         for (i=0, pt=gps->points, bezt=nu->bezt; i < gps->totpoints; i++, pt++, bezt++) {
409                 float p3d[3];
410                 
411                 /* get coordinates to add at */
412                 gp_strokepoint_convertcoords(gps, pt, p3d);
413                 
414                 /* TODO: maybe in future the handles shouldn't be in same place */
415                 VecCopyf(bezt->vec[0], p3d);
416                 VecCopyf(bezt->vec[1], p3d);
417                 VecCopyf(bezt->vec[2], p3d);
418                 
419                 /* set settings */
420                 bezt->h1= bezt->h2= HD_FREE;
421                 bezt->f1= bezt->f2= bezt->f3= SELECT;
422                 bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f;
423         }
424         
425         /* must calculate handles or else we crash */
426         calchandlesNurb(nu);
427         
428         /* add nurb to curve */
429         BLI_addtail(&cu->nurb, nu);
430 }
431
432 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
433 static void gp_layer_to_curve (bGPdata *gpd, bGPDlayer *gpl, Scene *scene, short mode)
434 {
435         bGPDframe *gpf= gpencil_layer_getframe(gpl, scene->r.cfra, 0);
436         bGPDstroke *gps;
437         Object *ob;
438         Curve *cu;
439         
440         /* error checking */
441         if (ELEM3(NULL, gpd, gpl, gpf))
442                 return;
443                 
444         /* only convert if there are any strokes on this layer's frame to convert */
445         if (gpf->strokes.first == NULL)
446                 return;
447         
448         /* init the curve object (remove rotation and get curve data from it)
449          *      - must clear transforms set on object, as those skew our results
450          */
451         add_object_draw(OB_CURVE);
452         ob= OBACT;
453         ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
454         ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
455         cu= ob->data;
456         cu->flag |= CU_3D;
457         
458         /* rename object and curve to layer name */
459         rename_id((ID *)ob, gpl->info);
460         rename_id((ID *)cu, gpl->info);
461         
462         /* add points to curve */
463         for (gps= gpf->strokes.first; gps; gps= gps->next) {
464                 switch (mode) {
465                         case 1: 
466                                 gp_stroke_to_path(gpl, gps, cu);
467                                 break;
468                         case 2:
469                                 gp_stroke_to_bezier(gpl, gps, cu);
470                                 break;
471                 }
472         }
473 }
474
475 /* --- */
476
477 /* convert a stroke to a bone chain */
478 static void gp_stroke_to_bonechain (bGPDlayer *gpl, bGPDstroke *gps, bArmature *arm, ListBase *bones)
479 {
480         EditBone *ebo, *prev=NULL;
481         bGPDspoint *pt, *ptn;
482         int i;
483         
484         /* add each segment separately */
485         for (i=0, pt=gps->points, ptn=gps->points+1; i < (gps->totpoints-1); prev=ebo, i++, pt++, ptn++) {
486                 float p3da[3], p3db[3];
487                 
488                 /* get coordinates to add at */
489                 gp_strokepoint_convertcoords(gps, pt, p3da);
490                 gp_strokepoint_convertcoords(gps, ptn, p3db);
491                 
492                 /* allocate new bone */
493                 ebo= MEM_callocN(sizeof(EditBone), "eBone");
494                 
495                 VecCopyf(ebo->head, p3da);
496                 VecCopyf(ebo->tail, p3db);
497                 
498                 /* add new bone - note: sync with editarmature.c::add_editbone() */
499                 {
500                         BLI_strncpy(ebo->name, "Stroke", 32);
501                         unique_editbone_name(bones, ebo->name, NULL);
502                         
503                         BLI_addtail(bones, ebo);
504                         
505                         if (i > 0)
506                         {
507                                 ebo->flag |= BONE_CONNECTED;
508                         }
509                         ebo->weight= 1.0f;
510                         ebo->dist= 0.25f;
511                         ebo->xwidth= 0.1f;
512                         ebo->zwidth= 0.1f;
513                         ebo->ease1= 1.0f;
514                         ebo->ease2= 1.0f;
515                         ebo->rad_head= pt->pressure * gpl->thickness * 0.1f;
516                         ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1f;
517                         ebo->segments= 1;
518                         ebo->layer= arm->layer;
519                 }
520                 
521                 /* set parenting */
522                 ebo->parent= prev;
523         }
524 }
525
526 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
527 // XXX depreceated... we now have etch-a-ton for this...
528 static void gp_layer_to_armature (bGPdata *gpd, bGPDlayer *gpl, Scene *scene, View3D *v3d, short mode)
529 {
530         bGPDframe *gpf= gpencil_layer_getframe(gpl, scene->r.cfra, 0);
531         bGPDstroke *gps;
532         Object *ob;
533         bArmature *arm;
534         
535         /* error checking */
536         if (ELEM3(NULL, gpd, gpl, gpf))
537                 return;
538                 
539         /* only convert if there are any strokes on this layer's frame to convert */
540         if (gpf->strokes.first == NULL)
541                 return;
542         
543         /* init the armature object (remove rotation and assign armature data to it) 
544          *      - must clear transforms set on object, as those skew our results
545          */
546         add_object_draw(OB_ARMATURE);
547         ob= OBACT;
548         ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
549         ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
550         arm= ob->data;
551         
552         /* rename object and armature to layer name */
553         rename_id((ID *)ob, gpl->info);
554         rename_id((ID *)arm, gpl->info);
555         
556         /* this is editmode armature */
557         arm->edbo= MEM_callocN(sizeof(ListBase), "arm edbo");
558         
559         /* convert segments to bones, strokes to bone chains */
560         for (gps= gpf->strokes.first; gps; gps= gps->next) {
561                 gp_stroke_to_bonechain(gpl, gps, arm, arm->edbo);
562         }
563         
564         /* adjust roll of bones
565          *      - set object as EditMode object, but need to clear afterwards!
566          *      - use 'align to world z-up' option
567          */
568         {
569                 /* set our data as if we're in editmode to fool auto_align_armature() */
570                 scene->obedit= ob;
571                 
572                 /* WARNING: need to make sure this magic number doesn't change */
573                 auto_align_armature(scene, v3d, 2);     
574                 
575                 scene->obedit= NULL;
576         }
577         
578         /* flush editbones to armature */
579         ED_armature_from_edit(scene, ob);
580         ED_armature_edit_free(ob);
581 }
582
583 /* --- */
584
585 /* convert grease-pencil strokes to another representation 
586  *      mode:   1 - Active layer to path
587  *                      2 - Active layer to bezier
588  *                      3 - Active layer to armature
589  */
590 void gpencil_convert_operation (short mode)
591 {
592         Scene *scene= NULL; // XXX
593         View3D *v3d= NULL; // XXX
594         RegionView3D *rv3d= NULL; // XXX
595         bGPdata *gpd;   
596         float *fp= give_cursor(scene, v3d);
597         
598         /* get datablock to work on */
599         gpd= gpencil_data_getactive(NULL);
600         if (gpd == NULL) return;
601         
602         /* initialise 3d-cursor correction globals */
603         initgrabz(rv3d, fp[0], fp[1], fp[2]);
604         
605         /* handle selection modes */
606         switch (mode) {
607                 case 1: /* active layer only (to path) */
608                 case 2: /* active layer only (to bezier) */
609                 {
610                         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
611                         gp_layer_to_curve(gpd, gpl, scene, mode);
612                 }
613                         break;
614                 case 3: /* active layer only (to armature) */
615                 {
616                         bGPDlayer *gpl= gpencil_layer_getactive(gpd);
617                         gp_layer_to_armature(gpd, gpl, scene, v3d, mode);
618                 }
619                         break;
620         }
621         
622         /* redraw and undo-push */
623         BIF_undo_push("GPencil Convert");
624 }
625
626 /* ************************************************** */
627 /* GREASE-PENCIL EDITING MODE - Painting */
628
629 /* ---------- 'Globals' and Defines ----------------- */
630
631 /* maximum sizes of gp-session buffer */
632 #define GP_STROKE_BUFFER_MAX    5000
633
634 /* Macros for accessing sensitivity thresholds... */
635         /* minimum number of pixels mouse should move before new point created */
636 #define MIN_MANHATTEN_PX        (U.gp_manhattendist)
637         /* minimum length of new segment before new point can be added */
638 #define MIN_EUCLIDEAN_PX        (U.gp_euclideandist)
639
640 /* macro to test if only converting endpoints - only for use when converting!  */       
641 #define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (ctrl))
642         
643 /* ------ */
644
645 /* Temporary 'Stroke' Operation data */
646 typedef struct tGPsdata {
647         Scene *scene;       /* current scene from context */
648         ScrArea *sa;            /* area where painting originated */
649         ARegion *ar;        /* region where painting originated */
650         View2D *v2d;            /* needed for GP_STROKE_2DSPACE */
651         
652         ImBuf *ibuf;            /* needed for GP_STROKE_2DIMAGE */
653         struct IBufViewSettings {
654                 int offsx, offsy;                       /* offsets */
655                 int sizex, sizey;                       /* dimensions to use as scale-factor */
656         } im2d_settings;        /* needed for GP_STROKE_2DIMAGE */
657         
658         bGPdata *gpd;           /* gp-datablock layer comes from */
659         bGPDlayer *gpl;         /* layer we're working on */
660         bGPDframe *gpf;         /* frame we're working on */
661         
662         short status;           /* current status of painting */
663         short paintmode;        /* mode for painting */
664         
665         short mval[2];          /* current mouse-position */
666         short mvalo[2];         /* previous recorded mouse-position */
667         
668         float pressure;         /* current stylus pressure */
669         float opressure;        /* previous stylus pressure */
670         
671         short radius;           /* radius of influence for eraser */
672 } tGPsdata;
673
674 /* values for tGPsdata->status */
675 enum {
676         GP_STATUS_NORMAL = 0,   /* running normally */
677         GP_STATUS_ERROR,                /* something wasn't correctly set up */
678         GP_STATUS_DONE                  /* painting done */
679 };
680
681 /* values for tGPsdata->paintmode */
682 enum {
683         GP_PAINTMODE_DRAW = 0,
684         GP_PAINTMODE_ERASER
685 };
686
687 /* Return flags for adding points to stroke buffer */
688 enum {
689         GP_STROKEADD_INVALID    = -2,           /* error occurred - insufficient info to do so */
690         GP_STROKEADD_OVERFLOW   = -1,           /* error occurred - cannot fit any more points */
691         GP_STROKEADD_NORMAL,                            /* point was successfully added */
692         GP_STROKEADD_FULL                                       /* cannot add any more points to buffer */
693 };
694
695 /* ---------- Stroke Editing ------------ */
696
697 /* clear the session buffers (call this before AND after a paint operation) */
698 static void gp_session_validatebuffer (tGPsdata *p)
699 {
700         bGPdata *gpd= p->gpd;
701         
702         /* clear memory of buffer (or allocate it if starting a new session) */
703         if (gpd->sbuffer)
704                 memset(gpd->sbuffer, 0, sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX);
705         else
706                 gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
707         
708         /* reset indices */
709         gpd->sbuffer_size = 0;
710         
711         /* reset flags */
712         gpd->sbuffer_sflag= 0;
713 }
714
715 /* check if the current mouse position is suitable for adding a new point */
716 static short gp_stroke_filtermval (tGPsdata *p, short mval[2], short pmval[2])
717 {
718         short dx= abs(mval[0] - pmval[0]);
719         short dy= abs(mval[1] - pmval[1]);
720         
721         /* check if mouse moved at least certain distance on both axes (best case) */
722         if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX))
723                 return 1;
724         
725         /* check if the distance since the last point is significant enough */
726         // future optimisation: sqrt here may be too slow?
727         else if (sqrt(dx*dx + dy*dy) > MIN_EUCLIDEAN_PX)
728                 return 1;
729         
730         /* mouse 'didn't move' */
731         else
732                 return 0;
733 }
734
735 /* convert screen-coordinates to buffer-coordinates */
736 static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[])
737 {
738         bGPdata *gpd= p->gpd;
739         
740         /* in 3d-space - pt->x/y/z are 3 side-by-side floats */
741         if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) {
742                 const short mx=mval[0], my=mval[1];
743                 float *fp= give_cursor(p->scene, NULL); // XXX NULL could be v3d
744                 float dvec[3];
745                 
746                 /* Current method just converts each point in screen-coordinates to 
747                  * 3D-coordinates using the 3D-cursor as reference. In general, this 
748                  * works OK, but it could of course be improved.
749                  *
750                  * TODO:
751                  *      - investigate using nearest point(s) on a previous stroke as
752                  *        reference point instead or as offset, for easier stroke matching
753                  *      - investigate projection onto geometry (ala retopo)
754                  */
755                 
756                 /* method taken from editview.c - mouse_cursor() */
757                 project_short_noclip(p->ar, fp, mval);
758                 window_to_3d_delta(p->ar, dvec, mval[0]-mx, mval[1]-my);
759                 VecSubf(out, fp, dvec);
760         }
761         
762         /* 2d - on 'canvas' (assume that p->v2d is set) */
763         else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) {
764                 float x, y;
765                 
766                 UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &x, &y);
767                 
768                 out[0]= x;
769                 out[1]= y;
770         }
771         
772         /* 2d - on image 'canvas' (assume that p->v2d is set) */
773         else if (gpd->sbuffer_sflag & GP_STROKE_2DIMAGE) {
774                 int sizex, sizey, offsx, offsy;
775                 
776                 /* get stored settings 
777                  *      - assume that these have been set already (there are checks that set sane 'defaults' just in case)
778                  */
779                 sizex= p->im2d_settings.sizex;
780                 sizey= p->im2d_settings.sizey;
781                 offsx= p->im2d_settings.offsx;
782                 offsy= p->im2d_settings.offsy;
783                 
784                 /* calculate new points */
785                 out[0]= (float)(mval[0] - offsx) / (float)sizex;
786                 out[1]= (float)(mval[1] - offsy) / (float)sizey;
787         }
788         
789         /* 2d - relative to screen (viewport area) */
790         else {
791                 out[0] = (float)(mval[0]) / (float)(p->sa->winx) * 1000;
792                 out[1] = (float)(mval[1]) / (float)(p->sa->winy) * 1000;
793         }
794 }
795
796 /* add current stroke-point to buffer (returns whether point was successfully added) */
797 static short gp_stroke_addpoint (tGPsdata *p, short mval[2], float pressure)
798 {
799         bGPdata *gpd= p->gpd;
800         tGPspoint *pt;
801         
802         /* check if still room in buffer */
803         if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX)
804                 return GP_STROKEADD_OVERFLOW;
805         
806         /* get pointer to destination point */
807         pt= ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size);
808         
809         /* store settings */
810         pt->x= mval[0];
811         pt->y= mval[1];
812         pt->pressure= pressure;
813         
814         /* increment counters */
815         gpd->sbuffer_size++;
816         
817         /* check if another operation can still occur */
818         if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX)
819                 return GP_STROKEADD_FULL;
820         else
821                 return GP_STROKEADD_NORMAL;
822 }
823
824 /* smooth a stroke (in buffer) before storing it */
825 static void gp_stroke_smooth (tGPsdata *p)
826 {
827         bGPdata *gpd= p->gpd;
828         int i=0, cmx=gpd->sbuffer_size;
829         int ctrl= 0; // XXX
830         
831         /* only smooth if smoothing is enabled, and we're not doing a straight line */
832         if (!(U.gp_settings & GP_PAINT_DOSMOOTH) || GP_BUFFER2STROKE_ENDPOINTS)
833                 return;
834         
835         /* don't try if less than 2 points in buffer */
836         if ((cmx <= 2) || (gpd->sbuffer == NULL))
837                 return;
838         
839         /* apply weighting-average (note doing this along path sequentially does introduce slight error) */
840         for (i=0; i < gpd->sbuffer_size; i++) {
841                 tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
842                 tGPspoint *pb= (i-1 > 0)?(pc-1):(pc);
843                 tGPspoint *pa= (i-2 > 0)?(pc-2):(pb);
844                 tGPspoint *pd= (i+1 < cmx)?(pc+1):(pc);
845                 tGPspoint *pe= (i+2 < cmx)?(pc+2):(pd);
846                 
847                 pc->x= (short)(0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x);
848                 pc->y= (short)(0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y);
849         }
850 }
851
852 /* simplify a stroke (in buffer) before storing it 
853  *      - applies a reverse Chaikin filter
854  *      - code adapted from etch-a-ton branch (editarmature_sketch.c)
855  */
856 static void gp_stroke_simplify (tGPsdata *p)
857 {
858         bGPdata *gpd= p->gpd;
859         tGPspoint *old_points= (tGPspoint *)gpd->sbuffer;
860         short num_points= gpd->sbuffer_size;
861         short flag= gpd->sbuffer_sflag;
862         short i, j;
863         int ctrl= 0; // XXX
864         
865         /* only simplify if simlification is enabled, and we're not doing a straight line */
866         if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || GP_BUFFER2STROKE_ENDPOINTS)
867                 return;
868         
869         /* don't simplify if less than 4 points in buffer */
870         if ((num_points <= 2) || (old_points == NULL))
871                 return;
872                 
873         /* clear buffer (but don't free mem yet) so that we can write to it 
874          *      - firstly set sbuffer to NULL, so a new one is allocated
875          *      - secondly, reset flag after, as it gets cleared auto
876          */
877         gpd->sbuffer= NULL;
878         gp_session_validatebuffer(p);
879         gpd->sbuffer_sflag = flag;
880         
881 /* macro used in loop to get position of new point
882  *      - used due to the mixture of datatypes in use here
883  */
884 #define GP_SIMPLIFY_AVPOINT(offs, sfac) \
885         { \
886                 co[0] += (float)(old_points[offs].x * sfac); \
887                 co[1] += (float)(old_points[offs].y * sfac); \
888                 pressure += old_points[offs].pressure * sfac; \
889         }
890         
891         for (i = 0, j = 0; i < num_points; i++)
892         {
893                 if (i - j == 3)
894                 {
895                         float co[2], pressure;
896                         short mco[2];
897                         
898                         /* initialise values */
899                         co[0]= 0;
900                         co[1]= 0;
901                         pressure = 0;
902                         
903                         /* using macro, calculate new point */
904                         GP_SIMPLIFY_AVPOINT(j, -0.25f);
905                         GP_SIMPLIFY_AVPOINT(j+1, 0.75f);
906                         GP_SIMPLIFY_AVPOINT(j+2, 0.75f);
907                         GP_SIMPLIFY_AVPOINT(j+3, -0.25f);
908                         
909                         /* set values for adding */
910                         mco[0]= (short)co[0];
911                         mco[1]= (short)co[1];
912                         
913                         /* ignore return values on this... assume to be ok for now */
914                         gp_stroke_addpoint(p, mco, pressure);
915                         
916                         j += 2;
917                 }
918         } 
919         
920         /* free old buffer */
921         MEM_freeN(old_points);
922 }
923
924
925 /* make a new stroke from the buffer data */
926 static void gp_stroke_newfrombuffer (tGPsdata *p)
927 {
928         bGPdata *gpd= p->gpd;
929         bGPDstroke *gps;
930         bGPDspoint *pt;
931         tGPspoint *ptc;
932         int i, totelem;
933         int ctrl= 0; // XXX
934         
935         /* get total number of points to allocate space for:
936          *      - in 'Draw Mode', holding the Ctrl-Modifier will only take endpoints
937          *      - otherwise, do whole stroke
938          */
939         if (GP_BUFFER2STROKE_ENDPOINTS)
940                 totelem = (gpd->sbuffer_size >= 2) ? 2: gpd->sbuffer_size;
941         else
942                 totelem = gpd->sbuffer_size;
943         
944         /* exit with error if no valid points from this stroke */
945         if (totelem == 0) {
946                 if (G.f & G_DEBUG) 
947                         printf("Error: No valid points in stroke buffer to convert (tot=%d) \n", gpd->sbuffer_size);
948                 return;
949         }
950         
951         /* allocate memory for a new stroke */
952         gps= MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
953         
954         /* allocate enough memory for a continuous array for storage points */
955         pt= gps->points= MEM_callocN(sizeof(bGPDspoint)*totelem, "gp_stroke_points");
956         
957         /* copy appropriate settings for stroke */
958         gps->totpoints= totelem;
959         gps->thickness= p->gpl->thickness;
960         gps->flag= gpd->sbuffer_sflag;
961         
962         /* copy points from the buffer to the stroke */
963         if (GP_BUFFER2STROKE_ENDPOINTS) {
964                 /* 'Draw Mode' + Ctrl-Modifier - only endpoints */
965                 {
966                         /* first point */
967                         ptc= gpd->sbuffer;
968                         
969                         /* convert screen-coordinates to appropriate coordinates (and store them) */
970                         gp_stroke_convertcoords(p, &ptc->x, &pt->x);
971                         
972                         /* copy pressure */
973                         pt->pressure= ptc->pressure;
974                         
975                         pt++;
976                 }
977                         
978                 if (totelem == 2) {
979                         /* last point if applicable */
980                         ptc= ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1);
981                         
982                         /* convert screen-coordinates to appropriate coordinates (and store them) */
983                         gp_stroke_convertcoords(p, &ptc->x, &pt->x);
984                         
985                         /* copy pressure */
986                         pt->pressure= ptc->pressure;
987                 }
988         }
989         else {
990                 /* convert all points (normal behaviour) */
991                 for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++) {
992                         /* convert screen-coordinates to appropriate coordinates (and store them) */
993                         gp_stroke_convertcoords(p, &ptc->x, &pt->x);
994                         
995                         /* copy pressure */
996                         pt->pressure= ptc->pressure;
997                         
998                         pt++;
999                 }
1000         }
1001         
1002         /* add stroke to frame */
1003         BLI_addtail(&p->gpf->strokes, gps);
1004 }
1005
1006 /* --- 'Eraser' for 'Paint' Tool ------ */
1007
1008 /* eraser tool - remove segment from stroke/split stroke (after lasso inside) */
1009 static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i)
1010 {
1011         bGPDspoint *pt_tmp= gps->points;
1012         bGPDstroke *gsn = NULL;
1013
1014         /* if stroke only had two points, get rid of stroke */
1015         if (gps->totpoints == 2) {
1016                 /* free stroke points, then stroke */
1017                 MEM_freeN(pt_tmp);
1018                 BLI_freelinkN(&gpf->strokes, gps);
1019                 
1020                 /* nothing left in stroke, so stop */
1021                 return 1;
1022         }
1023
1024         /* if last segment, just remove segment from the stroke */
1025         else if (i == gps->totpoints - 2) {
1026                 /* allocate new points array, and assign most of the old stroke there */
1027                 gps->totpoints--;
1028                 gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
1029                 memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*gps->totpoints);
1030                 
1031                 /* free temp buffer */
1032                 MEM_freeN(pt_tmp);
1033                 
1034                 /* nothing left in stroke, so stop */
1035                 return 1;
1036         }
1037
1038         /* if first segment, just remove segment from the stroke */
1039         else if (i == 0) {
1040                 /* allocate new points array, and assign most of the old stroke there */
1041                 gps->totpoints--;
1042                 gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
1043                 memcpy(gps->points, pt_tmp + 1, sizeof(bGPDspoint)*gps->totpoints);
1044                 
1045                 /* free temp buffer */
1046                 MEM_freeN(pt_tmp);
1047                 
1048                 /* no break here, as there might still be stuff to remove in this stroke */
1049                 return 0;
1050         }
1051
1052         /* segment occurs in 'middle' of stroke, so split */
1053         else {
1054                 /* duplicate stroke, and assign 'later' data to that stroke */
1055                 gsn= MEM_dupallocN(gps);
1056                 gsn->prev= gsn->next= NULL;
1057                 BLI_insertlinkafter(&gpf->strokes, gps, gsn);
1058                 
1059                 gsn->totpoints= gps->totpoints - i;
1060                 gsn->points= MEM_callocN(sizeof(bGPDspoint)*gsn->totpoints, "gp_stroke_points");
1061                 memcpy(gsn->points, pt_tmp + i, sizeof(bGPDspoint)*gsn->totpoints);
1062                 
1063                 /* adjust existing stroke  */
1064                 gps->totpoints= i;
1065                 gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
1066                 memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*i);
1067                 
1068                 /* free temp buffer */
1069                 MEM_freeN(pt_tmp);
1070                 
1071                 /* nothing left in stroke, so stop */
1072                 return 1;
1073         }
1074 }
1075
1076 /* eraser tool - check if part of stroke occurs within last segment drawn by eraser */
1077 static short gp_stroke_eraser_strokeinside (short mval[], short mvalo[], short rad, short x0, short y0, short x1, short y1)
1078 {
1079         /* simple within-radius check for now */
1080         if (edge_inside_circle(mval[0], mval[1], rad, x0, y0, x1, y1))
1081                 return 1;
1082         
1083         /* not inside */
1084         return 0;
1085
1086
1087 /* eraser tool - evaluation per stroke */
1088 static void gp_stroke_eraser_dostroke (tGPsdata *p, short mval[], short mvalo[], short rad, rcti *rect, bGPDframe *gpf, bGPDstroke *gps)
1089 {
1090         bGPDspoint *pt1, *pt2;
1091         int x0=0, y0=0, x1=0, y1=0;
1092         short xyval[2];
1093         int i;
1094         
1095         if (gps->totpoints == 0) {
1096                 /* just free stroke */
1097                 if (gps->points) 
1098                         MEM_freeN(gps->points);
1099                 BLI_freelinkN(&gpf->strokes, gps);
1100         }
1101         else if (gps->totpoints == 1) {
1102                 /* get coordinates */
1103                 if (gps->flag & GP_STROKE_3DSPACE) {
1104                         project_short(p->ar, &gps->points->x, xyval);
1105                         x0= xyval[0];
1106                         y0= xyval[1];
1107                 }
1108                 else if (gps->flag & GP_STROKE_2DSPACE) {                       
1109                         UI_view2d_view_to_region(p->v2d, gps->points->x, gps->points->y, &x0, &y0);
1110                 }
1111                 else if (gps->flag & GP_STROKE_2DIMAGE) {                       
1112                         int offsx, offsy, sizex, sizey;
1113                         
1114                         /* get stored settings */
1115                         sizex= p->im2d_settings.sizex;
1116                         sizey= p->im2d_settings.sizey;
1117                         offsx= p->im2d_settings.offsx;
1118                         offsy= p->im2d_settings.offsy;
1119                         
1120                         /* calculate new points */
1121                         x0= (short)((gps->points->x * sizex) + offsx);
1122                         y0= (short)((gps->points->y * sizey) + offsy);
1123                 }
1124                 else {
1125                         x0= (short)(gps->points->x / 1000 * p->sa->winx);
1126                         y0= (short)(gps->points->y / 1000 * p->sa->winy);
1127                 }
1128                 
1129                 /* do boundbox check first */
1130                 if (BLI_in_rcti(rect, x0, y0)) {
1131                         /* only check if point is inside */
1132                         if ( ((x0-mval[0])*(x0-mval[0]) + (y0-mval[1])*(y0-mval[1])) <= rad*rad ) {
1133                                 /* free stroke */
1134                                 MEM_freeN(gps->points);
1135                                 BLI_freelinkN(&gpf->strokes, gps);
1136                         }
1137                 }
1138         }
1139         else {  
1140                 /* loop over the points in the stroke, checking for intersections 
1141                  *      - an intersection will require the stroke to be split
1142                  */
1143                 for (i=0; (i+1) < gps->totpoints; i++) {
1144                         /* get points to work with */
1145                         pt1= gps->points + i;
1146                         pt2= gps->points + i + 1;
1147                         
1148                         /* get coordinates */
1149                         if (gps->flag & GP_STROKE_3DSPACE) {
1150                                 project_short(p->ar, &pt1->x, xyval);
1151                                 x0= xyval[0];
1152                                 y0= xyval[1];
1153                                 
1154                                 project_short(p->ar, &pt2->x, xyval);
1155                                 x1= xyval[0];
1156                                 y1= xyval[1];
1157                         }
1158                         else if (gps->flag & GP_STROKE_2DSPACE) {
1159                                 UI_view2d_view_to_region(p->v2d, pt1->x, pt1->y, &x0, &y0);
1160                                 
1161                                 UI_view2d_view_to_region(p->v2d, pt2->x, pt2->y, &x1, &y1);
1162                         }
1163                         else if (gps->flag & GP_STROKE_2DIMAGE) {
1164                                 int offsx, offsy, sizex, sizey;
1165                                 
1166                                 /* get stored settings */
1167                                 sizex= p->im2d_settings.sizex;
1168                                 sizey= p->im2d_settings.sizey;
1169                                 offsx= p->im2d_settings.offsx;
1170                                 offsy= p->im2d_settings.offsy;
1171                                 
1172                                 /* calculate new points */
1173                                 x0= (short)((pt1->x * sizex) + offsx);
1174                                 y0= (short)((pt1->y * sizey) + offsy);
1175                                 
1176                                 x1= (short)((pt2->x * sizex) + offsx);
1177                                 y1= (short)((pt2->y * sizey) + offsy);
1178                         }
1179                         else {
1180                                 x0= (short)(pt1->x / 1000 * p->sa->winx);
1181                                 y0= (short)(pt1->y / 1000 * p->sa->winy);
1182                                 x1= (short)(pt2->x / 1000 * p->sa->winx);
1183                                 y1= (short)(pt2->y / 1000 * p->sa->winy);
1184                         }
1185                         
1186                         /* check that point segment of the boundbox of the eraser stroke */
1187                         if (BLI_in_rcti(rect, x0, y0) || BLI_in_rcti(rect, x1, y1)) {
1188                                 /* check if point segment of stroke had anything to do with
1189                                  * eraser region  (either within stroke painted, or on its lines)
1190                                  *      - this assumes that linewidth is irrelevant
1191                                  */
1192                                 if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) {
1193                                         /* if function returns true, break this loop (as no more point to check) */
1194                                         if (gp_stroke_eraser_splitdel(gpf, gps, i))
1195                                                 break;
1196                                 }
1197                         }
1198                 }
1199         }
1200 }
1201
1202 /* erase strokes which fall under the eraser strokes */
1203 static void gp_stroke_doeraser (tGPsdata *p)
1204 {
1205         bGPDframe *gpf= p->gpf;
1206         bGPDstroke *gps, *gpn;
1207         rcti rect;
1208         
1209         /* rect is rectangle of eraser */
1210         rect.xmin= p->mval[0] - p->radius;
1211         rect.ymin= p->mval[1] - p->radius;
1212         rect.xmax= p->mval[0] + p->radius;
1213         rect.ymax= p->mval[1] + p->radius;
1214         
1215         /* loop over strokes, checking segments for intersections */
1216         for (gps= gpf->strokes.first; gps; gps= gpn) {
1217                 gpn= gps->next;
1218                 gp_stroke_eraser_dostroke(p, p->mval, p->mvalo, p->radius, &rect, gpf, gps);
1219         }
1220 }
1221
1222 /* ---------- 'Paint' Tool ------------ */
1223
1224 /* init new painting session */
1225 static void gp_session_initpaint (bContext *C, tGPsdata *p)
1226 {
1227         ScrArea *curarea= CTX_wm_area(C);
1228         ARegion *ar= CTX_wm_region(C);
1229         
1230         /* clear previous data (note: is on stack) */
1231         memset(p, 0, sizeof(tGPsdata));
1232         
1233         /* make sure the active view (at the starting time) is a 3d-view */
1234         if (curarea == NULL) {
1235                 p->status= GP_STATUS_ERROR;
1236                 if (G.f & G_DEBUG) 
1237                         printf("Error: No active view for painting \n");
1238                 return;
1239         }
1240         
1241         /* pass on current scene */
1242         p->scene= CTX_data_scene(C);
1243         
1244         switch (curarea->spacetype) {
1245                 /* supported views first */
1246                 case SPACE_VIEW3D:
1247                 {
1248                         View3D *v3d= curarea->spacedata.first;
1249                         
1250                         /* set current area */
1251                         p->sa= curarea;
1252                         p->ar= ar;
1253                         
1254                         /* check that gpencil data is allowed to be drawn */
1255                         if ((v3d->flag2 & V3D_DISPGP)==0) {
1256                                 p->status= GP_STATUS_ERROR;
1257                                 if (G.f & G_DEBUG) 
1258                                         printf("Error: In active view, Grease Pencil not shown \n");
1259                                 return;
1260                         }
1261                 }
1262                         break;
1263                 case SPACE_NODE:
1264                 {
1265                         SpaceNode *snode= curarea->spacedata.first;
1266                         
1267                         /* set current area */
1268                         p->sa= curarea;
1269                         p->ar= ar;
1270                         p->v2d= &ar->v2d;
1271                         
1272                         /* check that gpencil data is allowed to be drawn */
1273                         if ((snode->flag & SNODE_DISPGP)==0) {
1274                                 p->status= GP_STATUS_ERROR;
1275                                 if (G.f & G_DEBUG) 
1276                                         printf("Error: In active view, Grease Pencil not shown \n");
1277                                 return;
1278                         }
1279                 }
1280                         break;
1281                 case SPACE_SEQ:
1282                 {
1283                         SpaceSeq *sseq= curarea->spacedata.first;
1284                         
1285                         /* set current area */
1286                         p->sa= curarea;
1287                         p->ar= ar;
1288                         p->v2d= &ar->v2d;
1289                         
1290                         /* check that gpencil data is allowed to be drawn */
1291                         if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
1292                                 p->status= GP_STATUS_ERROR;
1293                                 if (G.f & G_DEBUG) 
1294                                         printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil \n");
1295                                 return;
1296                         }
1297                         if ((sseq->flag & SEQ_DRAW_GPENCIL)==0) {
1298                                 p->status= GP_STATUS_ERROR;
1299                                 if (G.f & G_DEBUG) 
1300                                         printf("Error: In active view, Grease Pencil not shown \n");
1301                                 return;
1302                         }
1303                 }
1304                         break;  
1305                 case SPACE_IMAGE:
1306                 {
1307                         SpaceImage *sima= curarea->spacedata.first;
1308                         
1309                         /* set the current area */
1310                         p->sa= curarea;
1311                         p->ar= ar;
1312                         p->v2d= &ar->v2d;
1313                         p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
1314                         
1315                         /* check that gpencil data is allowed to be drawn */
1316                         if ((sima->flag & SI_DISPGP)==0) {
1317                                 p->status= GP_STATUS_ERROR;
1318                                 if (G.f & G_DEBUG)
1319                                         printf("Error: In active view, Grease Pencil not shown \n");
1320                                 return;
1321                         }
1322                 }
1323                         break;
1324                 /* unsupported views */
1325                 default:
1326                 {
1327                         p->status= GP_STATUS_ERROR;
1328                         if (G.f & G_DEBUG) 
1329                                 printf("Error: Active view not appropriate for Grease Pencil drawing \n");
1330                         return;
1331                 }
1332                         break;
1333         }
1334         
1335         /* get gp-data */
1336         p->gpd= gpencil_data_getactive(p->sa);
1337         if (p->gpd == NULL) {
1338                 short ok;
1339                 
1340                 p->gpd= gpencil_data_addnew("GPencil");
1341                 ok= gpencil_data_setactive(p->sa, p->gpd);
1342                 
1343                 /* most of the time, the following check isn't needed */
1344                 if (ok == 0) {
1345                         /* free gpencil data as it can't be used */
1346                         free_gpencil_data(p->gpd);
1347                         p->gpd= NULL;
1348                         p->status= GP_STATUS_ERROR;
1349                         if (G.f & G_DEBUG) 
1350                                 printf("Error: Could not assign newly created Grease Pencil data to active area \n");
1351                         return;
1352                 }
1353         }
1354         
1355         /* set edit flags */
1356         G.f |= G_GREASEPENCIL;
1357         
1358         /* clear out buffer (stored in gp-data) in case something contaminated it */
1359         gp_session_validatebuffer(p);
1360         
1361         /* set 'default' im2d_settings just in case something that uses this doesn't set it */
1362         p->im2d_settings.sizex= 1;
1363         p->im2d_settings.sizey= 1;
1364 }
1365
1366 /* cleanup after a painting session */
1367 static void gp_session_cleanup (tGPsdata *p)
1368 {
1369         bGPdata *gpd= p->gpd;
1370         
1371         /* error checking */
1372         if (gpd == NULL)
1373                 return;
1374         
1375         /* free stroke buffer */
1376         if (gpd->sbuffer) {
1377                 MEM_freeN(gpd->sbuffer);
1378                 gpd->sbuffer= NULL;
1379         }
1380         
1381         /* clear flags */
1382         gpd->sbuffer_size= 0;
1383         gpd->sbuffer_sflag= 0;
1384 }
1385
1386 /* init new stroke */
1387 static void gp_paint_initstroke (tGPsdata *p, short paintmode)
1388 {       
1389         /* get active layer (or add a new one if non-existent) */
1390         p->gpl= gpencil_layer_getactive(p->gpd);
1391         if (p->gpl == NULL)
1392                 p->gpl= gpencil_layer_addnew(p->gpd);
1393         if (p->gpl->flag & GP_LAYER_LOCKED) {
1394                 p->status= GP_STATUS_ERROR;
1395                 if (G.f & G_DEBUG)
1396                         printf("Error: Cannot paint on locked layer \n");
1397                 return;
1398         }
1399                 
1400         /* get active frame (add a new one if not matching frame) */
1401         p->gpf= gpencil_layer_getframe(p->gpl, p->scene->r.cfra, 1);
1402         if (p->gpf == NULL) {
1403                 p->status= GP_STATUS_ERROR;
1404                 if (G.f & G_DEBUG) 
1405                         printf("Error: No frame created (gpencil_paint_init) \n");
1406                 return;
1407         }
1408         else
1409                 p->gpf->flag |= GP_FRAME_PAINT;
1410         
1411         /* set 'eraser' for this stroke if using eraser */
1412         p->paintmode= paintmode;
1413         if (p->paintmode == GP_PAINTMODE_ERASER)
1414                 p->gpd->sbuffer_sflag |= GP_STROKE_ERASER;
1415         
1416         /* check if points will need to be made in view-aligned space */
1417         if (p->gpd->flag & GP_DATA_VIEWALIGN) {
1418                 switch (p->sa->spacetype) {
1419                         case SPACE_VIEW3D:
1420                         {
1421                                 View3D *v3d= (View3D *)p->sa->spacedata.first;
1422                                 RegionView3D *rv3d= NULL; // XXX
1423                                 float *fp= give_cursor(p->scene, v3d);
1424                                 
1425                                 initgrabz(rv3d, fp[0], fp[1], fp[2]);
1426                                 
1427                                 p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE;
1428                         }
1429                                 break;
1430                         case SPACE_NODE:
1431                         {
1432                                 p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
1433                         }
1434                                 break;
1435                         case SPACE_SEQ:
1436                         {
1437                                 SpaceSeq *sseq= (SpaceSeq *)p->sa->spacedata.first;
1438                                 int rectx, recty;
1439                                 float zoom, zoomx, zoomy;
1440                                 
1441                                 /* set draw 2d-stroke flag */
1442                                 p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE;
1443                                 
1444                                 /* calculate zoom factor */
1445                                 zoom= (float)(SEQ_ZOOM_FAC(sseq->zoom));
1446                                 if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
1447                                         zoomx = zoom * ((float)p->scene->r.xasp / (float)p->scene->r.yasp);
1448                                         zoomy = zoom;
1449                                 } 
1450                                 else
1451                                         zoomx = zoomy = zoom;
1452                                 
1453                                 /* calculate rect size to use to calculate the size of the drawing area
1454                                  *      - We use the size of the output image not the size of the ibuf being shown
1455                                  *        as it is too messy getting the ibuf (and could be too slow). This should be
1456                                  *        a reasonable for most cases anyway.
1457                                  */
1458                                 rectx= (p->scene->r.size * p->scene->r.xsch) / 100;
1459                                 recty= (p->scene->r.size * p->scene->r.ysch) / 100; 
1460                                 
1461                                 /* set offset and scale values for opertations to use */
1462                                 p->im2d_settings.sizex= (int)(zoomx * rectx);
1463                                 p->im2d_settings.sizey= (int)(zoomy * recty);
1464                                 p->im2d_settings.offsx= (int)((p->sa->winx-p->im2d_settings.sizex)/2 + sseq->xof);
1465                                 p->im2d_settings.offsy= (int)((p->sa->winy-p->im2d_settings.sizey)/2 + sseq->yof);
1466                         }
1467                                 break;
1468                         case SPACE_IMAGE:
1469                         {
1470                                 /* check if any ibuf available */
1471                                 if (p->ibuf)
1472                                         p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
1473                         }
1474                                 break;
1475                 }
1476         }
1477 }
1478
1479 /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
1480 static void gp_paint_strokeend (tGPsdata *p)
1481 {
1482         /* check if doing eraser or not */
1483         if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) {
1484                 /* smooth stroke before transferring? */
1485                 gp_stroke_smooth(p);
1486                 
1487                 /* simplify stroke before transferring? */
1488                 gp_stroke_simplify(p);
1489                 
1490                 /* transfer stroke to frame */
1491                 gp_stroke_newfrombuffer(p);
1492         }
1493         
1494         /* clean up buffer now */
1495         gp_session_validatebuffer(p);
1496 }
1497
1498 /* finish off stroke painting operation */
1499 static void gp_paint_cleanup (tGPsdata *p)
1500 {
1501         /* finish off a stroke */
1502         gp_paint_strokeend(p);
1503         
1504         /* "unlock" frame */
1505         p->gpf->flag &= ~GP_FRAME_PAINT;
1506         
1507         /* add undo-push so stroke can be undone */
1508         /* FIXME: currently disabled, as it's impossible to get this working nice
1509          * as gpenci data is on currently screen-level (which isn't saved to undo files)
1510          */
1511         //BIF_undo_push("GPencil Stroke");
1512         
1513         /* force redraw after drawing action */
1514         // XXX force_draw_plus(SPACE_ACTION, 0);
1515 }
1516
1517 /* -------- */
1518
1519 /* main call to paint a new stroke */
1520 // XXX will become modal(), gets event, includes all info!
1521 short gpencil_paint (bContext *C, short paintmode)
1522 {
1523         tGPsdata p;
1524         short ok = GP_STROKEADD_NORMAL;
1525         
1526         /* init paint-data */
1527         gp_session_initpaint(C, &p);
1528         if (p.status == GP_STATUS_ERROR) {
1529                 gp_session_cleanup(&p);
1530                 return 0;
1531         }
1532         gp_paint_initstroke(&p, paintmode);
1533         if (p.status == GP_STATUS_ERROR) {
1534                 gp_session_cleanup(&p);
1535                 return 0;
1536         }
1537         
1538         /* set cursor to indicate drawing */
1539         // XXX (cursor callbacks in regiontype) setcursor_space(p.sa->spacetype, CURSOR_VPAINT);
1540         
1541         /* init drawing-device settings */
1542         // XXX getmouseco_areawin(p.mval);
1543         // XXX p.pressure = get_pressure();
1544         
1545         p.mvalo[0]= p.mval[0];
1546         p.mvalo[1]= p.mval[1];
1547         p.opressure= p.pressure;
1548         
1549         /* radius for eraser circle is defined in userprefs now */
1550         // TODO: make this more easily tweaked... 
1551         p.radius= U.gp_eraser;
1552         
1553         /* start drawing eraser-circle (if applicable) */
1554         //if (paintmode == GP_PAINTMODE_ERASER)
1555         // XXX  draw_sel_circle(p.mval, NULL, p.radius, p.radius, 0); // draws frontbuffer, but sets backbuf again
1556         
1557         /* only allow painting of single 'dots' if: 
1558          *      - pressure is not excessive (as it can be on some windows tablets)
1559          *      - draw-mode for active datablock is turned on
1560          *      - not erasing
1561          */
1562         if (paintmode != GP_PAINTMODE_ERASER) {
1563                 if (!(p.pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) { 
1564                         gp_stroke_addpoint(&p, p.mval, p.pressure);
1565                 }
1566         }
1567         
1568         /* XXX paint loop */
1569         if(0) {
1570                 /* get current user input */
1571                 // XXX getmouseco_areawin(p.mval);
1572                 // XXX p.pressure = get_pressure();
1573                 
1574                 /* only add current point to buffer if mouse moved (otherwise wait until it does) */
1575                 if (paintmode == GP_PAINTMODE_ERASER) {
1576                         /* do 'live' erasing now */
1577                         gp_stroke_doeraser(&p);
1578                         
1579                         // XXX draw_sel_circle(p.mval, p.mvalo, p.radius, p.radius, 0);
1580                         // XXX force_draw(0);
1581                         
1582                         p.mvalo[0]= p.mval[0];
1583                         p.mvalo[1]= p.mval[1];
1584                         p.opressure= p.pressure;
1585                 }
1586                 else if (gp_stroke_filtermval(&p, p.mval, p.mvalo)) {
1587                         /* try to add point */
1588                         ok= gp_stroke_addpoint(&p, p.mval, p.pressure);
1589                         
1590                         /* handle errors while adding point */
1591                         if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
1592                                 /* finish off old stroke */
1593                                 gp_paint_strokeend(&p);
1594                                 
1595                                 /* start a new stroke, starting from previous point */
1596                                 gp_stroke_addpoint(&p, p.mvalo, p.opressure);
1597                                 ok= gp_stroke_addpoint(&p, p.mval, p.pressure);
1598                         }
1599                         else if (ok == GP_STROKEADD_INVALID) {
1600                                 /* the painting operation cannot continue... */
1601                                 error("Cannot paint stroke");
1602                                 p.status = GP_STATUS_ERROR;
1603                                 
1604                                 if (G.f & G_DEBUG) 
1605                                         printf("Error: Grease-Pencil Paint - Add Point Invalid \n");
1606                                 // XXX break;
1607                         }
1608                         // XXX force_draw(0);
1609                         
1610                         p.mvalo[0]= p.mval[0];
1611                         p.mvalo[1]= p.mval[1];
1612                         p.opressure= p.pressure;
1613                 }
1614                 
1615                 /* do mouse checking at the end, so don't check twice, and potentially
1616                  * miss a short tap 
1617                  */
1618         }
1619         
1620         /* clear edit flags */
1621         G.f &= ~G_GREASEPENCIL;
1622         
1623         /* restore cursor to indicate end of drawing */
1624         // XXX  (cursor callbacks in regiontype) setcursor_space(p.sa->spacetype, CURSOR_STD);
1625         
1626         /* check size of buffer before cleanup, to determine if anything happened here */
1627         if (paintmode == GP_PAINTMODE_ERASER) {
1628                 ok= 1; /* assume that we did something... */
1629                 // XXX draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0);
1630         }
1631         else
1632                 ok= p.gpd->sbuffer_size;
1633         
1634         /* cleanup */
1635         gp_paint_cleanup(&p);
1636         gp_session_cleanup(&p);
1637         
1638         /* done! return if a stroke was successfully added */
1639         return ok;
1640 }
1641
1642
1643 /* All event (loops) handling checking if stroke drawing should be initiated
1644  * should call this function.
1645  */
1646 short gpencil_do_paint (bContext *C)
1647 {
1648         ScrArea *sa= CTX_wm_area(C);
1649         bGPdata *gpd = gpencil_data_getactive(sa);
1650         short retval= 0;
1651         int alt= 0, shift= 0, mbut= 0; // XXX
1652         
1653         /* check if possible to do painting */
1654         if (gpd == NULL) 
1655                 return 0;
1656         
1657         /* currently, we will only 'paint' if:
1658          *      1. draw-mode on gpd is set (for accessibility reasons)
1659          *              a) single dots are only available by this method if a single click is made
1660          *              b) a straight line is drawn if ctrl-modifier is held (check is done when stroke is converted!)
1661          *      2. if shift-modifier is held + lmb -> 'quick paint'
1662          *
1663          *      OR
1664          * 
1665          * draw eraser stroke if:
1666          *      1. using the eraser on a tablet
1667          *      2. draw-mode on gpd is set (for accessiblity reasons)
1668          *              (eraser is mapped to right-mouse)
1669          *      3. Alt + 'select' mouse-button
1670          *              i.e.  if LMB = select: Alt-LMB
1671          *                        if RMB = select: Alt-RMB
1672          */
1673         if (get_activedevice() == 2) {
1674                 /* eraser on a tablet - always try to erase strokes */
1675                 retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
1676         }
1677         else if (gpd->flag & GP_DATA_EDITPAINT) {
1678                 /* try to paint/erase */
1679                 if (mbut == L_MOUSE)
1680                         retval = gpencil_paint(C, GP_PAINTMODE_DRAW);
1681                 else if (mbut == R_MOUSE)
1682                         retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
1683         }
1684         else if (!(gpd->flag & GP_DATA_LMBPLOCK)) {
1685                 /* try to paint/erase as not locked */
1686                 if (shift && (mbut == L_MOUSE)) {
1687                         retval = gpencil_paint(C, GP_PAINTMODE_DRAW);
1688                 }
1689                 else if (alt) {
1690                         if ((U.flag & USER_LMOUSESELECT) && (mbut == L_MOUSE))
1691                                 retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
1692                         else if (!(U.flag & USER_LMOUSESELECT) && (mbut == R_MOUSE))
1693                                 retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
1694                 }
1695         }
1696         
1697         /* return result of trying to paint */
1698         return retval;
1699 }
1700
1701 /* ************************************************** */
1702 #endif // XXX COMPILE GUARDS FOR OLD CODE