Grease Pencil:
authorJoshua Leung <aligorith@gmail.com>
Sun, 5 Oct 2008 11:49:09 +0000 (11:49 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sun, 5 Oct 2008 11:49:09 +0000 (11:49 +0000)
* Separated duplicate methods out into several functions

* Added copy/paste for gp-frames in Action Editor. Only strokes that are appropriate for the spacetype that the destination layer belongs to will be added to avoid wasted memory usage. Otherwise, was impossible to move sketches between views/layers (which was intended for PyAPI access that didn't get done).

Note: there will currently still be an extra gp-frame created, even if the current no strokes got pasted. There's commented out code which would delete it, but somehow that causes errors, so better to be safe for now.

source/blender/include/BDR_gpencil.h
source/blender/include/BIF_editaction.h
source/blender/src/editaction_gpencil.c
source/blender/src/gpencil.c
source/blender/src/header_action.c
source/blender/src/usiblender.c

index 9b9294b0343fab9c34db32b5cf04c0fe4661b7a7..82263c8cda776a9e6d2d0e32b238c4cf38d9c172 100644 (file)
@@ -57,10 +57,13 @@ struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
 struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd);
 struct bGPdata *gpencil_data_addnew(void);
 
+struct bGPDframe *gpencil_frame_duplicate(struct bGPDframe *src);
+struct bGPDlayer *gpencil_layer_duplicate(struct bGPDlayer *src);
 struct bGPdata *gpencil_data_duplicate(struct bGPdata *gpd);
 
 struct bGPdata *gpencil_data_getactive(struct ScrArea *sa);
 short gpencil_data_setactive(struct ScrArea *sa, struct bGPdata *gpd);
+struct ScrArea *gpencil_data_findowner(struct bGPdata *gpd);
 
 void gpencil_frame_delete_laststroke(struct bGPDframe *gpf);
 
@@ -79,7 +82,6 @@ void gpencil_delete_menu(void);
 void gpencil_convert_operation(short mode);
 void gpencil_convert_menu(void);
 
-//short gpencil_paint(short mousebutton);
 short gpencil_do_paint(struct ScrArea *sa, short mousebutton);
 
 #endif /*  BDR_GPENCIL_H */
index 1425cf6a67c51c3dabdea5129de762aaa9393127..ae330b30d81f02c0a2a3f216b411eb3c33bde7f3 100644 (file)
@@ -201,6 +201,10 @@ void delete_gpencil_layers(void);
 void delete_gplayer_frames(struct bGPDlayer *gpl);
 void duplicate_gplayer_frames(struct bGPDlayer *gpd);
 
+void free_gpcopybuf(void);
+void copy_gpdata(void);
+void paste_gpdata(void);
+
 void snap_gplayer_frames(struct bGPDlayer *gpl, short mode);
 void mirror_gplayer_frames(struct bGPDlayer *gpl, short mode);
 
index 14269080b1f69b441d3caa378bf59bd083254f89..1f30b83c6b3472c6871f8ea8e9a01e6df0153857 100644 (file)
@@ -362,23 +362,224 @@ void duplicate_gplayer_frames (bGPDlayer *gpl)
                /* duplicate this frame */
                if (gpf->flag & GP_FRAME_SELECT) {
                        bGPDframe *gpfd; 
-                       bGPDstroke *gps;
                        
                        /* duplicate frame, and deselect self */
-                       gpfd= MEM_dupallocN(gpf);
+                       gpfd= gpencil_frame_duplicate(gpf);
                        gpf->flag &= ~GP_FRAME_SELECT;
                        
-                       /* duplicate list of strokes too */
-                       duplicatelist(&gpfd->strokes, &gpf->strokes);
+                       BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
+               }
+       }
+}
+
+/* -------------------------------------- */
+/* Copy and Paste Tools */
+/* - The copy/paste buffer currently stores a set of GP_Layers, with temporary
+ *     GP_Frames with the necessary strokes
+ * - Unless there is only one element in the buffer, names are also tested to check for compatability.
+ * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
+ *     the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
+ * - The earliest frame is calculated per copy operation.
+ */
+/* globals for copy/paste data (like for other copy/paste buffers) */
+ListBase gpcopybuf = {NULL, NULL};
+static float gpcopy_firstframe= 999999999.0f;
+
+/* This function frees any MEM_calloc'ed copy/paste buffer data */
+void free_gpcopybuf ()
+{
+       free_gpencil_layers(&gpcopybuf); 
+       
+       gpcopybuf.first= gpcopybuf.last= NULL;
+       gpcopy_firstframe= 999999999.0f;
+}
+
+/* This function adds data to the copy/paste buffer, freeing existing data first
+ * Only the selected action channels gets their selected keyframes copied.
+ */
+void copy_gpdata ()
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       /* clear buffer first */
+       free_gpcopybuf();
+       
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
+       if (datatype != ACTCONT_GPENCIL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* assume that each of these is an ipo-block */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               bGPDlayer *gpls, *gpln;
+               bGPDframe *gpf, *gpfn;
+               
+               /* get new layer to put into buffer */
+               gpls= (bGPDlayer *)ale->data;
+               gpln= MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
+               
+               gpln->frames.first= gpln->frames.last= NULL;
+               strcpy(gpln->info, gpls->info);
+               
+               BLI_addtail(&gpcopybuf, gpln);
+               
+               /* loop over frames, and copy only selected frames */
+               for (gpf= gpls->frames.first; gpf; gpf= gpf->next) {
+                       /* if frame is selected, make duplicate it and its strokes */
+                       if (gpf->flag & GP_FRAME_SELECT) {
+                               /* add frame to buffer */
+                               gpfn= gpencil_frame_duplicate(gpf);
+                               BLI_addtail(&gpln->frames, gpfn);
+                               
+                               /* check if this is the earliest frame encountered so far */
+                               if (gpf->framenum < gpcopy_firstframe)
+                                       gpcopy_firstframe= gpf->framenum;
+                       }
+               }
+       }
+       
+       /* check if anything ended up in the buffer */
+       if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last))
+               error("Nothing copied to buffer");
+       
+       /* free temp memory */
+       BLI_freelistN(&act_data);
+}
+
+void paste_gpdata ()
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       short no_name= 0;
+       float offset = CFRA - gpcopy_firstframe;
+       
+       /* check if buffer is empty */
+       if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) {
+               error("No data in buffer to paste");
+               return;
+       }
+       /* check if single channel in buffer (disregard names if so)  */
+       if (gpcopybuf.first == gpcopybuf.last)
+               no_name= 1;
+       
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
+       if (datatype != ACTCONT_GPENCIL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* from selected channels */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               bGPDlayer *gpld= (bGPDlayer *)ale->data;
+               bGPDlayer *gpls= NULL;
+               bGPDframe *gpfs, *gpf;
+               
+               /* find suitable layer from buffer to use to paste from */
+               for (gpls= gpcopybuf.first; gpls; gpls= gpls->next) {
+                       /* check if layer name matches */
+                       if ((no_name) || (strcmp(gpls->info, gpld->info)==0))
+                               break;
+               }
+               
+               /* this situation might occur! */
+               if (gpls == NULL)
+                       continue;
+               
+               /* add frames from buffer */
+               for (gpfs= gpls->frames.first; gpfs; gpfs= gpfs->next) {
+                       /* temporarily apply offset to buffer-frame while copying */
+                       gpfs->framenum += offset;
                        
-                       /* dupalloc only makes another copy of mem, but doesn't adjust pointers */
-                       for (gps= gpfd->strokes.first; gps; gps= gps->next) {
-                               gps->points= MEM_dupallocN(gps->points);
+                       /* get frame to copy data into (if no frame returned, then just ignore) */
+                       gpf= gpencil_layer_getframe(gpld, gpfs->framenum, 1);
+                       if (gpf) {
+                               bGPDstroke *gps, *gpsn;
+                               ScrArea *sa;
+                               
+                               /* get area that gp-data comes from */
+                               sa= gpencil_data_findowner((bGPdata *)ale->owner);                              
+                               
+                               /* this should be the right frame... as it may be a pre-existing frame, 
+                                * must make sure that only compatible stroke types get copied over 
+                                *      - we cannot just add a duplicate frame, as that would cause errors
+                                *      - need to check for compatible types to minimise memory usage (copying 'junk' over)
+                                */
+                               for (gps= gpfs->strokes.first; gps; gps= gps->next) {
+                                       short stroke_ok;
+                                       
+                                       /* if there's an area, check that it supports this type of stroke */
+                                       if (sa) {
+                                               stroke_ok= 0;
+                                               
+                                               /* check if spacetype supports this type of stroke
+                                                *      - NOTE: must sync this with gp_paint_initstroke() in gpencil.c
+                                                */
+                                               switch (sa->spacetype) {
+                                                       case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */
+                                                               if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE))
+                                                                       stroke_ok= 1;
+                                                               break;
+                                                               
+                                                       case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */
+                                                       case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */
+                                                               if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE))
+                                                                       stroke_ok= 1;
+                                                               break;
+                                                               
+                                                       case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */
+                                                               if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE))
+                                                                       stroke_ok= 1;
+                                                               break;
+                                               }
+                                       }
+                                       else
+                                               stroke_ok= 1;
+                                       
+                                       /* if stroke is ok, we make a copy of this stroke and add to frame */
+                                       if (stroke_ok) {
+                                               /* make a copy of stroke, then of its points array */
+                                               gpsn= MEM_dupallocN(gps);
+                                               gpsn->points= MEM_dupallocN(gps->points);
+                                               
+                                               /* append stroke to frame */
+                                               BLI_addtail(&gpf->strokes, gpsn);
+                                       }
+                               }
+                               
+                               /* if no strokes (i.e. new frame) added, free gpf */
+                               if (gpf->strokes.first == NULL)
+                                       gpencil_layer_delframe(gpld, gpf);
                        }
                        
-                       BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
+                       /* unapply offset from buffer-frame */
+                       gpfs->framenum -= offset;
                }
        }
+       
+       /* free temp memory */
+       BLI_freelistN(&act_data);
+       
+       /* undo and redraw stuff */
+       allqueue(REDRAWVIEW3D, 0);
+       //allqueue(REDRAWNODES, 0);
+       allqueue(REDRAWACTION, 0);
+       BIF_undo_push("Paste Grease Pencil Frames");
 }
 
 /* -------------------------------------- */
index 57fd958a94e59b04829564706560d223897bded0..f6703fbe66b0c948b5418aa0800fea2498a31bc2 100644 (file)
@@ -112,7 +112,7 @@ void free_gpencil_strokes (bGPDframe *gpf)
                gpsn= gps->next;
                
                /* free stroke memory arrays, then stroke itself */
-               MEM_freeN(gps->points);
+               if (gps->points) MEM_freeN(gps->points);
                BLI_freelinkN(&gpf->strokes, gps);
        }
 }
@@ -135,7 +135,7 @@ void free_gpencil_frames (bGPDlayer *gpl)
        }
 }
 
-/* Free all of the gp-layers for a viewport (list should be &G.vd->gpd or so) */
+/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */
 void free_gpencil_layers (ListBase *list) 
 {
        bGPDlayer *gpl, *gpln;
@@ -255,13 +255,63 @@ bGPdata *gpencil_data_addnew (void)
 
 /* -------- Data Duplication ---------- */
 
+/* make a copy of a given gpencil frame */
+bGPDframe *gpencil_frame_duplicate (bGPDframe *src)
+{
+       bGPDstroke *gps, *gpsd;
+       bGPDframe *dst;
+       
+       /* error checking */
+       if (src == NULL)
+               return NULL;
+               
+       /* make a copy of the source frame */
+       dst= MEM_dupallocN(src);
+       
+       /* copy strokes */
+       dst->strokes.first = dst->strokes.last= NULL;
+       for (gps= src->strokes.first; gps; gps= gps->next) {
+               /* make copy of source stroke, then adjust pointer to points too */
+               gpsd= MEM_dupallocN(gps);
+               gpsd->points= MEM_dupallocN(gps->points);
+               
+               BLI_addtail(&dst->strokes, gpsd);
+       }
+       
+       /* return new frame */
+       return dst;
+}
+
+/* make a copy of a given gpencil layer */
+bGPDlayer *gpencil_layer_duplicate (bGPDlayer *src)
+{
+       bGPDframe *gpf, *gpfd;
+       bGPDlayer *dst;
+       
+       /* error checking */
+       if (src == NULL)
+               return NULL;
+               
+       /* make a copy of source layer */
+       dst= MEM_dupallocN(src);
+       
+       /* copy frames */
+       dst->frames.first= dst->frames.last= NULL;
+       for (gpf= src->frames.first; gpf; gpf= gpf->next) {
+               /* make a copy of source stroke */
+               gpfd= gpencil_frame_duplicate(gpf);
+               BLI_addtail(&dst->frames, gpfd);
+       }
+       
+       /* return new layer */
+       return dst;
+}
+
 /* make a copy of a given gpencil datablock */
 bGPdata *gpencil_data_duplicate (bGPdata *src)
 {
+       bGPDlayer *gpl, *gpld;
        bGPdata *dst;
-       bGPDlayer *gpld, *gpls;
-       bGPDframe *gpfd, *gpfs;
-       bGPDstroke *gps;
        
        /* error checking */
        if (src == NULL)
@@ -271,25 +321,11 @@ bGPdata *gpencil_data_duplicate (bGPdata *src)
        dst= MEM_dupallocN(src);
        
        /* copy layers */
-       duplicatelist(&dst->layers, &src->layers);
-       
-       for (gpld=dst->layers.first, gpls=src->layers.first; gpld && gpls; 
-                gpld=gpld->next, gpls=gpls->next) 
-       {
-               /* copy frames */
-               duplicatelist(&gpld->frames, &gpls->frames);
-               
-               for (gpfd=gpld->frames.first, gpfs=gpls->frames.first; gpfd && gpfs;
-                        gpfd=gpfd->next, gpfs=gpfs->next) 
-               {
-                       /* copy strokes */
-                       duplicatelist(&gpfd->strokes, &gpfs->strokes);
-                       
-                       for (gps= gpfd->strokes.first; gps; gps= gps->next) 
-                       {
-                               gps->points= MEM_dupallocN(gps->points);
-                       }
-               }
+       dst->layers.first= dst->layers.last= NULL;
+       for (gpl= src->layers.first; gpl; gpl= gpl->next) {
+               /* make a copy of source layer and its data */
+               gpld= gpencil_layer_duplicate(gpl);
+               BLI_addtail(&dst->layers, gpld);
        }
        
        /* return new */
@@ -415,6 +451,30 @@ short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd)
        return 0;
 }
 
+/* return the ScrArea that has the given GP-datablock
+ *     - assumes that only searching in current screen
+ *     - is based on GP-datablocks only being able to 
+ *       exist for one area at a time (i.e. not multiuser)
+ */
+ScrArea *gpencil_data_findowner (bGPdata *gpd)
+{
+       ScrArea *sa;
+       
+       /* error checking */
+       if (gpd == NULL)
+               return NULL;
+               
+       /* loop over all scrareas for current screen, and check if that area has this gpd */
+       for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
+               /* use get-active func to see if match */
+               if (gpencil_data_getactive(sa) == gpd)
+                       return sa;
+       }
+       
+       /* not found */
+       return NULL;
+}
+
 /* -------- GP-Frame API ---------- */
 
 /* delete the last stroke of the given frame */
@@ -539,6 +599,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew)
                else {
                        /* unresolved errogenous situation! */
                        printf("Error: cannot find appropriate gp-frame \n");
+                       /* gpl->actframe should still be NULL */
                }
        }
        else {
@@ -547,6 +608,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew)
                        gpl->actframe= gpencil_frame_addnew(gpl, cframe);
                else {
                        /* don't do anything... this may be when no frames yet! */
+                       /* gpl->actframe should still be NULL */
                }
        }
        
@@ -792,7 +854,7 @@ static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
                /* set settings */
                bezt->h1= bezt->h2= HD_FREE;
                bezt->f1= bezt->f2= bezt->f3= SELECT;
-               bezt->radius = bezt->weight = pt->pressure * gpl->thickness;
+               bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1;
        }
        
        /* must calculate handles or else we crash */
@@ -1717,7 +1779,6 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode)
                                break;
                        case SPACE_SEQ:
                        {
-                               /* for now, this is not applicable here... */
                                p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE;
                        }
                                break;
index 2834a41e9ecfe0d3ed0b84f577871197c95c4444..0cd5fcba27122416b273a13f32eb676df5511453 100644 (file)
@@ -285,10 +285,16 @@ void do_action_buttons(unsigned short event)
                        
                /* copy/paste buttons in Action Editor header */
                case B_ACTCOPYKEYS:
-                       copy_actdata();
+                       if (G.saction->mode == SACTCONT_GPENCIL)
+                               copy_gpdata();
+                       else
+                               copy_actdata();
                        break;
                case B_ACTPASTEKEYS:
-                       paste_actdata();
+                       if (G.saction->mode == SACTCONT_GPENCIL)
+                               paste_gpdata();
+                       else
+                               paste_actdata();
                        break;
 
                case B_ACTPIN:  /* __PINFAKE */
@@ -1735,20 +1741,24 @@ void action_buttons(void)
                uiClearButLock();
                
                xco += 8;
-               
-               /* COPY PASTE */
-               uiBlockBeginAlign(block);
-               if (curarea->headertype==HEADERTOP) {
-                       uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP,    xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
-                       uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP,  xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
-               }
-               else {
-                       uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN,  xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
-                       uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN,        xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
-               }
-               uiBlockEndAlign(block);
-               xco += (XIC + 8);
-               
+       }
+       
+       
+       /* COPY PASTE */
+       uiBlockBeginAlign(block);
+       if (curarea->headertype==HEADERTOP) {
+               uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP,    xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
+               uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP,  xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
+       }
+       else {
+               uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN,  xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
+               uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN,        xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
+       }
+       uiBlockEndAlign(block);
+       xco += (XIC + 8);
+       
+       
+       if (G.saction->mode != SACTCONT_GPENCIL) {
                /* draw AUTOSNAP */
                if (G.saction->flag & SACTION_DRAWTIME) {
                        uiDefButC(block, MENU, B_REDR,
index f6e4054379c94afc52ad638d09b84c50d377758b..c4d22664b8edf45264684d317ed6bf9f04719015 100644 (file)
@@ -1133,6 +1133,7 @@ void exit_usiblender(void)
        free_matcopybuf();
        free_ipocopybuf();
        free_actcopybuf();
+       free_gpcopybuf();
        free_vertexpaint();
        free_texttools();