/* delete active frame - wrapper around API calls */ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPdata *gpd = gpencil_data_get_active(C); bGPDlayer *gpl = gpencil_layer_getactive(gpd); bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { BKE_report(op->reports, RPT_ERROR, "No grease pencil data"); return OPERATOR_CANCELLED; } if (ELEM(NULL, gpl, gpf)) { BKE_report(op->reports, RPT_ERROR, "No active frame to delete"); return OPERATOR_CANCELLED; } /* delete it... */ gpencil_layer_delframe(gpl, gpf); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; }
static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) { Scene *scene = CTX_data_scene(C); bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; RulerItem *ruler_item; const char *ruler_name = RULER_ID; bool changed = false; if (scene->gpd == NULL) { scene->gpd = gpencil_data_addnew("GPencil"); } gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } gpf = gpencil_layer_getframe(gpl, CFRA, true); free_gpencil_strokes(gpf); for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) { bGPDspoint *pt; int j; /* allocate memory for a new stroke */ gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); if (ruler_item->flag & RULERITEM_USE_ANGLE) { gps->totpoints = 3; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; pt++; } } else { gps->totpoints = 2; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; pt++; } } gps->flag = GP_STROKE_3DSPACE; BLI_addtail(&gpf->strokes, gps); changed = true; } return changed; }
/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ static void gp_layer_to_curve(bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode) { Scene *scene = CTX_data_scene(C); bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps; Object *ob; Curve *cu; /* camera framing */ rctf subrect, *subrect_ptr = NULL; /* error checking */ if (ELEM3(NULL, gpd, gpl, gpf)) return; /* only convert if there are any strokes on this layer's frame to convert */ if (gpf->strokes.first == NULL) return; /* initialize camera framing */ if (gp_camera_view_subrect(C, &subrect)) { subrect_ptr = &subrect; } /* init the curve object (remove rotation and get curve data from it) * - must clear transforms set on object, as those skew our results */ ob = BKE_object_add(scene, OB_CURVE); zero_v3(ob->loc); zero_v3(ob->rot); cu = ob->data; cu->flag |= CU_3D; /* rename object and curve to layer name */ rename_id((ID *)ob, gpl->info); rename_id((ID *)cu, gpl->info); /* add points to curve */ for (gps = gpf->strokes.first; gps; gps = gps->next) { switch (mode) { case GP_STROKECONVERT_PATH: gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr); break; case GP_STROKECONVERT_CURVE: gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr); break; default: BLI_assert(!"invalid mode"); break; } } }
static int gp_convert_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = NULL; bGPDframe *gpf = NULL; ScrArea *sa = CTX_wm_area(C); Scene *scene = CTX_data_scene(C); /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), * and if we are not in edit mode! */ return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = gpencil_layer_getactive(gpd)) && (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) && (gpf->strokes.first) && (scene->obedit == NULL)); }
/* delete the last stroke of the given frame */ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) { bGPDstroke *gps = (gpf) ? gpf->strokes.last : NULL; int cfra = (gpf) ? gpf->framenum : 0; /* assume that the current frame was not locked */ /* error checking */ if (ELEM(NULL, gpf, gps)) return; /* free the stroke and its data */ MEM_freeN(gps->points); BLI_freelinkN(&gpf->strokes, gps); /* if frame has no strokes after this, delete it */ if (BLI_listbase_is_empty(&gpf->strokes)) { gpencil_layer_delframe(gpl, gpf); gpencil_layer_getframe(gpl, cfra, 0); } }
/* Check a GP layer has valid timing data! Else, most timing options are hidden in the operator. * op may be NULL. */ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPDframe *gpf = NULL; bGPDstroke *gps = NULL; bGPDspoint *pt; double base_time, cur_time, prev_time = -1.0; int i; bool valid = true; if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) return false; do { base_time = cur_time = gps->inittime; if (cur_time <= prev_time) { valid = false; break; } prev_time = cur_time; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { cur_time = base_time + (double)pt->time; /* First point of a stroke should have the same time as stroke's inittime, * so it's the only case where equality is allowed! */ if ((i && cur_time <= prev_time) || (cur_time < prev_time)) { valid = false; break; } prev_time = cur_time; } if (!valid) { break; } } while ((gps = gps->next)); if (op) { RNA_boolean_set(op->ptr, "use_timing_data", valid); } return valid; }
static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info) { Scene *scene = CTX_data_scene(C); bool changed = false; if (scene->gpd) { bGPDlayer *gpl; const char *ruler_name = RULER_ID; gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl) { bGPDframe *gpf; gpf = gpencil_layer_getframe(gpl, CFRA, false); if (gpf) { bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt = gps->points; int j; if (gps->totpoints == 3) { RulerItem *ruler_item = ruler_item_add(ruler_info); for (j = 0; j < 3; j++) { copy_v3_v3(ruler_item->co[j], &pt->x); pt++; } ruler_item->flag |= RULERITEM_USE_ANGLE; changed = true; } else if (gps->totpoints == 2) { RulerItem *ruler_item = ruler_item_add(ruler_info); for (j = 0; j < 3; j += 2) { copy_v3_v3(ruler_item->co[j], &pt->x); pt++; } changed = true; } } } } } return changed; }
/* Pastes keyframes from buffer, and reports success */ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; Scene *scene = ac->scene; bool no_name = false; int offset = 0; /* check if buffer is empty */ if (BLI_listbase_is_empty(&gp_anim_copybuf)) { BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste"); return false; } /* check if single channel in buffer (disregard names if so) */ if (gp_anim_copybuf.first == gp_anim_copybuf.last) { no_name = true; } /* methods of offset (eKeyPasteOffset) */ switch (offset_mode) { case KEYFRAME_PASTE_OFFSET_CFRA_START: offset = (CFRA - gp_anim_copy_firstframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_END: offset = (CFRA - gp_anim_copy_lastframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: offset = (CFRA - gp_anim_copy_cfra); break; case KEYFRAME_PASTE_OFFSET_NONE: offset = 0; break; } /* filter data */ // TODO: try doing it with selection, then without selection imits filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* from selected channels */ for (ale = anim_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 = gp_anim_copybuf.first; gpls; gpls = gpls->next) { /* check if layer name matches */ if ((no_name) || STREQ(gpls->info, gpld->info)) { 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; /* 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; /* 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 * - For now, we don't check if the types will be compatible since we * don't have enough info to do so. Instead, we simply just paste, * af it works, it will show up. */ for (gps = gpfs->strokes.first; gps; gps = gps->next) { /* make a copy of stroke, then of its points array */ gpsn = MEM_dupallocN(gps); gpsn->points = MEM_dupallocN(gps->points); /* duplicate triangle information */ gpsn->triangles = MEM_dupallocN(gps->triangles); /* append stroke to frame */ BLI_addtail(&gpf->strokes, gpsn); } /* if no strokes (i.e. new frame) added, free gpf */ if (BLI_listbase_is_empty(&gpf->strokes)) gpencil_layer_delframe(gpld, gpf); } /* unapply offset from buffer-frame */ gpfs->framenum -= offset; } } /* clean up */ ANIM_animdata_freelist(&anim_data); return true; }
/* draw grease-pencil datablock */ static void gp_draw_data (bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { bGPDlayer *gpl, *actlay=NULL; /* reset line drawing style (in case previous user didn't reset) */ setlinestyle(0); /* turn on smooth lines (i.e. anti-aliasing) */ glEnable(GL_LINE_SMOOTH); /* turn on alpha-blending */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); /* loop over layers, drawing them */ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { bGPDframe *gpf; short debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? 1 : 0; short lthick= gpl->thickness; float color[4], tcolor[4]; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) continue; /* if layer is active one, store pointer to it */ if (gpl->flag & GP_LAYER_ACTIVE) actlay= gpl; /* get frame to draw */ gpf= gpencil_layer_getframe(gpl, cfra, 0); if (gpf == NULL) continue; /* set color, stroke thickness, and point size */ glLineWidth(lthick); QUATCOPY(color, gpl->color); // just for copying 4 array elements QUATCOPY(tcolor, gpl->color); // additional copy of color (for ghosting) glColor4f(color[0], color[1], color[2], color[3]); glPointSize((float)(gpl->thickness + 2)); /* draw 'onionskins' (frame left + right) */ if (gpl->flag & GP_LAYER_ONIONSKIN) { /* drawing method - only immediately surrounding (gstep = 0), or within a frame range on either side (gstep > 0)*/ if (gpl->gstep) { bGPDframe *gf; float fac; /* draw previous frames first */ for (gf=gpf->prev; gf; gf=gf->prev) { /* check if frame is drawable */ if ((gpf->framenum - gf->framenum) <= gpl->gstep) { /* alpha decreases with distance from curframe index */ fac= (float)(gpf->framenum - gf->framenum) / (float)gpl->gstep; tcolor[3] = color[3] - fac; gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor); } else break; } /* now draw next frames */ for (gf= gpf->next; gf; gf=gf->next) { /* check if frame is drawable */ if ((gf->framenum - gpf->framenum) <= gpl->gstep) { /* alpha decreases with distance from curframe index */ fac= (float)(gf->framenum - gpf->framenum) / (float)gpl->gstep; tcolor[3] = color[3] - fac; gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor); } else break; } /* restore alpha */ glColor4f(color[0], color[1], color[2], color[3]); } else { /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->prev) { tcolor[3] = (color[3] / 7); gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor); } if (gpf->next) { tcolor[3] = (color[3] / 4); gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor); } /* restore alpha */ glColor4f(color[0], color[1], color[2], color[3]); } } /* draw the strokes already in active frame */ tcolor[3]= color[3]; gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor); /* Check if may need to draw the active stroke cache, only if this layer is the active layer * that is being edited. (Stroke buffer is currently stored in gp-data) */ if ((G.f & G_GREASEPENCIL) && (gpl->flag & GP_LAYER_ACTIVE) && (gpf->flag & GP_FRAME_PAINT)) { /* Buffer stroke needs to be drawn with a different linestyle to help differentiate them from normal strokes. */ gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); } } /* turn off alpha blending, then smooth lines */ glDisable(GL_BLEND); // alpha blending glDisable(GL_LINE_SMOOTH); // smooth lines /* restore initial gl conditions */ glLineWidth(1.0); glPointSize(1.0); glColor4f(0, 0, 0, 1); }
/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bGPDlayer *gpl, const int mode, const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd) { struct Main *bmain = CTX_data_main(C); View3D *v3d = CTX_wm_view3d(C); /* may be NULL */ Scene *scene = CTX_data_scene(C); bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps, *prev_gps = NULL; Object *ob; Curve *cu; Nurb *nu = NULL; Base *base_orig = BASACT, *base_new = NULL; float minmax_weights[2] = {1.0f, 0.0f}; /* camera framing */ rctf subrect, *subrect_ptr = NULL; /* error checking */ if (ELEM(NULL, gpd, gpl, gpf)) return; /* only convert if there are any strokes on this layer's frame to convert */ if (BLI_listbase_is_empty(&gpf->strokes)) return; /* initialize camera framing */ if (gp_camera_view_subrect(C, &subrect)) { subrect_ptr = &subrect; } /* init the curve object (remove rotation and get curve data from it) * - must clear transforms set on object, as those skew our results */ ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); base_new = BKE_scene_base_add(scene, ob); cu->flag |= CU_3D; gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime; /* add points to curve */ for (gps = gpf->strokes.first; gps; gps = gps->next) { const bool add_start_point = (link_strokes && !(prev_gps)); const bool add_end_point = (link_strokes && !(gps->next)); /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, and stitch them to previous one. */ bool stitch = false; if (prev_gps) { bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1]; bGPDspoint *pt2 = &gps->points[0]; if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) { stitch = true; } } /* Decide whether we connect this stroke to previous one */ if (!(stitch || link_strokes)) { nu = NULL; } switch (mode) { case GP_STROKECONVERT_PATH: gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, add_start_point, add_end_point, gtd); break; case GP_STROKECONVERT_CURVE: case GP_STROKECONVERT_POLY: /* convert after */ gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, add_start_point, add_end_point, gtd); break; default: BLI_assert(!"invalid mode"); break; } prev_gps = gps; } /* If link_strokes, be sure first and last points have a zero weight/size! */ if (link_strokes) { gp_stroke_finalize_curve_endpoints(cu); } /* Update curve's weights, if needed */ if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) { gp_stroke_norm_curve_weights(cu, minmax_weights); } /* Create the path animation, if needed */ gp_stroke_path_animation(C, reports, cu, gtd); if (mode == GP_STROKECONVERT_POLY) { for (nu = cu->nurb.first; nu; nu = nu->next) { BKE_nurb_type_convert(nu, CU_POLY, false); } } /* set the layer and select */ base_new->lay = ob->lay = base_orig ? base_orig->lay : BKE_screen_view3d_layer_active(v3d, scene); base_new->flag = ob->flag = base_new->flag | SELECT; }
/* loop over gpencil data layers, drawing them */ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { bGPDframe *gpf; bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false; short lthick = gpl->thickness; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) continue; /* get frame to draw */ gpf = gpencil_layer_getframe(gpl, cfra, 0); if (gpf == NULL) continue; /* set basic stroke thickness */ glLineWidth(lthick); /* Add layer drawing settings to the set of "draw flags" * NOTE: If the setting doesn't apply, it *must* be cleared, * as dflag's carry over from the previous layer */ #define GP_DRAWFLAG_APPLY(condition, draw_flag_value) { \ if (condition) dflag |= (draw_flag_value); \ else dflag &= ~(draw_flag_value); \ } (void)0 /* xray... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_NO_XRAY), GP_DRAWDATA_NO_XRAY); /* volumetric strokes... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC); /* fill strokes... */ // XXX: this is not a very good limit GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); #undef GP_DRAWFLAG_APPLY /* draw 'onionskins' (frame left + right) */ if ((gpl->flag & GP_LAYER_ONIONSKIN) && !(dflag & GP_DRAWDATA_NO_ONIONS)) { /* Drawing method - only immediately surrounding (gstep = 0), * or within a frame range on either side (gstep > 0) */ gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick); } /* draw the strokes already in active frame */ gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill); /* Draw verts of selected strokes * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering * - locked layers can't be edited, so there's no point showing these verts * as they will have no bearings on what gets edited * - only show when in editmode, since operators shouldn't work otherwise * (NOTE: doing it this way means that the toggling editmode shows visible change immediately) */ /* XXX: perhaps we don't want to show these when users are drawing... */ if ((G.f & G_RENDER_OGL) == 0 && (gpl->flag & GP_LAYER_LOCKED) == 0 && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag, (gpl->color[3] < 0.95f) ? gpl->color : NULL); } /* Check if may need to draw the active stroke cache, only if this layer is the active layer * that is being edited. (Stroke buffer is currently stored in gp-data) */ if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) && (gpf->flag & GP_FRAME_PAINT)) { /* Set color for drawing buffer stroke - since this may not be set yet */ glColor4fv(gpl->color); /* Buffer stroke needs to be drawn with a different linestyle * to help differentiate them from normal strokes. * * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ if (gpl->flag & GP_LAYER_VOLUMETRIC) { gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); } else { gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); } } } }
void paste_gpdata (Scene *scene) { ListBase act_data = {NULL, NULL}; bActListElem *ale; int filter; void *data; short datatype; const int offset = (CFRA - gpcopy_firstframe); short no_name= 0; /* 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; /* 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); sa = NULL; /* 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); } /* unapply offset from buffer-frame */ gpfs->framenum -= offset; } } /* free temp memory */ BLI_freelistN(&act_data); /* undo and redraw stuff */ BIF_undo_push("Paste Grease Pencil Frames"); }