Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_data.c
| Context not available. | |||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "DNA_anim_types.h" | |||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_modifier_types.h" | |||||
| #include "DNA_object_types.h" | |||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "DNA_view3d_types.h" | #include "DNA_view3d_types.h" | ||||
| #include "DNA_gpencil_types.h" | |||||
| #include "BKE_main.h" | |||||
| #include "BKE_brush.h" | |||||
| #include "BKE_animsys.h" | |||||
| #include "BKE_colortools.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_deform.h" | |||||
| #include "BKE_fcurve.h" | |||||
| #include "BKE_global.h" | |||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_modifier.h" | |||||
| #include "BKE_object.h" | #include "BKE_object.h" | ||||
| #include "BKE_paint.h" | |||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "BKE_colortools.h" | |||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| Context not available. | |||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "ED_object.h" | |||||
| #include "ED_gpencil.h" | #include "ED_gpencil.h" | ||||
| #include "DEG_depsgraph.h" | |||||
| #include "DEG_depsgraph_build.h" | |||||
| #include "gpencil_intern.h" | #include "gpencil_intern.h" | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| Context not available. | |||||
| static int gp_data_add_exec(bContext *C, wmOperator *op) | static int gp_data_add_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| if (gpd_ptr == NULL) { | if (gpd_ptr == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | ||||
| Context not available. | |||||
| } | } | ||||
| else { | else { | ||||
| /* decrement user count and add new datablock */ | /* decrement user count and add new datablock */ | ||||
| /* TODO: if a datablock exists, we should make a copy of it instead of starting fresh (as in other areas) */ | |||||
| Main *bmain = CTX_data_main(C); | |||||
| bGPdata *gpd = (*gpd_ptr); | bGPdata *gpd = (*gpd_ptr); | ||||
| id_us_min(&gpd->id); | id_us_min((ID *)gpd); | ||||
| *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); | *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | ||||
| /* if not exist brushes, create a new set */ | |||||
| if (ts) { | |||||
| if (BLI_listbase_is_empty(&ts->gp_brushes)) { | |||||
| /* create new brushes */ | |||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| } | |||||
| } | |||||
| /* add default sets of colors and brushes */ | |||||
| ED_gpencil_add_defaults(C); | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| Context not available. | |||||
| static int gp_layer_add_exec(bContext *C, wmOperator *op) | static int gp_layer_add_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| /* if there's no existing Grease-Pencil data there, add some */ | /* if there's no existing Grease-Pencil data there, add some */ | ||||
| if (gpd_ptr == NULL) { | if (gpd_ptr == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (*gpd_ptr == NULL) | if (*gpd_ptr == NULL) { | ||||
| *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); | *gpd_ptr = BKE_gpencil_data_addnew(CTX_data_main(C), DATA_("GPencil")); | ||||
| /* if not exist brushes, create a new set */ | |||||
| if (ts) { | |||||
| if (BLI_listbase_is_empty(&ts->gp_brushes)) { | |||||
| /* create new brushes */ | |||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| } | |||||
| } | } | ||||
| /* add default sets of colors and brushes */ | |||||
| ED_gpencil_add_defaults(C); | |||||
| /* add new layer now */ | /* add new layer now */ | ||||
| BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); | BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); | ||||
| Context not available. | |||||
| BKE_gpencil_layer_delete(gpd, gpl); | BKE_gpencil_layer_delete(gpd, gpl); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ | BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ | ||||
| if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { | if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| } | } | ||||
| Context not available. | |||||
| BKE_gpencil_layer_setactive(gpd, new_layer); | BKE_gpencil_layer_setactive(gpd, new_layer); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ********************* Duplicate Frame ************************** */ | |||||
| enum { | |||||
| GP_FRAME_DUP_ACTIVE = 0, | |||||
| GP_FRAME_DUP_ALL = 1 | |||||
| }; | |||||
| static int gp_frame_duplicate_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); | |||||
| int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd, gpl)) | |||||
| return OPERATOR_CANCELLED; | |||||
| if (mode == 0) { | |||||
| BKE_gpencil_frame_addcopy(gpl, CFRA); | |||||
| } | |||||
| else { | |||||
| for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | |||||
| if ((gpl->flag & GP_LAYER_LOCKED) == 0) { | |||||
| BKE_gpencil_frame_addcopy(gpl, CFRA); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* notifiers */ | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_frame_duplicate(wmOperatorType *ot) | |||||
| { | |||||
| static const EnumPropertyItem duplicate_mode[] = { | |||||
| { GP_FRAME_DUP_ACTIVE, "ACTIVE", 0, "Active", "Duplicate frame in active layer only" }, | |||||
| { GP_FRAME_DUP_ALL, "ALL", 0, "All", "Duplicate active frames in all layers" }, | |||||
| { 0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Duplicate Frame"; | |||||
| ot->idname = "GPENCIL_OT_frame_duplicate"; | |||||
| ot->description = "Make a copy of the active Grease Pencil Frame"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_frame_duplicate_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); | |||||
| } | |||||
| /* ********************* Clean Fill Boundaries on Frame ************************** */ | |||||
| enum { | |||||
| GP_FRAME_CLEAN_FILL_ACTIVE = 0, | |||||
| GP_FRAME_CLEAN_FILL_ALL = 1 | |||||
| }; | |||||
| static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bool changed = false; | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | |||||
| { | |||||
| bGPDframe *init_gpf = gpl->actframe; | |||||
| if (mode == GP_FRAME_CLEAN_FILL_ALL) { | |||||
| init_gpf = gpl->frames.first; | |||||
| } | |||||
| for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { | |||||
| if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) { | |||||
| bGPDstroke *gps, *gpsn; | |||||
| if (gpf == NULL) | |||||
| continue; | |||||
| /* simply delete strokes which are no fill */ | |||||
| for (gps = gpf->strokes.first; gps; gps = gpsn) { | |||||
| gpsn = gps->next; | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| /* free stroke */ | |||||
| if (gps->flag & GP_STROKE_NOFILL) { | |||||
| /* free stroke memory arrays, then stroke itself */ | |||||
| if (gps->points) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| MEM_SAFE_FREE(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| changed = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| /* notifiers */ | |||||
| if (changed) { | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| } | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot) | |||||
| { | |||||
| static const EnumPropertyItem duplicate_mode[] = { | |||||
| { GP_FRAME_CLEAN_FILL_ACTIVE, "ACTIVE", 0, "Active Frame Only", "Clean active frame only" }, | |||||
| { GP_FRAME_CLEAN_FILL_ALL, "ALL", 0, "All Frames", "Clean all frames in all layers" }, | |||||
| { 0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Clean Fill Boundaries"; | |||||
| ot->idname = "GPENCIL_OT_frame_clean_fill"; | |||||
| ot->description = "Remove 'no fill' boundary strokes"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_frame_clean_fill_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); | |||||
| } | |||||
| /* *********************** Hide Layers ******************************** */ | /* *********************** Hide Layers ******************************** */ | ||||
| static int gp_hide_exec(bContext *C, wmOperator *op) | static int gp_hide_exec(bContext *C, wmOperator *op) | ||||
| Context not available. | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); | BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); | ||||
| } | } | ||||
| /* read all frames from next layer */ | /* read all frames from next layer and add any missing in current layer */ | ||||
| for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { | for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { | ||||
| /* try to find frame in active layer */ | /* try to find frame in current layer */ | ||||
| bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); | bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); | ||||
| if (!frame) { | if (!frame) { | ||||
| /* nothing found, create new */ | bGPDframe *actframe = BKE_gpencil_layer_getframe(gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); | ||||
| frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); | frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); | ||||
| /* duplicate strokes of current active frame */ | |||||
| if (actframe) { | |||||
| BKE_gpencil_frame_copy_strokes(actframe, frame); | |||||
| } | |||||
| } | } | ||||
| /* add to tail all strokes */ | /* add to tail all strokes */ | ||||
| BLI_movelisttolist(&frame->strokes, &gpf->strokes); | BLI_movelisttolist(&frame->strokes, &gpf->strokes); | ||||
| } | } | ||||
| /* Now delete next layer */ | /* Now delete next layer */ | ||||
| BKE_gpencil_layer_delete(gpd, gpl_next); | BKE_gpencil_layer_delete(gpd, gpl_next); | ||||
| BLI_ghash_free(gh_frames_cur, NULL, NULL); | BLI_ghash_free(gh_frames_cur, NULL, NULL); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| BKE_gpencil_layer_setactive(gpd, gpl); | BKE_gpencil_layer_setactive(gpd, gpl); | ||||
| /* updates */ | /* updates */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| bGPDframe *gpf = gpl->actframe; | |||||
| /* temp listbase to store selected strokes */ | |||||
| ListBase selected = {NULL}; | |||||
| const int direction = RNA_enum_get(op->ptr, "direction"); | const int direction = RNA_enum_get(op->ptr, "direction"); | ||||
| /* verify if any selected stroke is in the extreme of the stack and select to move */ | for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| for (gps = gpf->strokes.first; gps; gps = gps->next) { | /* temp listbase to store selected strokes by layer */ | ||||
| /* only if selected */ | ListBase selected = { NULL }; | ||||
| if (gps->flag & GP_STROKE_SELECT) { | bGPDframe *gpf = gpl->actframe; | ||||
| /* skip strokes that are invalid for current view */ | if (gpl->flag & GP_LAYER_LOCKED) { | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) { | continue; | ||||
| continue; | } | ||||
| } | |||||
| /* check if the color is editable */ | if (gpf == NULL) { | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) { | continue; | ||||
| continue; | } | ||||
| } | bool gpf_lock = false; | ||||
| /* some stroke is already at front*/ | /* verify if any selected stroke is in the extreme of the stack and select to move */ | ||||
| if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { | for (gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| if (gps == gpf->strokes.last) { | /* only if selected */ | ||||
| return OPERATOR_CANCELLED; | if (gps->flag & GP_STROKE_SELECT) { | ||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) { | |||||
| continue; | |||||
| } | } | ||||
| } | /* check if the color is editable */ | ||||
| /* some stroke is already at botom */ | if (ED_gpencil_stroke_color_use(gpl, gps) == false) { | ||||
| if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { | continue; | ||||
| if (gps == gpf->strokes.first) { | } | ||||
| return OPERATOR_CANCELLED; | /* some stroke is already at front*/ | ||||
| if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { | |||||
| if (gps == gpf->strokes.last) { | |||||
| gpf_lock = true; | |||||
| continue; | |||||
| } | |||||
| } | |||||
| /* some stroke is already at botom */ | |||||
| if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { | |||||
| if (gps == gpf->strokes.first) { | |||||
| gpf_lock = true; | |||||
| continue; | |||||
| } | |||||
| } | |||||
| /* add to list (if not locked) */ | |||||
| if (!gpf_lock) { | |||||
| BLI_addtail(&selected, BLI_genericNodeN(gps)); | |||||
| } | } | ||||
| } | } | ||||
| /* add to list */ | |||||
| BLI_addtail(&selected, BLI_genericNodeN(gps)); | |||||
| } | } | ||||
| } | /* Now do the movement of the stroke */ | ||||
| if (!gpf_lock) { | |||||
| /* Now do the movement of the stroke */ | switch (direction) { | ||||
| switch (direction) { | /* Bring to Front */ | ||||
| /* Bring to Front */ | case GP_STROKE_MOVE_TOP: | ||||
| case GP_STROKE_MOVE_TOP: | for (LinkData *link = selected.first; link; link = link->next) { | ||||
| for (LinkData *link = selected.first; link; link = link->next) { | gps = link->data; | ||||
| gps = link->data; | BLI_remlink(&gpf->strokes, gps); | ||||
| BLI_remlink(&gpf->strokes, gps); | BLI_addtail(&gpf->strokes, gps); | ||||
| BLI_addtail(&gpf->strokes, gps); | } | ||||
| } | break; | ||||
| break; | /* Bring Forward */ | ||||
| /* Bring Forward */ | case GP_STROKE_MOVE_UP: | ||||
| case GP_STROKE_MOVE_UP: | for (LinkData *link = selected.last; link; link = link->prev) { | ||||
| for (LinkData *link = selected.last; link; link = link->prev) { | gps = link->data; | ||||
| gps = link->data; | BLI_listbase_link_move(&gpf->strokes, gps, 1); | ||||
| BLI_listbase_link_move(&gpf->strokes, gps, 1); | } | ||||
| } | break; | ||||
| break; | /* Send Backward */ | ||||
| /* Send Backward */ | case GP_STROKE_MOVE_DOWN: | ||||
| case GP_STROKE_MOVE_DOWN: | for (LinkData *link = selected.first; link; link = link->next) { | ||||
| for (LinkData *link = selected.first; link; link = link->next) { | gps = link->data; | ||||
| gps = link->data; | BLI_listbase_link_move(&gpf->strokes, gps, -1); | ||||
| BLI_listbase_link_move(&gpf->strokes, gps, -1); | } | ||||
| } | break; | ||||
| break; | /* Send to Back */ | ||||
| /* Send to Back */ | case GP_STROKE_MOVE_BOTTOM: | ||||
| case GP_STROKE_MOVE_BOTTOM: | for (LinkData *link = selected.last; link; link = link->prev) { | ||||
| for (LinkData *link = selected.last; link; link = link->prev) { | gps = link->data; | ||||
| gps = link->data; | BLI_remlink(&gpf->strokes, gps); | ||||
| BLI_remlink(&gpf->strokes, gps); | BLI_addhead(&gpf->strokes, gps); | ||||
| BLI_addhead(&gpf->strokes, gps); | } | ||||
| break; | |||||
| default: | |||||
| BLI_assert(0); | |||||
| break; | |||||
| } | } | ||||
| break; | } | ||||
| default: | BLI_freelistN(&selected); | ||||
| BLI_assert(0); | |||||
| break; | |||||
| } | } | ||||
| BLI_freelistN(&selected); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| ot->idname = "GPENCIL_OT_stroke_arrange"; | ot->idname = "GPENCIL_OT_stroke_arrange"; | ||||
| ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; | ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; | ||||
| /* api callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_stroke_arrange_exec; | ot->exec = gp_stroke_arrange_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | |||||
| ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); | ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); | ||||
| } | } | ||||
| /* ******************* Move Stroke to new color ************************** */ | |||||
| static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) | /* ******************* Move Stroke to new palette ************************** */ | ||||
| static int gp_stroke_change_palette_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| Scene *scene = CTX_data_scene(C); | |||||
| const int type = RNA_enum_get(op->ptr, "type"); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette; | bGPDpaletteref *palslot = BKE_gpencil_paletteslot_get_active(gpd); | ||||
| bGPDpalettecolor *color; | Palette *palette; | ||||
| PaletteColor *palcolor; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd)) { | if (ELEM(NULL, gpd, palslot)) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| palette = BKE_gpencil_palette_getactive(gpd); | palette = palslot->palette; | ||||
| color = BKE_gpencil_palettecolor_getactive(palette); | if (ELEM(NULL, palette)) { | ||||
| if (ELEM(NULL, palette, color)) { | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* loop all strokes */ | /* loop all strokes */ | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| /* only editable and visible layers are considered */ | /* only editable and visible layers are considered */ | ||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | if (!gpencil_layer_is_editable(gpl)) | ||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | continue; | ||||
| for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | |||||
| /* check frame if frame range */ | |||||
| if ((type == GP_MOVE_PALETTE_BEFORE) && (gpf->framenum >= scene->r.cfra)) | |||||
| continue; | |||||
| if ((type == GP_MOVE_PALETTE_AFTER) && (gpf->framenum < scene->r.cfra)) | |||||
| continue; | |||||
| if ((type == GP_MOVE_PALETTE_CURRENT) && (gpf->framenum != scene->r.cfra)) | |||||
| continue; | |||||
| for (bGPDstroke *gps = gpf->strokes.last; gps; gps = gps->prev) { | |||||
| /* only if selected */ | /* only if selected */ | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (((gps->flag & GP_STROKE_SELECT) == 0) && (type == GP_MOVE_PALETTE_SELECT)) | ||||
| /* skip strokes that are invalid for current view */ | continue; | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | /* skip strokes that are invalid for current view */ | ||||
| continue; | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| /* check if the color is editable */ | continue; | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | /* check if the color is editable */ | ||||
| continue; | if (ED_gpencil_stroke_color_use(gpl, gps) == false) | ||||
| continue; | |||||
| /* look for new color */ | |||||
| palcolor = BKE_palette_color_getbyname(palette, gps->colorname); | |||||
| /* if the color does not exist, create a new one to keep stroke */ | |||||
| if (palcolor == NULL) { | |||||
| palcolor = BKE_palette_color_add_name(palette, gps->colorname); | |||||
| copy_v4_v4(palcolor->rgb, gps->palcolor->rgb); | |||||
| copy_v4_v4(palcolor->fill, gps->palcolor->fill); | |||||
| /* duplicate flags */ | |||||
| palcolor->flag = gps->palcolor->flag; | |||||
| palcolor->stroke_style = gps->palcolor->stroke_style; | |||||
| palcolor->fill_style = gps->palcolor->fill_style; | |||||
| } | |||||
| /* asign new color */ | |||||
| BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); | |||||
| gps->palette = palette; | |||||
| gps->palcolor = palcolor; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* notifiers */ | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_stroke_change_palette(wmOperatorType *ot) | |||||
| { | |||||
| static EnumPropertyItem palette_move_type[] = { | |||||
| { GP_MOVE_PALETTE_SELECT, "SELECTED", 0, "Change Strokes Selected", "Move to new palette any stroke selected in any frame" }, | |||||
| { GP_MOVE_PALETTE_ALL, "ALL", 0, "Change All Frames", "Move all strokes in all frames to new palette" }, | |||||
| { GP_MOVE_PALETTE_BEFORE, "BEFORE", 0, "Change Frames Before", "Move all strokes in frames before current frame to new palette" }, | |||||
| { GP_MOVE_PALETTE_AFTER, "AFTER", 0, "Change Frames After", "Move all strokes in frames greater or equal current frame to new palette" }, | |||||
| { GP_MOVE_PALETTE_CURRENT, "CURRENT", 0, "Change Current Frame", "Move all strokes in current frame to new palette" }, | |||||
| { 0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Change Stroke Palette"; | |||||
| ot->idname = "GPENCIL_OT_stroke_change_palette"; | |||||
| ot->description = "Move strokes to active palette"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_stroke_change_palette_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* properties */ | |||||
| ot->prop = RNA_def_enum(ot->srna, "type", palette_move_type, GP_MOVE_PALETTE_SELECT, "Type", ""); | |||||
| } | |||||
| /* ******************* Move Stroke to new color ************************** */ | |||||
| static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpaletteref *palslot = BKE_gpencil_paletteslot_get_active(gpd); | |||||
| Palette *palette; | |||||
| PaletteColor *color; | |||||
| /* asign new color (only if different) */ | /* sanity checks */ | ||||
| if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) { | if (ELEM(NULL, gpd, palslot)) { | ||||
| BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname)); | return OPERATOR_CANCELLED; | ||||
| gps->palcolor = color; | } | ||||
| bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); | |||||
| palette = palslot->palette; | |||||
| color = BKE_palette_color_get_active(palette); | |||||
| if (ELEM(NULL, color)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| /* loop all strokes */ | |||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | |||||
| { | |||||
| bGPDframe *init_gpf = gpl->actframe; | |||||
| if (is_multiedit) { | |||||
| init_gpf = gpl->frames.first; | |||||
| } | |||||
| for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { | |||||
| if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { | |||||
| if (gpf == NULL) | |||||
| continue; | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | |||||
| /* only if selected */ | |||||
| if (gps->flag & GP_STROKE_SELECT) { | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| /* check if the color is editable */ | |||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | |||||
| continue; | |||||
| /* asign new color (only if different) */ | |||||
| if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) { | |||||
| BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname)); | |||||
| gps->palette = palette; | |||||
| gps->palcolor = color; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | |||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| ot->idname = "GPENCIL_OT_stroke_change_color"; | ot->idname = "GPENCIL_OT_stroke_change_color"; | ||||
| ot->description = "Move selected strokes to active color"; | ot->description = "Move selected strokes to active color"; | ||||
| /* api callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_stroke_change_color_exec; | ot->exec = gp_stroke_change_color_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | } | ||||
| /* ******************* Lock color of non selected Strokes colors ************************** */ | /* ******************* Lock color of non selected Strokes colors ************************** */ | ||||
| Context not available. | |||||
| static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) | static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette; | bGPDpaletteref *palslot = BKE_gpencil_paletteslot_get_active(gpd); | ||||
| Palette *palette; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd)) | if (ELEM(NULL, gpd, palslot)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| palette = BKE_gpencil_palette_getactive(gpd); | palette = palslot->palette; | ||||
| if (ELEM(NULL, palette)) | if (ELEM(NULL, palette)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* first lock all colors */ | /* first lock all colors */ | ||||
| for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | for (PaletteColor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | ||||
| palcolor->flag |= PC_COLOR_LOCKED; | palcolor->flag |= PC_COLOR_LOCKED; | ||||
| } | } | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_stroke_lock_color_exec; | ot->exec = gp_stroke_lock_color_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | } | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Drawing Brushes Operators */ | /* Palette Slot Operators */ | ||||
| /* ******************* Add New Brush ************************ */ | /* ********************* Add Palette SLot ************************* */ | ||||
| /* add new brush - wrapper around API */ | static int gp_paletteslot_add_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| static int gp_brush_add_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | bGPdata *gpd = CTX_data_gpencil_data(C); | ||||
| /* if there's no existing container */ | /* just add an empty slot */ | ||||
| if (ts == NULL) { | BKE_gpencil_paletteslot_add(gpd, NULL); | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, NULL); | ||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| /* add new brush now */ | |||||
| BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_add(wmOperatorType *ot) | void GPENCIL_OT_palette_slot_add(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Add Brush"; | ot->name = "Add Palette Slot"; | ||||
| ot->idname = "GPENCIL_OT_brush_add"; | ot->idname = "GPENCIL_OT_palette_slot_add"; | ||||
| ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil data-block"; | ot->description = "Add new Palette Slot to refer to a Palette used by this Grease Pencil object"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_brush_add_exec; | ot->exec = gp_paletteslot_add_exec; | ||||
| ot->poll = gp_add_poll; | ot->poll = gp_active_layer_poll; // XXX | ||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | } | ||||
| /* ******************* Remove Active Brush ************************* */ | /* ******************* Remove Palette Slot *********************** */ | ||||
| static int gp_brush_remove_exec(bContext *C, wmOperator *op) | static int gp_paletteslot_active_poll(bContext *C) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | bGPDpaletteref *palslot = BKE_gpencil_paletteslot_get_active(gpd); | ||||
| /* sanity checks */ | return (palslot != NULL); | ||||
| if (ELEM(NULL, ts, brush)) | } | ||||
| return OPERATOR_CANCELLED; | |||||
| if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) { | static int gp_paletteslot_remove_exec(bContext *C, wmOperator *op) | ||||
| BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one"); | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpaletteref *palslot = BKE_gpencil_paletteslot_get_active(gpd); | |||||
| /* 1) Check if palette is still used anywhere */ | |||||
| if (BKE_gpencil_paletteslot_has_users(gpd, palslot)) { | |||||
| /* XXX: Change strokes to the new active slot's palette instead? */ | |||||
| BKE_report(op->reports, RPT_ERROR, "Cannot remove, Palette still in use"); | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* 2) Remove the slot (will unlink user and free it) */ | |||||
| if ((palslot->next == NULL) && (gpd->active_palette_slot > 0)) { | |||||
| /* fix active slot index */ | |||||
| gpd->active_palette_slot--; | |||||
| } | |||||
| BKE_gpencil_palette_slot_free(gpd, palslot); | |||||
| /* updates */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_REMOVED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_palette_slot_remove(wmOperatorType *ot) | |||||
| /* make the brush before this the new active brush | |||||
| * - use the one after if this is the first | |||||
| * - if this is the only brush, this naturally becomes NULL | |||||
| */ | |||||
| if (brush->prev) | |||||
| BKE_gpencil_brush_setactive(ts, brush->prev); | |||||
| else | |||||
| BKE_gpencil_brush_setactive(ts, brush->next); | |||||
| /* delete the brush now... */ | |||||
| BKE_gpencil_brush_delete(ts, brush); | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_brush_remove(wmOperatorType *ot) | |||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Remove Brush"; | ot->name = "Remove Palette Slot"; | ||||
| ot->idname = "GPENCIL_OT_brush_remove"; | ot->idname = "GPENCIL_OT_palette_slot_remove"; | ||||
| ot->description = "Remove active Grease Pencil drawing brush"; | ot->description = "Remove active Palette Slot to refer to a Palette used by this Grease Pencil object"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_brush_remove_exec; | |||||
| ot->poll = gp_active_brush_poll; | |||||
| } | |||||
| /* ********************** Change Brush ***************************** */ | |||||
| static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) | |||||
| { | |||||
| uiPopupMenu *pup; | |||||
| uiLayout *layout; | |||||
| /* call the menu, which will call this operator again, hence the canceled */ | |||||
| pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); | |||||
| layout = UI_popup_menu_layout(pup); | |||||
| uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush"); | |||||
| UI_popup_menu_end(C, pup); | |||||
| return OPERATOR_INTERFACE; | |||||
| } | |||||
| static int gp_brush_change_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| bGPDbrush *brush = NULL; | |||||
| int brush_num = RNA_enum_get(op->ptr, "brush"); | |||||
| /* Get brush or create new one */ | |||||
| if (brush_num == -1) { | |||||
| /* Create brush */ | |||||
| brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); | |||||
| } | |||||
| else { | |||||
| /* Try to get brush */ | |||||
| brush = BLI_findlink(&ts->gp_brushes, brush_num); | |||||
| if (brush == NULL) { | |||||
| BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| } | |||||
| /* Set active brush */ | |||||
| BKE_gpencil_brush_setactive(ts, brush); | |||||
| /* updates */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_brush_change(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Change Brush"; | |||||
| ot->idname = "GPENCIL_OT_brush_change"; | |||||
| ot->description = "Change active Grease Pencil drawing brush"; | |||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->invoke = gp_brush_change_invoke; | ot->exec = gp_paletteslot_remove_exec; | ||||
| ot->exec = gp_brush_change_exec; | ot->poll = gp_paletteslot_active_poll; | ||||
| ot->poll = gp_active_brush_poll; | |||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* gp brush to use (dynamic enum) */ | |||||
| ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", ""); | |||||
| RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf); | |||||
| } | |||||
| /* ******************* Move Brush Up/Down ************************** */ | |||||
| enum { | |||||
| GP_BRUSH_MOVE_UP = -1, | |||||
| GP_BRUSH_MOVE_DOWN = 1 | |||||
| }; | |||||
| static int gp_brush_move_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | |||||
| int direction = RNA_enum_get(op->ptr, "type"); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, ts, brush)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| /* up or down? */ | |||||
| if (direction == GP_BRUSH_MOVE_UP) { | |||||
| /* up */ | |||||
| BLI_remlink(&ts->gp_brushes, brush); | |||||
| BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush); | |||||
| } | |||||
| else if (direction == GP_BRUSH_MOVE_DOWN) { | |||||
| /* down */ | |||||
| BLI_remlink(&ts->gp_brushes, brush); | |||||
| BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush); | |||||
| } | |||||
| else { | |||||
| BLI_assert(0); | |||||
| } | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | } | ||||
| void GPENCIL_OT_brush_move(wmOperatorType *ot) | |||||
| { | |||||
| static const EnumPropertyItem slot_move[] = { | |||||
| {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""}, | |||||
| {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""}, | |||||
| {0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Move Brush"; | |||||
| ot->idname = "GPENCIL_OT_brush_move"; | |||||
| ot->description = "Move the active Grease Pencil drawing brush up/down in the list"; | |||||
| /* api callbacks */ | |||||
| ot->exec = gp_brush_move_exec; | |||||
| ot->poll = gp_active_brush_poll; | |||||
| /* flags */ | /* ************************************************ */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | /* Drawing Brushes Operators */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", ""); | |||||
| } | |||||
| /* ******************* Brush create presets ************************** */ | /* ******************* Brush create presets ************************** */ | ||||
| static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) | static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | BKE_brush_gpencil_presets(C); | ||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| Context not available. | |||||
| } | } | ||||
| /* ***************** Copy Brush ************************ */ | |||||
| static int gp_brush_copy_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| /* if there's no existing container */ | |||||
| if (ts == NULL) { | |||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | |||||
| bGPDbrush *newbrush; | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, brush)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* create a brush and duplicate data */ | |||||
| newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true); | |||||
| newbrush->thickness = brush->thickness; | |||||
| newbrush->draw_smoothfac = brush->draw_smoothfac; | |||||
| newbrush->draw_smoothlvl = brush->draw_smoothlvl; | |||||
| newbrush->sublevel = brush->sublevel; | |||||
| newbrush->flag = brush->flag; | |||||
| newbrush->draw_sensitivity = brush->draw_sensitivity; | |||||
| newbrush->draw_strength = brush->draw_strength; | |||||
| newbrush->draw_jitter = brush->draw_jitter; | |||||
| newbrush->draw_angle = brush->draw_angle; | |||||
| newbrush->draw_angle_factor = brush->draw_angle_factor; | |||||
| newbrush->draw_random_press = brush->draw_random_press; | |||||
| newbrush->draw_random_sub = brush->draw_random_sub; | |||||
| /* free automatic curves created by default (replaced by copy) */ | |||||
| curvemapping_free(newbrush->cur_sensitivity); | |||||
| curvemapping_free(newbrush->cur_strength); | |||||
| curvemapping_free(newbrush->cur_jitter); | |||||
| /* make a copy of curves */ | |||||
| newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity); | |||||
| newbrush->cur_strength = curvemapping_copy(brush->cur_strength); | |||||
| newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); | |||||
| BKE_gpencil_brush_setactive(ts, newbrush); | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_brush_copy(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Copy Brush"; | |||||
| ot->idname = "GPENCIL_OT_brush_copy"; | |||||
| ot->description = "Copy current Grease Pencil drawing brush"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_brush_copy_exec; | |||||
| ot->poll = gp_active_brush_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | |||||
| /* ***************** Select Brush ************************ */ | /* ***************** Select Brush ************************ */ | ||||
| static int gp_brush_select_exec(bContext *C, wmOperator *op) | static int gp_brush_select_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| Main *bmain = CTX_data_main(C); | |||||
| /* if there's no existing container */ | /* if there's no existing container */ | ||||
| if (ts == NULL) { | if (ts == NULL) { | ||||
| Context not available. | |||||
| } | } | ||||
| const int index = RNA_int_get(op->ptr, "index"); | const int index = RNA_int_get(op->ptr, "index"); | ||||
| bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index); | |||||
| /* sanity checks */ | Paint *paint = BKE_brush_get_gpencil_paint(ts); | ||||
| if (ELEM(NULL, brush)) { | int i = 0; | ||||
| return OPERATOR_CANCELLED; | for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { | ||||
| } | if (brush->ob_mode == OB_MODE_GPENCIL_PAINT) { | ||||
| if (i == index) { | |||||
| BKE_gpencil_brush_setactive(ts, brush); | BKE_paint_brush_set(paint, brush); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| i++; | |||||
| } | |||||
| } | |||||
| return OPERATOR_FINISHED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_select(wmOperatorType *ot) | void GPENCIL_OT_brush_select(wmOperatorType *ot) | ||||
| Context not available. | |||||
| RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); | RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); | ||||
| } | } | ||||
| /* ************************************************ */ | /* ***************** Select Sculpt Brush ************************ */ | ||||
| /* Palette Operators */ | |||||
| /* ******************* Add New Palette ************************ */ | |||||
| /* add new palette - wrapper around API */ | static int gp_sculpt_select_exec(bContext *C, wmOperator *op) | ||||
| static int gp_palette_add_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| /* if there's no existing Grease-Pencil data there, add some */ | /* if there's no existing container */ | ||||
| if (gpd_ptr == NULL) { | if (ts == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (*gpd_ptr == NULL) | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); | |||||
| /* add new palette now */ | |||||
| BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_palette_add(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Add Palette"; | |||||
| ot->idname = "GPENCIL_OT_palette_add"; | |||||
| ot->description = "Add new Grease Pencil palette for the active Grease Pencil data-block"; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_palette_add_exec; | |||||
| ot->poll = gp_add_poll; | |||||
| } | |||||
| /* ******************* Remove Active Palette ************************* */ | |||||
| static int gp_palette_remove_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| const int index = RNA_int_get(op->ptr, "index"); | |||||
| GP_BrushEdit_Settings *gp_sculpt = &ts->gp_sculpt; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette)) | if (ELEM(NULL, gp_sculpt)) { | ||||
| return OPERATOR_CANCELLED; | |||||
| if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) { | |||||
| BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one"); | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (index < TOT_GP_EDITBRUSH_TYPES - 1) { | |||||
| /* make the palette before this the new active palette | gp_sculpt->brushtype = index; | ||||
| * - use the one after if this is the first | } | ||||
| * - if this is the only palette, this naturally becomes NULL | |||||
| */ | |||||
| if (palette->prev) | |||||
| BKE_gpencil_palette_setactive(gpd, palette->prev); | |||||
| else | |||||
| BKE_gpencil_palette_setactive(gpd, palette->next); | |||||
| /* delete the palette now... */ | |||||
| BKE_gpencil_palette_delete(gpd, palette); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palette_remove(wmOperatorType *ot) | void GPENCIL_OT_sculpt_select(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Remove palette"; | ot->name = "Select Sculpt Brush"; | ||||
| ot->idname = "GPENCIL_OT_palette_remove"; | ot->idname = "GPENCIL_OT_sculpt_select"; | ||||
| ot->description = "Remove active Grease Pencil palette"; | ot->description = "Select a Grease Pencil sculpt brush"; | ||||
| /* callbacks */ | |||||
| ot->exec = gp_sculpt_select_exec; | |||||
| ot->poll = gp_add_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* callbacks */ | /* properties */ | ||||
| ot->exec = gp_palette_remove_exec; | RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Sculpt Brush", 0, INT_MAX); | ||||
| ot->poll = gp_active_palette_poll; | |||||
| } | } | ||||
| /* ********************** Change Palette ***************************** */ | /* ******************* Convert animation data ************************ */ | ||||
| static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) | static int gp_convert_old_palettes_poll(bContext *C) | ||||
| { | { | ||||
| uiPopupMenu *pup; | /* TODO: need better poll */ | ||||
| uiLayout *layout; | Main *bmain = CTX_data_main(C); | ||||
| return bmain->gpencil.first != NULL; | |||||
| /* call the menu, which will call this operator again, hence the canceled */ | |||||
| pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); | |||||
| layout = UI_popup_menu_layout(pup); | |||||
| uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette"); | |||||
| UI_popup_menu_end(C, pup); | |||||
| return OPERATOR_INTERFACE; | |||||
| } | } | ||||
| static int gp_palette_change_exec(bContext *C, wmOperator *op) | /* convert old animation data to new format */ | ||||
| static int gp_convert_old_palettes_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | Main *bmain = CTX_data_main(C); | ||||
| bGPDpalette *palette = NULL; | for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { | ||||
| int palette_num = RNA_enum_get(op->ptr, "palette"); | BKE_gpencil_move_animdata_to_palettes(C, gpd); | ||||
| /* Get palette or create new one */ | |||||
| if (palette_num == -1) { | |||||
| /* Create palette */ | |||||
| palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); | |||||
| } | |||||
| else { | |||||
| /* Try to get palette */ | |||||
| palette = BLI_findlink(&gpd->palettes, palette_num); | |||||
| if (palette == NULL) { | |||||
| BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| } | } | ||||
| /* notifiers */ | |||||
| /* Set active palette */ | |||||
| BKE_gpencil_palette_setactive(gpd, palette); | |||||
| /* updates */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palette_change(wmOperatorType *ot) | void GPENCIL_OT_convert_old_palettes(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Change Palette"; | ot->name = "Convert Old Palettes"; | ||||
| ot->idname = "GPENCIL_OT_palette_change"; | ot->idname = "GPENCIL_OT_convert_old_palettes"; | ||||
| ot->description = "Change active Grease Pencil palette"; | ot->description = "Convert old gpencil palettes animation data to blender palettes"; | ||||
| /* callbacks */ | |||||
| ot->invoke = gp_palette_change_invoke; | |||||
| ot->exec = gp_palette_change_exec; | |||||
| ot->poll = gp_active_palette_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* gp palette to use (dynamic enum) */ | /* callbacks */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", ""); | ot->exec = gp_convert_old_palettes_exec; | ||||
| RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf); | ot->poll = gp_convert_old_palettes_poll; | ||||
| } | } | ||||
| /* ******************* Lock and hide any color non used in current layer ************************** */ | /* ******************* Convert scene gp data to gp object ************************ */ | ||||
| static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) | static int gp_convert_scene_to_object_poll(bContext *C) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Scene *scene = CTX_data_scene(C); | ||||
| bGPDpalette *palette; | if (scene->gpd) { | ||||
| return true; | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd)) | |||||
| return OPERATOR_CANCELLED; | |||||
| palette = BKE_gpencil_palette_getactive(gpd); | |||||
| if (ELEM(NULL, palette)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* first lock and hide all colors */ | |||||
| for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | |||||
| palcolor->flag |= PC_COLOR_LOCKED; | |||||
| palcolor->flag |= PC_COLOR_HIDE; | |||||
| } | } | ||||
| else { | |||||
| /* loop all selected strokes and unlock any color used in active layer */ | return false; | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | |||||
| /* only editable and visible layers are considered */ | |||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { | |||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| /* unlock/unhide color if not unlocked before */ | |||||
| if (gps->palcolor != NULL) { | |||||
| gps->palcolor->flag &= ~PC_COLOR_LOCKED; | |||||
| gps->palcolor->flag &= ~PC_COLOR_HIDE; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | } | ||||
| void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) | /* convert scene datablock to gpencil object */ | ||||
| static int gp_convert_scene_to_object_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| /* identifiers */ | Scene *scene = CTX_data_scene(C); | ||||
| ot->name = "Disable Unused Layer Colors"; | bGPdata *gpd = scene->gpd; | ||||
| ot->idname = "GPENCIL_OT_palette_lock_layer"; | float loc[3] = { 0.0f, 0.0f, 0.0f }; | ||||
| ot->description = "Lock and hide any color not used in any layer"; | |||||
| /* api callbacks */ | |||||
| ot->exec = gp_palette_lock_layer_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| } | |||||
| /* ************************************************ */ | Object *ob = ED_add_gpencil_object(C, scene, loc); /* always in origin */ | ||||
| /* Palette Colors Operators */ | |||||
| // FIXME: This loses the datablock created above... | |||||
| /* ******************* Add New Palette ************************ */ | ob->data = gpd; | ||||
| scene->gpd = NULL; | |||||
| /* add new palette - wrapper around API */ | |||||
| static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | |||||
| /* if there's no existing Grease-Pencil data there, add some */ | |||||
| if (gpd_ptr == NULL) { | |||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| if (*gpd_ptr == NULL) | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); | |||||
| /* verify palette */ | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); | |||||
| if (palette == NULL) | |||||
| palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); | |||||
| /* add new palette color now */ | |||||
| BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(gpd); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) | void GPENCIL_OT_convert_scene_to_object(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Add Palette Color"; | ot->name = "Convert Scene Datablock to gpencil Object"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_add"; | ot->idname = "GPENCIL_OT_convert_scene_to_object"; | ||||
| ot->description = "Add new Grease Pencil palette color for the active Grease Pencil data-block"; | ot->description = "Convert scene grease pencil datablock to gpencil object"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_palettecolor_add_exec; | ot->exec = gp_convert_scene_to_object_exec; | ||||
| ot->poll = gp_add_poll; | ot->poll = gp_convert_scene_to_object_poll; | ||||
| } | } | ||||
| /* ******************* Remove Active Palette color ************************* */ | /*********************** Vertex Groups ***********************************/ | ||||
| static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) | static int gpencil_vertex_group_poll(bContext *C) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette); | |||||
| /* sanity checks */ | if ((ob) && (ob->type == OB_GPENCIL)) { | ||||
| if (ELEM(NULL, gpd, palette, color)) | if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { | ||||
| return OPERATOR_CANCELLED; | if (ELEM(ob->mode, | ||||
| OB_MODE_GPENCIL_EDIT, | |||||
| /* make the palette color before this the new active color | OB_MODE_GPENCIL_SCULPT)) | ||||
| * - use the one after if this is the first | { | ||||
| * - if this is the only color, this naturally becomes NULL | return true; | ||||
| */ | } | ||||
| if (color->prev) | } | ||||
| BKE_gpencil_palettecolor_setactive(palette, color->prev); | } | ||||
| else | |||||
| BKE_gpencil_palettecolor_setactive(palette, color->next); | |||||
| /* delete the strokes */ | |||||
| BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); | |||||
| /* delete the palette color now... */ | |||||
| BKE_gpencil_palettecolor_delete(palette, color); | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | return false; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) | static int gpencil_vertex_group_weight_poll(bContext *C) | ||||
| { | { | ||||
| /* identifiers */ | Object *ob = CTX_data_active_object(C); | ||||
| ot->name = "Remove palette color"; | |||||
| ot->idname = "GPENCIL_OT_palettecolor_remove"; | |||||
| ot->description = "Remove active Grease Pencil palette color"; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | if ((ob) && (ob->type == OB_GPENCIL)) { | ||||
| if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { | |||||
| if (ob->mode == OB_MODE_GPENCIL_WEIGHT) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* callbacks */ | return false; | ||||
| ot->exec = gp_palettecolor_remove_exec; | |||||
| ot->poll = gp_active_palettecolor_poll; | |||||
| } | } | ||||
| /* ********************** Isolate palette color **************************** */ | static int gpencil_vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); | |||||
| bGPDpalettecolor *palcolor; | |||||
| int flags = PC_COLOR_LOCKED; | |||||
| bool isolate = false; | |||||
| if (RNA_boolean_get(op->ptr, "affect_visibility")) | |||||
| flags |= PC_COLOR_HIDE; | |||||
| if (ELEM(NULL, gpd, active_color)) { | /* sanity checks */ | ||||
| BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); | if (ELEM(NULL, ts, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | |||||
| /* Test whether to isolate or clear all flags */ | |||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | |||||
| /* Skip if this is the active one */ | |||||
| if (palcolor == active_color) | |||||
| continue; | |||||
| /* If the flags aren't set, that means that the color is | ED_gpencil_vgroup_assign(C, ob, ts->vgroup_weight); | ||||
| * not alone, so we have some colors to isolate still | |||||
| */ | |||||
| if ((palcolor->flag & flags) == 0) { | |||||
| isolate = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* Set/Clear flags as appropriate */ | |||||
| if (isolate) { | |||||
| /* Set flags on all "other" colors */ | |||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | |||||
| if (palcolor == active_color) | |||||
| continue; | |||||
| else | |||||
| palcolor->flag |= flags; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* Clear flags - Restore everything else */ | |||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | |||||
| palcolor->flag &= ~flags; | |||||
| } | |||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(ob->data); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_assign(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Isolate Palette Color"; | ot->name = "Assign to Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_isolate"; | ot->idname = "GPENCIL_OT_vertex_group_assign"; | ||||
| ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; | ot->description = "Assign the selected vertices to the active vertex group"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_isolate_palettecolor_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_active_palettecolor_poll; | ot->exec = gpencil_vertex_group_assign_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | |||||
| RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " | |||||
| "the editability, also affect the visibility"); | |||||
| } | } | ||||
| /* *********************** Hide Palette colors ******************************** */ | /* remove point from vertex group */ | ||||
| static int gpencil_vertex_group_remove_from_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| bool unselected = RNA_boolean_get(op->ptr, "unselected"); | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette, palcolor)) | if (ELEM(NULL, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| if (unselected) { | ED_gpencil_vgroup_remove(C, ob); | ||||
| bGPDpalettecolor *color; | |||||
| /* hide unselected */ | |||||
| for (color = palette->colors.first; color; color = color->next) { | |||||
| if (color != palcolor) { | |||||
| color->flag |= PC_COLOR_HIDE; | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* hide selected/active */ | |||||
| palcolor->flag |= PC_COLOR_HIDE; | |||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(ob->data); // XXX: Review this (aligorith) | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_remove_from(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Hide Color(s)"; | ot->name = "Remove from Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_hide"; | ot->idname = "GPENCIL_OT_vertex_group_remove_from"; | ||||
| ot->description = "Hide selected/unselected Grease Pencil colors"; | ot->description = "Remove the selected vertices from active or all vertex group(s)"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_palettecolor_hide_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */ | ot->exec = gpencil_vertex_group_remove_from_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* props */ | |||||
| RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); | |||||
| } | |||||
| /* ********************** Show All Colors ***************************** */ | |||||
| /* poll callback for showing colors */ | |||||
| static int gp_palettecolor_reveal_poll(bContext *C) | |||||
| { | |||||
| return ED_gpencil_data_get_active(C) != NULL; | |||||
| } | } | ||||
| static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) | static int gpencil_vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette)) | if (ELEM(NULL, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all colors visible */ | ED_gpencil_vgroup_select(C, ob); | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | |||||
| palcolor->flag &= ~PC_COLOR_HIDE; | |||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(ob->data); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_select(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Show All Colors"; | ot->name = "Select Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_reveal"; | ot->idname = "GPENCIL_OT_vertex_group_select"; | ||||
| ot->description = "Unhide all hidden Grease Pencil palette colors"; | ot->description = "Select all the vertices assigned to the active vertex group"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_palettecolor_reveal_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_palettecolor_reveal_poll; | ot->exec = gpencil_vertex_group_select_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ***************** Lock/Unlock All Palette colors ************************ */ | static int gpencil_vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette)) | if (ELEM(NULL, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all layers non-editable */ | ED_gpencil_vgroup_deselect(C, ob); | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | |||||
| palcolor->flag |= PC_COLOR_LOCKED; | |||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(ob->data); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_deselect(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Lock All Colors"; | ot->name = "Deselect Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_lock_all"; | ot->idname = "GPENCIL_OT_vertex_group_deselect"; | ||||
| ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; | ot->description = "Deselect all selected vertices assigned to the active vertex group"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_palettecolor_lock_all_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | ot->exec = gpencil_vertex_group_deselect_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* -------------------------- */ | /* invert */ | ||||
| static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalettecolor *palcolor; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette)) | if (ELEM(NULL, ts, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | |||||
| bGPDspoint *pt; | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all layers editable again*/ | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | { | ||||
| palcolor->flag &= ~PC_COLOR_LOCKED; | for (int i = 0; i < gps->totpoints; i++) { | ||||
| pt = &gps->points[i]; | |||||
| if (pt->weights == NULL) { | |||||
| BKE_gpencil_vgroup_add_point_weight(pt, def_nr, 1.0f); | |||||
| } | |||||
| else if (pt->weights->factor == 1.0f) { | |||||
| BKE_gpencil_vgroup_remove_point_weight(pt, def_nr); | |||||
| } | |||||
| else { | |||||
| pt->weights->factor = 1.0f - pt->weights->factor; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| CTX_DATA_END; | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(ob->data); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_invert(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Unlock All Colors"; | ot->name = "Invert Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; | ot->idname = "GPENCIL_OT_vertex_group_invert"; | ||||
| ot->description = "Unlock all Grease Pencil colors so that they can be edited"; | ot->description = "Invert weights to the active vertex group"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_palettecolor_unlock_all_exec; | ot->poll = gpencil_vertex_group_weight_poll; | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | ot->exec = gpencil_vertex_group_invert_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ******************* Move Color Up/Down ************************** */ | /* smooth */ | ||||
| static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) | |||||
| enum { | |||||
| GP_COLOR_MOVE_UP = -1, | |||||
| GP_COLOR_MOVE_DOWN = 1 | |||||
| }; | |||||
| static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | const float fac = RNA_float_get(op->ptr, "factor"); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | const int repeat = RNA_int_get(op->ptr, "repeat"); | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| int direction = RNA_enum_get(op->ptr, "direction"); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| Object *ob = CTX_data_active_object(C); | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette, palcolor)) | if (ELEM(NULL, ts, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* up or down? */ | bGPDspoint *pta, *ptb, *ptc; | ||||
| if (direction == GP_COLOR_MOVE_UP) { | |||||
| /* up */ | const int def_nr = ob->actdef - 1; | ||||
| BLI_remlink(&palette->colors, palcolor); | if (!BLI_findlink(&ob->defbase, def_nr)) | ||||
| BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor); | return OPERATOR_CANCELLED; | ||||
| } | |||||
| else if (direction == GP_COLOR_MOVE_DOWN) { | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| /* down */ | { | ||||
| BLI_remlink(&palette->colors, palcolor); | for (int s = 0; s < repeat; s++) { | ||||
| BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor); | for (int i = 0; i < gps->totpoints; i++) { | ||||
| } | /* previous point */ | ||||
| else { | if (i > 0) { | ||||
| BLI_assert(0); | pta = &gps->points[i - 1]; | ||||
| } | |||||
| else { | |||||
| pta = &gps->points[i]; | |||||
| } | |||||
| /* current */ | |||||
| ptb = &gps->points[i]; | |||||
| /* next point */ | |||||
| if (i + 1 < gps->totpoints) { | |||||
| ptc = &gps->points[i + 1]; | |||||
| } | |||||
| else { | |||||
| ptc = &gps->points[i]; | |||||
| } | |||||
| float wa = BKE_gpencil_vgroup_use_index(pta, def_nr); | |||||
| float wb = BKE_gpencil_vgroup_use_index(ptb, def_nr); | |||||
| float wc = BKE_gpencil_vgroup_use_index(ptc, def_nr); | |||||
| CLAMP_MIN(wa, 0.0f); | |||||
| CLAMP_MIN(wb, 0.0f); | |||||
| CLAMP_MIN(wc, 0.0f); | |||||
| /* the optimal value is the corresponding to the interpolation of the weight | |||||
| * at the distance of point b | |||||
| */ | |||||
| const float opfac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); | |||||
| const float optimal = interpf(wa, wb, opfac); | |||||
| /* Based on influence factor, blend between original and optimal */ | |||||
| wb = interpf(wb, optimal, fac); | |||||
| BKE_gpencil_vgroup_add_point_weight(ptb, def_nr, wb); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| CTX_DATA_END; | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(ob->data); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_smooth(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem slot_move[] = { | |||||
| {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""}, | |||||
| {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""}, | |||||
| {0, NULL, 0, NULL, NULL} | |||||
| }; | |||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Move Palette color"; | ot->name = "Smooth Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_move"; | ot->idname = "GPENCIL_OT_vertex_group_smooth"; | ||||
| ot->description = "Move the active Grease Pencil palette color up/down in the list"; | ot->description = "Smooth weights to the active vertex group"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_palettecolor_move_exec; | ot->poll = gpencil_vertex_group_weight_poll; | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | ot->exec = gpencil_vertex_group_smooth_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", ""); | RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); | ||||
| RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); | |||||
| } | } | ||||
| /* ***************** Select all strokes using Palette color ************************ */ | /****************************** Join ***********************************/ | ||||
| static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd, palette, palcolor)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* read all strokes and select*/ | |||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | |||||
| /* only editable and visible layers are considered */ | |||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | |||||
| /* verify something to do */ | |||||
| for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| /* check if the color is editable */ | |||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | |||||
| continue; | |||||
| /* select */ | /* userdata for joined_gpencil_fix_animdata_cb() */ | ||||
| if (strcmp(palcolor->info, gps->colorname) == 0) { | typedef struct tJoinGPencil_AdtFixData { | ||||
| bGPDspoint *pt; | bGPdata *src_gpd; | ||||
| int i; | bGPdata *tar_gpd; | ||||
| GHash *names_map; | |||||
| } tJoinGPencil_AdtFixData; | |||||
| gps->flag |= GP_STROKE_SELECT; | /* Callback to pass to BKE_fcurves_main_cb() for RNA Paths attached to each F-Curve used in the AnimData */ | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) | ||||
| pt->flag |= GP_SPOINT_SELECT; | { | ||||
| tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data; | |||||
| ID *src_id = &afd->src_gpd->id; | |||||
| ID *dst_id = &afd->tar_gpd->id; | |||||
| GHashIterator gh_iter; | |||||
| /* Fix paths - If this is the target datablock, it will have some "dirty" paths */ | |||||
| if ((id == src_id) && fcu->rna_path && strstr(fcu->rna_path, "layers[")) { | |||||
| GHASH_ITER(gh_iter, afd->names_map) { | |||||
| const char *old_name = BLI_ghashIterator_getKey(&gh_iter); | |||||
| const char *new_name = BLI_ghashIterator_getValue(&gh_iter); | |||||
| /* only remap if changed; this still means there will be some waste if there aren't many drivers/keys */ | |||||
| if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { | |||||
| fcu->rna_path = BKE_animsys_fix_rna_path_rename(id, fcu->rna_path, "layers", | |||||
| old_name, new_name, 0, 0, false); | |||||
| /* we don't want to apply a second remapping on this F-Curve now, | |||||
| * so stop trying to fix names names | |||||
| */ | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Fix driver targets */ | |||||
| if (fcu->driver) { | |||||
| /* Fix driver references to invalid ID's */ | |||||
| for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { | |||||
| /* only change the used targets, since the others will need fixing manually anyway */ | |||||
| DRIVER_TARGETS_USED_LOOPER(dvar) | |||||
| { | |||||
| /* change the ID's used... */ | |||||
| if (dtar->id == src_id) { | |||||
| dtar->id = dst_id; | |||||
| /* also check on the subtarget... | |||||
| * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own | |||||
| * little twists so that we know that it isn't going to clobber the wrong data | |||||
| */ | |||||
| if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { | |||||
| GHASH_ITER(gh_iter, afd->names_map) { | |||||
| const char *old_name = BLI_ghashIterator_getKey(&gh_iter); | |||||
| const char *new_name = BLI_ghashIterator_getValue(&gh_iter); | |||||
| /* only remap if changed */ | |||||
| if (!STREQ(old_name, new_name)) { | |||||
| if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { | |||||
| /* Fix up path */ | |||||
| dtar->rna_path = BKE_animsys_fix_rna_path_rename(id, dtar->rna_path, "layers", | |||||
| old_name, new_name, 0, 0, false); | |||||
| break; /* no need to try any more names for layer path */ | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| DRIVER_TARGETS_LOOPER_END | |||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) | /* join objects called from OBJECT_OT_join */ | ||||
| int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| /* identifiers */ | Main *bmain = CTX_data_main(C); | ||||
| ot->name = "Select Color"; | Scene *scene = CTX_data_scene(C); | ||||
| ot->idname = "GPENCIL_OT_palettecolor_select"; | Depsgraph *depsgraph = CTX_data_depsgraph(C); | ||||
| ot->description = "Select all Grease Pencil strokes using current color"; | Object *obact = CTX_data_active_object(C); | ||||
| bGPdata *gpd_dst = NULL; | |||||
| bool ok = false; | |||||
| /* callbacks */ | /* Ensure we're in right mode and that the active object is correct */ | ||||
| ot->exec = gp_palettecolor_select_exec; | if (!obact || obact->type != OB_GPENCIL) | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | return OPERATOR_CANCELLED; | ||||
| /* flags */ | bGPdata *gpd = (bGPdata *)obact->data; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { | ||||
| } | return OPERATOR_CANCELLED; | ||||
| } | |||||
| /* ***************** Copy Palette color ************************ */ | /* Ensure all rotations are applied before */ | ||||
| // XXX: Why don't we apply them here instead of warning? | |||||
| CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) | |||||
| { | |||||
| if (base->object->type == OB_GPENCIL) { | |||||
| if ((base->object->rot[0] != 0) || | |||||
| (base->object->rot[1] != 0) || | |||||
| (base->object->rot[2] != 0)) | |||||
| { | |||||
| BKE_report(op->reports, RPT_ERROR, "Apply all rotations before join objects"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) | CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | if (base->object == obact) { | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | ok = true; | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | break; | ||||
| bGPDpalettecolor *newcolor; | } | ||||
| } | |||||
| CTX_DATA_END; | |||||
| /* sanity checks */ | /* that way the active object is always selected */ | ||||
| if (ELEM(NULL, gpd, palette, palcolor)) | if (ok == false) { | ||||
| BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil"); | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | |||||
| gpd_dst = obact->data; | |||||
| /* loop and join all data */ | |||||
| CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) | |||||
| { | |||||
| if ((base->object->type == OB_GPENCIL) && (base->object != obact)) { | |||||
| /* we assume that each datablock is not already used in active object */ | |||||
| if (obact->data != base->object->data) { | |||||
| bGPdata *gpd_src = base->object->data; | |||||
| /* Apply all GP modifiers before */ | |||||
| for (ModifierData *md = base->object->modifiers.first; md; md = md->next) { | |||||
| const ModifierTypeInfo *mti = modifierType_getInfo(md->type); | |||||
| if (mti->bakeModifierGP) { | |||||
| mti->bakeModifierGP(C, depsgraph, md, base->object); | |||||
| } | |||||
| } | |||||
| /* create a new color and duplicate data */ | /* copy vertex groups to the base one's */ | ||||
| newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true); | int old_idx = 0; | ||||
| copy_v4_v4(newcolor->color, palcolor->color); | for (bDeformGroup *dg = base->object->defbase.first; dg; dg = dg->next) { | ||||
| copy_v4_v4(newcolor->fill, palcolor->fill); | bDeformGroup *vgroup = MEM_dupallocN(dg); | ||||
| newcolor->flag = palcolor->flag; | int idx = BLI_listbase_count(&obact->defbase); | ||||
| defgroup_unique_name(vgroup, obact); | |||||
| BLI_addtail(&obact->defbase, vgroup); | |||||
| /* update vertex groups in strokes in original data */ | |||||
| for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) { | |||||
| for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) { | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | |||||
| bGPDspoint *pt; | |||||
| int i; | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| if ((pt->weights) && (pt->weights->index == old_idx)) { | |||||
| pt->weights->index = idx; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| old_idx++; | |||||
| } | |||||
| if (obact->defbase.first && obact->actdef == 0) | |||||
| obact->actdef = 1; | |||||
| /* add missing paletteslots */ | |||||
| bGPDpaletteref *palslot; | |||||
| for (palslot = gpd_src->palette_slots.first; palslot; palslot = palslot->next) { | |||||
| if (BKE_gpencil_paletteslot_find(gpd_dst, palslot->palette) == NULL) { | |||||
| BKE_gpencil_paletteslot_add(gpd_dst, palslot->palette); | |||||
| } | |||||
| } | |||||
| /* notifiers */ | /* duplicate bGPDlayers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | tJoinGPencil_AdtFixData afd = {0}; | ||||
| afd.src_gpd = gpd_src; | |||||
| afd.tar_gpd = gpd_dst; | |||||
| afd.names_map = BLI_ghash_str_new("joined_gp_layers_map"); | |||||
| float imat[3][3], bmat[3][3]; | |||||
| float offset_global[3]; | |||||
| float offset_local[3]; | |||||
| sub_v3_v3v3(offset_global, obact->loc, base->object->obmat[3]); | |||||
| copy_m3_m4(bmat, obact->obmat); | |||||
| invert_m3_m3(imat, bmat); | |||||
| mul_m3_v3(imat, offset_global); | |||||
| mul_v3_m3v3(offset_local, imat, offset_global); | |||||
| for (bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { | |||||
| bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); | |||||
| float diff_mat[4][4]; | |||||
| float inverse_diff_mat[4][4]; | |||||
| /* recalculate all stroke points */ | |||||
| ED_gpencil_parent_location(base->object, gpd_src, gpl_src, diff_mat); | |||||
| invert_m4_m4(inverse_diff_mat, diff_mat); | |||||
| for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | |||||
| bGPDspoint *pt; | |||||
| int i; | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| float mpt[3]; | |||||
| mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); | |||||
| sub_v3_v3(mpt, offset_local); | |||||
| mul_v3_m4v3(&pt->x, diff_mat, mpt); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* be sure name is unique in new object */ | |||||
| BLI_uniquename(&gpd_dst->layers, gpl_new, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl_new->info)); | |||||
| BLI_ghash_insert(afd.names_map, BLI_strdup(gpl_src->info), gpl_new->info); | |||||
| /* add to destination datablock */ | |||||
| BLI_addtail(&gpd_dst->layers, gpl_new); | |||||
| } | |||||
| /* Fix all the animation data */ | |||||
| BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd); | |||||
| BLI_ghash_free(afd.names_map, MEM_freeN, NULL); | |||||
| /* Only copy over animdata now, after all the remapping has been done, | |||||
| * so that we don't have to worry about ambiguities re which datablock | |||||
| * a layer came from! | |||||
| */ | |||||
| if (base->object->adt) { | |||||
| if (obact->adt == NULL) { | |||||
| /* no animdata, so just use a copy of the whole thing */ | |||||
| obact->adt = BKE_animdata_copy(bmain, base->object->adt, false); | |||||
| } | |||||
| else { | |||||
| /* merge in data - we'll fix the drivers manually */ | |||||
| BKE_animdata_merge_copy(&obact->id, &base->object->id, ADT_MERGECOPY_KEEP_DST, false); | |||||
| } | |||||
| } | |||||
| if (gpd_src->adt) { | |||||
| if (gpd_dst->adt == NULL) { | |||||
| /* no animdata, so just use a copy of the whole thing */ | |||||
| gpd_dst->adt = BKE_animdata_copy(bmain, gpd_src->adt, false); | |||||
| } | |||||
| else { | |||||
| /* merge in data - we'll fix the drivers manually */ | |||||
| BKE_animdata_merge_copy(&gpd_dst->id, &gpd_src->id, ADT_MERGECOPY_KEEP_DST, false); | |||||
| } | |||||
| } | |||||
| } | |||||
| return OPERATOR_FINISHED; | /* Free the old object */ | ||||
| } | ED_object_base_free_and_unlink(bmain, scene, base->object); | ||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot) | DEG_relations_tag_update(bmain); /* because we removed object(s) */ | ||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Copy Color"; | |||||
| ot->idname = "GPENCIL_OT_palettecolor_copy"; | |||||
| ot->description = "Copy current Grease Pencil palette color"; | |||||
| /* callbacks */ | WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); | ||||
| ot->exec = gp_palettecolor_copy_exec; | |||||
| ot->poll = gp_active_palettecolor_poll; | |||||
| /* flags */ | return OPERATOR_FINISHED; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | } | ||||
| Context not available. | |||||