Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_edit.c
| Show First 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "DNA_object_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 "DNA_gpencil_types.h" | ||||
| #include "DNA_workspace_types.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_paint.h" | |||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "BKE_workspace.h" | |||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "UI_view2d.h" | #include "UI_view2d.h" | ||||
| #include "ED_gpencil.h" | #include "ED_gpencil.h" | ||||
| #include "ED_object.h" | #include "ED_object.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "ED_space_api.h" | #include "ED_space_api.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "gpencil_intern.h" | #include "gpencil_intern.h" | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Stroke Edit Mode Management */ | /* verify if is using the right brush */ | ||||
| static void gpencil_verify_brush_type(bContext *C, int newmode) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| GP_BrushEdit_Settings *gset = &ts->gp_sculpt; | |||||
| switch (newmode) { | |||||
| case OB_MODE_GPENCIL_SCULPT: | |||||
| gset->flag &= ~GP_BRUSHEDIT_FLAG_WEIGHT_MODE; | |||||
| if ((gset->brushtype < 0) || (gset->brushtype >= GP_EDITBRUSH_TYPE_WEIGHT)) { | |||||
| gset->brushtype = 0; | |||||
| } | |||||
| break; | |||||
| case OB_MODE_GPENCIL_WEIGHT: | |||||
| gset->flag |= GP_BRUSHEDIT_FLAG_WEIGHT_MODE; | |||||
| if ((gset->weighttype < GP_EDITBRUSH_TYPE_WEIGHT) || (gset->weighttype >= TOT_GP_EDITBRUSH_TYPES)) { | |||||
| gset->weighttype = GP_EDITBRUSH_TYPE_WEIGHT; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* setup modes and cursor */ | |||||
| static void gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) | |||||
| { | |||||
| if (!gpd) { | |||||
| return; | |||||
| } | |||||
| switch (newmode) { | |||||
| case OB_MODE_GPENCIL_EDIT: | |||||
| gpd->flag |= GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| ED_gpencil_toggle_brush_cursor(C, false); | |||||
| break; | |||||
| case OB_MODE_GPENCIL_PAINT: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag |= GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| ED_gpencil_toggle_brush_cursor(C, true); | |||||
| break; | |||||
| case OB_MODE_GPENCIL_SCULPT: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag |= GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| gpencil_verify_brush_type(C, OB_MODE_GPENCIL_SCULPT); | |||||
| ED_gpencil_toggle_brush_cursor(C, true); | |||||
| break; | |||||
| case OB_MODE_GPENCIL_WEIGHT: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag |= GP_DATA_STROKE_WEIGHTMODE; | |||||
| gpencil_verify_brush_type(C, OB_MODE_GPENCIL_WEIGHT); | |||||
| ED_gpencil_toggle_brush_cursor(C, true); | |||||
| break; | |||||
| default: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| ED_gpencil_toggle_brush_cursor(C, false); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* Stroke Edit Mode Management */ | |||||
| static int gpencil_editmode_toggle_poll(bContext *C) | static int gpencil_editmode_toggle_poll(bContext *C) | ||||
| { | { | ||||
| /* if using gpencil object, use this gpd */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| return ob->gpd != NULL; | |||||
| } | |||||
| return ED_gpencil_data_get_active(C) != NULL; | return ED_gpencil_data_get_active(C) != NULL; | ||||
| } | } | ||||
| static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op)) | static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| const int back = RNA_int_get(op->ptr, "back"); | |||||
| WorkSpace *workspace = CTX_wm_workspace(C); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bool is_object = false; | |||||
| int mode; | |||||
| /* if using a gpencil object, use this datablock */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| gpd = ob->gpd; | |||||
| is_object = true; | |||||
| } | |||||
| if (gpd == NULL) | if (gpd == NULL) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* Just toggle editmode flag... */ | /* Just toggle editmode flag... */ | ||||
| gpd->flag ^= GP_DATA_STROKE_EDITMODE; | gpd->flag ^= GP_DATA_STROKE_EDITMODE; | ||||
| /* recalculate parent matrix */ | /* recalculate parent matrix */ | ||||
| if (gpd->flag & GP_DATA_STROKE_EDITMODE) { | if (gpd->flag & GP_DATA_STROKE_EDITMODE) { | ||||
| ED_gpencil_reset_layers_parent(gpd); | ED_gpencil_reset_layers_parent(ob, gpd); | ||||
| } | |||||
| /* set mode */ | |||||
| if (gpd->flag & GP_DATA_STROKE_EDITMODE) { | |||||
| mode = OB_MODE_GPENCIL_EDIT; | |||||
| } | |||||
| else { | |||||
| mode = OB_MODE_OBJECT; | |||||
| } | } | ||||
| if (is_object) { | |||||
| /* try to back previous mode */ | |||||
| if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) { | |||||
| mode = ob->restore_mode; | |||||
| } | |||||
| ob->restore_mode = ob->mode; | |||||
| ob->mode = mode; | |||||
| } | |||||
| /* set workspace mode */ | |||||
| BKE_workspace_object_mode_set(workspace, mode); | |||||
| /* setup other modes */ | |||||
| gpencil_setup_modes(C, gpd, mode); | |||||
| /* set cache as dirty */ | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); | ||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) | void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Strokes Edit Mode Toggle"; | ot->name = "Strokes Edit Mode Toggle"; | ||||
| ot->idname = "GPENCIL_OT_editmode_toggle"; | ot->idname = "GPENCIL_OT_editmode_toggle"; | ||||
| ot->description = "Enter/Exit edit mode for Grease Pencil strokes"; | ot->description = "Enter/Exit edit mode for Grease Pencil strokes"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gpencil_editmode_toggle_exec; | ot->exec = gpencil_editmode_toggle_exec; | ||||
| ot->poll = gpencil_editmode_toggle_poll; | ot->poll = gpencil_editmode_toggle_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ||||
| /* properties */ | |||||
| RNA_def_int(ot->srna, "back", 0, 0, 1, "back", "1 to back previous mode", 0, 1); | |||||
| } | |||||
| /* Stroke Paint Mode Management */ | |||||
| static int gpencil_paintmode_toggle_poll(bContext *C) | |||||
| { | |||||
| /* if using gpencil object, use this gpd */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| return ob->gpd != NULL; | |||||
| } | |||||
| return ED_gpencil_data_get_active(C) != NULL; | |||||
| } | |||||
| static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| const int back = RNA_int_get(op->ptr, "back"); | |||||
| WorkSpace *workspace = CTX_wm_workspace(C); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bool is_object = false; | |||||
| int mode; | |||||
| /* if using a gpencil object, use this datablock */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| gpd = ob->gpd; | |||||
| is_object = true; | |||||
| } | |||||
| if (gpd == NULL) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* Just toggle paintmode flag... */ | |||||
| gpd->flag ^= GP_DATA_STROKE_PAINTMODE; | |||||
| /* set mode */ | |||||
| if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { | |||||
| mode = OB_MODE_GPENCIL_PAINT; | |||||
| } | |||||
| else { | |||||
| mode = OB_MODE_OBJECT; | |||||
| } | |||||
| if (is_object) { | |||||
| /* try to back previous mode */ | |||||
| if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) { | |||||
| mode = ob->restore_mode; | |||||
| } | |||||
| ob->restore_mode = ob->mode; | |||||
| ob->mode = mode; | |||||
| } | |||||
| /* set workspace mode */ | |||||
| BKE_workspace_object_mode_set(workspace, mode); | |||||
| /* setup other modes */ | |||||
| gpencil_setup_modes(C, gpd, mode); | |||||
| /* set cache as dirty */ | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); | |||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Strokes Paint Mode Toggle"; | |||||
| ot->idname = "GPENCIL_OT_paintmode_toggle"; | |||||
| ot->description = "Enter/Exit paint mode for Grease Pencil strokes"; | |||||
| /* callbacks */ | |||||
| ot->exec = gpencil_paintmode_toggle_exec; | |||||
| ot->poll = gpencil_paintmode_toggle_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | |||||
| /* properties */ | |||||
| RNA_def_int(ot->srna, "back", 0, 0, 1, "back", "1 to back previous mode", 0, 1); | |||||
| } | |||||
| /* Stroke Sculpt Mode Management */ | |||||
| static int gpencil_sculptmode_toggle_poll(bContext *C) | |||||
| { | |||||
| /* if using gpencil object, use this gpd */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| return ob->gpd != NULL; | |||||
| } | |||||
| return ED_gpencil_data_get_active(C) != NULL; | |||||
| } | |||||
| static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| const int back = RNA_int_get(op->ptr, "back"); | |||||
| WorkSpace *workspace = CTX_wm_workspace(C); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bool is_object = false; | |||||
| int mode; | |||||
| /* if using a gpencil object, use this datablock */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| gpd = ob->gpd; | |||||
| is_object = true; | |||||
| } | |||||
| if (gpd == NULL) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* Just toggle sculptmode flag... */ | |||||
| gpd->flag ^= GP_DATA_STROKE_SCULPTMODE; | |||||
| /* set mode */ | |||||
| if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) { | |||||
| mode = OB_MODE_GPENCIL_SCULPT; | |||||
| } | |||||
| else { | |||||
| mode = OB_MODE_OBJECT; | |||||
| } | |||||
| if (is_object) { | |||||
| /* try to back previous mode */ | |||||
| if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) { | |||||
| mode = ob->restore_mode; | |||||
| } | |||||
| ob->restore_mode = ob->mode; | |||||
| ob->mode = mode; | |||||
| } | |||||
| /* set workspace mode */ | |||||
| BKE_workspace_object_mode_set(workspace, mode); | |||||
| /* setup other modes */ | |||||
| gpencil_setup_modes(C, gpd, mode); | |||||
| /* set cache as dirty */ | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); | |||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Strokes Sculpt Mode Toggle"; | |||||
| ot->idname = "GPENCIL_OT_sculptmode_toggle"; | |||||
| ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes"; | |||||
| /* callbacks */ | |||||
| ot->exec = gpencil_sculptmode_toggle_exec; | |||||
| ot->poll = gpencil_sculptmode_toggle_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | |||||
| /* properties */ | |||||
| RNA_def_int(ot->srna, "back", 0, 0, 1, "back", "1 to back previous mode", 0, 1); | |||||
| } | |||||
| /* Stroke Weight Paint Mode Management */ | |||||
| static int gpencil_weightmode_toggle_poll(bContext *C) | |||||
| { | |||||
| /* if using gpencil object, use this gpd */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| return ob->gpd != NULL; | |||||
| } | |||||
| return ED_gpencil_data_get_active(C) != NULL; | |||||
| } | |||||
| static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| const int back = RNA_int_get(op->ptr, "back"); | |||||
| WorkSpace *workspace = CTX_wm_workspace(C); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bool is_object = false; | |||||
| int mode; | |||||
| /* if using a gpencil object, use this datablock */ | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if ((ob) && (ob->type == OB_GPENCIL)) { | |||||
| gpd = ob->gpd; | |||||
| is_object = true; | |||||
| } | |||||
| if (gpd == NULL) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* Just toggle weightmode flag... */ | |||||
| gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE; | |||||
| /* set mode */ | |||||
| if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) { | |||||
| mode = OB_MODE_GPENCIL_WEIGHT; | |||||
| } | |||||
| else { | |||||
| mode = OB_MODE_OBJECT; | |||||
| } | |||||
| if (is_object) { | |||||
| /* try to back previous mode */ | |||||
| if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) { | |||||
| mode = ob->restore_mode; | |||||
| } | |||||
| ob->restore_mode = ob->mode; | |||||
| ob->mode = mode; | |||||
| } | |||||
| /* set workspace mode */ | |||||
| BKE_workspace_object_mode_set(workspace, mode); | |||||
| /* setup other modes */ | |||||
| gpencil_setup_modes(C, gpd, mode); | |||||
| /* set cache as dirty */ | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); | |||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Strokes Weight Mode Toggle"; | |||||
| ot->idname = "GPENCIL_OT_weightmode_toggle"; | |||||
| ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes"; | |||||
| /* callbacks */ | |||||
| ot->exec = gpencil_weightmode_toggle_exec; | |||||
| ot->poll = gpencil_weightmode_toggle_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | |||||
| /* properties */ | |||||
| RNA_def_int(ot->srna, "back", 0, 0, 1, "back", "1 to back previous mode", 0, 1); | |||||
| } | } | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Stroke Editing Operators */ | /* Stroke Editing Operators */ | ||||
| /* poll callback for all stroke editing operators */ | /* poll callback for all stroke editing operators */ | ||||
| static int gp_stroke_edit_poll(bContext *C) | static int gp_stroke_edit_poll(bContext *C) | ||||
| { | { | ||||
| Show All 34 Lines | void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gpencil_hideselect_toggle_exec; | ot->exec = gpencil_hideselect_toggle_exec; | ||||
| ot->poll = gp_stroke_edit_poll; | ot->poll = gp_stroke_edit_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ||||
| } | } | ||||
| /* toggle multi edit strokes support */ | |||||
| static int gpencil_multiedit_toggle_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| const int lines = RNA_int_get(op->ptr, "lines"); | |||||
| if (gpd == NULL) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* Just toggle value */ | |||||
| if (lines == 0) { | |||||
| gpd->flag ^= GP_DATA_STROKE_MULTIEDIT; | |||||
| } | |||||
| else { | |||||
| gpd->flag ^= GP_DATA_STROKE_MULTIEDIT_LINES; | |||||
| } | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); | |||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_multiedit_toggle(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "MultiEdit Toggle"; | |||||
| ot->idname = "GPENCIL_OT_multiedit_toggle"; | |||||
| ot->description = "Enable/Disable multiedit strokes support"; | |||||
| /* callbacks */ | |||||
| ot->exec = gpencil_multiedit_toggle_exec; | |||||
| ot->poll = gp_stroke_edit_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | |||||
| /* properties */ | |||||
| RNA_def_int(ot->srna, "lines", 0, 0, 1, "lines", "1 to toggle display lines only", 0, 1); | |||||
| } | |||||
| /* ************** Duplicate Selected Strokes **************** */ | /* ************** Duplicate Selected Strokes **************** */ | ||||
| /* Make copies of selected point segments in a selected stroke */ | /* Make copies of selected point segments in a selected stroke */ | ||||
| static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername) | static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername) | ||||
| { | { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| Show All 39 Lines | else { | ||||
| gpsd->triangles = NULL; | gpsd->triangles = NULL; | ||||
| gpsd->flag |= GP_STROKE_RECALC_CACHES; | gpsd->flag |= GP_STROKE_RECALC_CACHES; | ||||
| gpsd->tot_triangles = 0; | gpsd->tot_triangles = 0; | ||||
| /* now, make a new points array, and copy of the relevant parts */ | /* now, make a new points array, and copy of the relevant parts */ | ||||
| gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); | gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); | ||||
| memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); | memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); | ||||
| gpsd->totpoints = len; | gpsd->totpoints = len; | ||||
| /* Copy weights */ | |||||
| int e = start_idx; | |||||
| for (int j = 0; j < gpsd->totpoints; ++j) { | |||||
| bGPDspoint *pt_src = &gps->points[e]; | |||||
| bGPDspoint *pt_dst = &gpsd->points[j]; | |||||
| pt_dst->weights = MEM_dupallocN(pt_src->weights); | |||||
| ++e; | |||||
| } | |||||
| /* add to temp buffer */ | /* add to temp buffer */ | ||||
| gpsd->next = gpsd->prev = NULL; | gpsd->next = gpsd->prev = NULL; | ||||
| BLI_addtail(new_strokes, gpsd); | BLI_addtail(new_strokes, gpsd); | ||||
| /* cleanup + reset for next */ | /* cleanup + reset for next */ | ||||
| start_idx = -1; | start_idx = -1; | ||||
| } | } | ||||
| } | } | ||||
| Show All 33 Lines | for (gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| if (gps->totpoints == 1) { | if (gps->totpoints == 1) { | ||||
| /* Special Case: If there's just a single point in this stroke... */ | /* Special Case: If there's just a single point in this stroke... */ | ||||
| bGPDstroke *gpsd; | bGPDstroke *gpsd; | ||||
| /* make direct copies of the stroke and its points */ | /* make direct copies of the stroke and its points */ | ||||
| gpsd = MEM_dupallocN(gps); | gpsd = MEM_dupallocN(gps); | ||||
| BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); | BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); | ||||
| gpsd->points = MEM_dupallocN(gps->points); | gpsd->points = MEM_dupallocN(gps->points); | ||||
| BKE_gpencil_stroke_weights_duplicate(gps, gpsd); | |||||
| /* triangle information - will be calculated on next redraw */ | /* triangle information - will be calculated on next redraw */ | ||||
| gpsd->flag |= GP_STROKE_RECALC_CACHES; | gpsd->flag |= GP_STROKE_RECALC_CACHES; | ||||
| gpsd->triangles = NULL; | gpsd->triangles = NULL; | ||||
| /* add to temp buffer */ | /* add to temp buffer */ | ||||
| gpsd->next = gpsd->prev = NULL; | gpsd->next = gpsd->prev = NULL; | ||||
| BLI_addtail(&new_strokes, gpsd); | BLI_addtail(&new_strokes, gpsd); | ||||
| Show All 12 Lines | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| /* add all new strokes in temp buffer to the frame (preventing double-copies) */ | /* add all new strokes in temp buffer to the frame (preventing double-copies) */ | ||||
| BLI_movelisttolist(&gpf->strokes, &new_strokes); | BLI_movelisttolist(&gpf->strokes, &new_strokes); | ||||
| BLI_assert(new_strokes.first == NULL); | BLI_assert(new_strokes.first == NULL); | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| /* 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; | ||||
| } | } | ||||
| void GPENCIL_OT_duplicate(wmOperatorType *ot) | void GPENCIL_OT_duplicate(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (gp_strokes_copypastebuf_colors) { | ||||
| BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); | BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); | ||||
| gp_strokes_copypastebuf_colors = NULL; | gp_strokes_copypastebuf_colors = NULL; | ||||
| } | } | ||||
| /* Free the stroke buffer */ | /* Free the stroke buffer */ | ||||
| for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { | for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { | ||||
| gpsn = gps->next; | gpsn = gps->next; | ||||
| if (gps->points) MEM_freeN(gps->points); | if (gps->points) { | ||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| if (gps->triangles) MEM_freeN(gps->triangles); | if (gps->triangles) MEM_freeN(gps->triangles); | ||||
| BLI_freelinkN(&gp_strokes_copypastebuf, gps); | BLI_freelinkN(&gp_strokes_copypastebuf, gps); | ||||
| } | } | ||||
| gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; | gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; | ||||
| } | } | ||||
| /* Ensure that destination datablock has all the colours the pasted strokes need | /* Ensure that destination datablock has all the colours the pasted strokes need | ||||
| * Helper function for copy-pasting strokes | * Helper function for copy-pasting strokes | ||||
| */ | */ | ||||
| GHash *gp_copybuf_validate_colormap(bGPdata *gpd) | GHash *gp_copybuf_validate_colormap(bContext *C) | ||||
| { | { | ||||
| GHash *new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); | GHash *new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); | ||||
| GHashIterator gh_iter; | GHashIterator gh_iter; | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | |||||
| bGPDpaletteref *palslot; | |||||
| Palette *palette; | |||||
| /* If there's no active palette yet (i.e. new datablock), add one */ | /* If there's no active palette yet (i.e. new datablock), add one */ | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | palslot = BKE_gpencil_paletteslot_validate(CTX_data_main(C), gpd); | ||||
| if (palette == NULL) { | palette = palslot->palette; | ||||
| palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); | |||||
| } | |||||
| /* For each color, figure out what to map to... */ | /* For each color, figure out what to map to... */ | ||||
| GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { | GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { | ||||
| bGPDpalettecolor *palcolor; | PaletteColor *palcolor; | ||||
| char *name = BLI_ghashIterator_getKey(&gh_iter); | char *name = BLI_ghashIterator_getKey(&gh_iter); | ||||
| /* Look for existing color to map to */ | /* Look for existing color to map to */ | ||||
| /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ | /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ | ||||
| palcolor = BKE_gpencil_palettecolor_getbyname(palette, name); | palcolor = BKE_palette_color_getbyname(palette, name); | ||||
| if (palcolor == NULL) { | if (palcolor == NULL) { | ||||
| /* Doesn't Exist - Create new matching color for this palette */ | /* Doesn't Exist - Create new matching color for this palette */ | ||||
| /* XXX: This still doesn't fix the pasting across file boundaries problem... */ | /* XXX: This still doesn't fix the pasting across file boundaries problem... */ | ||||
| bGPDpalettecolor *src_color = BLI_ghashIterator_getValue(&gh_iter); | PaletteColor *src_color = BLI_ghashIterator_getValue(&gh_iter); | ||||
| palcolor = MEM_dupallocN(src_color); | palcolor = MEM_dupallocN(src_color); | ||||
| BLI_addtail(&palette->colors, palcolor); | BLI_addtail(&palette->colors, palcolor); | ||||
| BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); | BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(PaletteColor, info), sizeof(palcolor->info)); | ||||
| } | } | ||||
| /* Store this mapping (for use later when pasting) */ | /* Store this mapping (for use later when pasting) */ | ||||
| BLI_ghash_insert(new_colors, name, palcolor); | BLI_ghash_insert(new_colors, name, palcolor); | ||||
| } | } | ||||
| return new_colors; | return new_colors; | ||||
| } | } | ||||
| Show All 35 Lines | for (gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| if (gps->totpoints == 1) { | if (gps->totpoints == 1) { | ||||
| /* Special Case: If there's just a single point in this stroke... */ | /* Special Case: If there's just a single point in this stroke... */ | ||||
| bGPDstroke *gpsd; | bGPDstroke *gpsd; | ||||
| /* make direct copies of the stroke and its points */ | /* make direct copies of the stroke and its points */ | ||||
| gpsd = MEM_dupallocN(gps); | gpsd = MEM_dupallocN(gps); | ||||
| BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ | BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ | ||||
| gpsd->points = MEM_dupallocN(gps->points); | gpsd->points = MEM_dupallocN(gps->points); | ||||
| BKE_gpencil_stroke_weights_duplicate(gps, gpsd); | |||||
| /* triangles cache - will be recalculated on next redraw */ | /* triangles cache - will be recalculated on next redraw */ | ||||
| gpsd->flag |= GP_STROKE_RECALC_CACHES; | gpsd->flag |= GP_STROKE_RECALC_CACHES; | ||||
| gpsd->tot_triangles = 0; | gpsd->tot_triangles = 0; | ||||
| gpsd->triangles = NULL; | gpsd->triangles = NULL; | ||||
| /* add to temp buffer */ | /* add to temp buffer */ | ||||
| gpsd->next = gpsd->prev = NULL; | gpsd->next = gpsd->prev = NULL; | ||||
| BLI_addtail(&gp_strokes_copypastebuf, gpsd); | BLI_addtail(&gp_strokes_copypastebuf, gpsd); | ||||
| Show All 9 Lines | static int gp_strokes_copy_exec(bContext *C, wmOperator *op) | ||||
| /* Build up hash of colors used in these strokes, making copies of these to protect against dangling pointers */ | /* Build up hash of colors used in these strokes, making copies of these to protect against dangling pointers */ | ||||
| if (gp_strokes_copypastebuf.first) { | if (gp_strokes_copypastebuf.first) { | ||||
| gp_strokes_copypastebuf_colors = BLI_ghash_str_new("GPencil CopyBuf Colors"); | gp_strokes_copypastebuf_colors = BLI_ghash_str_new("GPencil CopyBuf Colors"); | ||||
| for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { | ||||
| if (ED_gpencil_stroke_can_use(C, gps)) { | if (ED_gpencil_stroke_can_use(C, gps)) { | ||||
| if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, gps->colorname) == false) { | if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, gps->colorname) == false) { | ||||
| bGPDpalettecolor *color = MEM_dupallocN(gps->palcolor); | PaletteColor *color = MEM_dupallocN(gps->palcolor); | ||||
| BLI_ghash_insert(gp_strokes_copypastebuf_colors, gps->colorname, color); | BLI_ghash_insert(gp_strokes_copypastebuf_colors, gps->colorname, color); | ||||
| gps->palcolor = color; | gps->palcolor = color; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| pt->flag &= ~GP_SPOINT_SELECT; | pt->flag &= ~GP_SPOINT_SELECT; | ||||
| } | } | ||||
| gps->flag &= ~GP_STROKE_SELECT; | gps->flag &= ~GP_STROKE_SELECT; | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| /* Ensure that all the necessary colors exist */ | /* Ensure that all the necessary colors exist */ | ||||
| new_colors = gp_copybuf_validate_colormap(gpd); | new_colors = gp_copybuf_validate_colormap(C); | ||||
| /* Copy over the strokes from the buffer (and adjust the colors) */ | /* Copy over the strokes from the buffer (and adjust the colors) */ | ||||
| for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { | ||||
| if (ED_gpencil_stroke_can_use(C, gps)) { | if (ED_gpencil_stroke_can_use(C, gps)) { | ||||
| /* Need to verify if layer exists */ | /* Need to verify if layer exists */ | ||||
| if (type != GP_COPY_MERGE) { | if (type != GP_COPY_MERGE) { | ||||
| gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); | gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); | ||||
| if (gpl == NULL) { | if (gpl == NULL) { | ||||
| Show All 9 Lines | if (ED_gpencil_stroke_can_use(C, gps)) { | ||||
| */ | */ | ||||
| gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); | gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); | ||||
| if (gpf) { | if (gpf) { | ||||
| /* Create new stroke */ | /* Create new stroke */ | ||||
| bGPDstroke *new_stroke = MEM_dupallocN(gps); | bGPDstroke *new_stroke = MEM_dupallocN(gps); | ||||
| new_stroke->tmp_layerinfo[0] = '\0'; | new_stroke->tmp_layerinfo[0] = '\0'; | ||||
| new_stroke->points = MEM_dupallocN(gps->points); | new_stroke->points = MEM_dupallocN(gps->points); | ||||
| BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); | |||||
| new_stroke->flag |= GP_STROKE_RECALC_CACHES; | new_stroke->flag |= GP_STROKE_RECALC_CACHES; | ||||
| new_stroke->triangles = NULL; | new_stroke->triangles = NULL; | ||||
| new_stroke->next = new_stroke->prev = NULL; | new_stroke->next = new_stroke->prev = NULL; | ||||
| BLI_addtail(&gpf->strokes, new_stroke); | BLI_addtail(&gpf->strokes, new_stroke); | ||||
| /* Fix color references */ | /* Fix color references */ | ||||
| BLI_assert(new_stroke->colorname[0] != '\0'); | BLI_assert(new_stroke->colorname[0] != '\0'); | ||||
| new_stroke->palcolor = BLI_ghash_lookup(new_colors, new_stroke->colorname); | new_stroke->palcolor = BLI_ghash_lookup(new_colors, new_stroke->colorname); | ||||
| BLI_assert(new_stroke->palcolor != NULL); | BLI_assert(new_stroke->palcolor != NULL); | ||||
| BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); | BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); | ||||
| /*new_stroke->flag |= GP_STROKE_RECALC_COLOR; */ | /*new_stroke->flag |= GP_STROKE_RECALC_COLOR; */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* free temp data */ | /* free temp data */ | ||||
| BLI_ghash_free(new_colors, NULL, NULL); | BLI_ghash_free(new_colors, NULL, NULL); | ||||
| /* 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; | ||||
| } | } | ||||
| void GPENCIL_OT_paste(wmOperatorType *ot) | void GPENCIL_OT_paste(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem copy_type[] = { | static const EnumPropertyItem copy_type[] = { | ||||
| ▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | if (strokes.first) { | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true); | bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true); | ||||
| BLI_movelisttolist(&gpf->strokes, &strokes); | BLI_movelisttolist(&gpf->strokes, &strokes); | ||||
| BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); | BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); | ||||
| } | } | ||||
| /* 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; | ||||
| } | } | ||||
| void GPENCIL_OT_move_to_layer(wmOperatorType *ot) | void GPENCIL_OT_move_to_layer(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| } | } | ||||
| /* 2) Now add a new frame, with nothing in it */ | /* 2) Now add a new frame, with nothing in it */ | ||||
| gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); | gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); | ||||
| } | } | ||||
| CTX_DATA_END; | 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; | ||||
| } | } | ||||
| void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) | void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 39 Lines | if (ELEM(NULL, gpl, gpf)) { | ||||
| BKE_report(op->reports, RPT_ERROR, "No active frame to delete"); | BKE_report(op->reports, RPT_ERROR, "No active frame to delete"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* delete it... */ | /* delete it... */ | ||||
| BKE_gpencil_layer_delframe(gpl, gpf); | BKE_gpencil_layer_delframe(gpl, gpf); | ||||
| /* 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; | ||||
| } | } | ||||
| void GPENCIL_OT_active_frame_delete(wmOperatorType *ot) | void GPENCIL_OT_active_frame_delete(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 17 Lines | static int gp_actframe_delete_all_poll(bContext *C) | ||||
| /* 1) There must be grease pencil data | /* 1) There must be grease pencil data | ||||
| * 2) Hopefully some of the layers have stuff we can use | * 2) Hopefully some of the layers have stuff we can use | ||||
| */ | */ | ||||
| return (gpd && gpd->layers.first); | return (gpd && gpd->layers.first); | ||||
| } | } | ||||
| static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) | static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| bool success = false; | bool success = false; | ||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| { | { | ||||
| /* try to get the "active" frame - but only if it actually occurs on this frame */ | /* try to get the "active" frame - but only if it actually occurs on this frame */ | ||||
| bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); | bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); | ||||
| if (gpf == NULL) | if (gpf == NULL) | ||||
| continue; | continue; | ||||
| /* delete it... */ | /* delete it... */ | ||||
| BKE_gpencil_layer_delframe(gpl, gpf); | BKE_gpencil_layer_delframe(gpl, gpf); | ||||
| /* we successfully modified something */ | /* we successfully modified something */ | ||||
| success = true; | success = true; | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| /* updates */ | /* updates */ | ||||
| if (success) { | if (success) { | ||||
| 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; | ||||
| } | } | ||||
| else { | else { | ||||
| BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete"); | BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| Show All 17 Lines | typedef enum eGP_DeleteMode { | ||||
| /* delete selected stroke points */ | /* delete selected stroke points */ | ||||
| GP_DELETEOP_POINTS = 0, | GP_DELETEOP_POINTS = 0, | ||||
| /* delete selected strokes */ | /* delete selected strokes */ | ||||
| GP_DELETEOP_STROKES = 1, | GP_DELETEOP_STROKES = 1, | ||||
| /* delete active frame */ | /* delete active frame */ | ||||
| GP_DELETEOP_FRAME = 2, | GP_DELETEOP_FRAME = 2, | ||||
| } eGP_DeleteMode; | } eGP_DeleteMode; | ||||
| typedef enum eGP_DissolveMode { | |||||
| /* dissolve all selected points */ | |||||
| GP_DISSOLVE_POINTS = 0, | |||||
| /* dissolve between selected points */ | |||||
| GP_DISSOLVE_BETWEEN = 1, | |||||
| /* dissolve unselected points */ | |||||
| GP_DISSOLVE_UNSELECT = 2, | |||||
| } eGP_DissolveMode; | |||||
| /* ----------------------------------- */ | /* ----------------------------------- */ | ||||
| /* Delete selected strokes */ | /* Delete selected strokes */ | ||||
| static int gp_delete_selected_strokes(bContext *C) | static int gp_delete_selected_strokes(bContext *C) | ||||
| { | { | ||||
| bool changed = false; | bool changed = false; | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| { | { | ||||
| bGPDframe *gpf = gpl->actframe; | bGPDframe *gpf = gpl->actframe; | ||||
| bGPDstroke *gps, *gpsn; | bGPDstroke *gps, *gpsn; | ||||
| if (gpf == NULL) | if (gpf == NULL) | ||||
| continue; | continue; | ||||
| /* simply delete strokes which are selected */ | /* simply delete strokes which are selected */ | ||||
| for (gps = gpf->strokes.first; gps; gps = gpsn) { | for (gps = gpf->strokes.first; gps; gps = gpsn) { | ||||
| gpsn = gps->next; | gpsn = gps->next; | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* free stroke if selected */ | /* free stroke if selected */ | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| /* free stroke memory arrays, then stroke itself */ | /* free stroke memory arrays, then stroke itself */ | ||||
| if (gps->points) MEM_freeN(gps->points); | if (gps->points) { | ||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| if (gps->triangles) MEM_freeN(gps->triangles); | if (gps->triangles) MEM_freeN(gps->triangles); | ||||
| BLI_freelinkN(&gpf->strokes, gps); | BLI_freelinkN(&gpf->strokes, gps); | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| if (changed) { | if (changed) { | ||||
| 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; | ||||
| } | } | ||||
| else { | else { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| /* ----------------------------------- */ | /* ----------------------------------- */ | ||||
| /* Delete selected points but keep the stroke */ | /* Delete selected points but keep the stroke */ | ||||
| static int gp_dissolve_selected_points(bContext *C) | static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bool changed = false; | bool changed = false; | ||||
| int first, last; | |||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| { | { | ||||
| bGPDframe *gpf = gpl->actframe; | bGPDframe *gpf = gpl->actframe; | ||||
| bGPDstroke *gps, *gpsn; | bGPDstroke *gps, *gpsn; | ||||
| if (gpf == NULL) | if (gpf == NULL) | ||||
| continue; | continue; | ||||
| /* simply delete points from selected strokes | /* simply delete points from selected strokes | ||||
| * NOTE: we may still have to remove the stroke if it ends up having no points! | * NOTE: we may still have to remove the stroke if it ends up having no points! | ||||
| */ | */ | ||||
| for (gps = gpf->strokes.first; gps; gps = gpsn) { | for (gps = gpf->strokes.first; gps; gps = gpsn) { | ||||
| gpsn = gps->next; | gpsn = gps->next; | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | if (ED_gpencil_stroke_color_use(gpl, gps) == false) | ||||
| continue; | continue; | ||||
| /* the stroke must have at least one point selected for any operator */ | |||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| int tot = gps->totpoints; /* number of points in new buffer */ | int tot = gps->totpoints; /* number of points in new buffer */ | ||||
| /* First Pass: Count how many points are selected (i.e. how many to remove) */ | /* first pass: count points to remove */ | ||||
| switch (mode) { | |||||
| case GP_DISSOLVE_POINTS: | |||||
| /* Count how many points are selected (i.e. how many to remove) */ | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| if (pt->flag & GP_SPOINT_SELECT) { | if (pt->flag & GP_SPOINT_SELECT) { | ||||
| /* selected point - one of the points to remove */ | /* selected point - one of the points to remove */ | ||||
| tot--; | tot--; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| case GP_DISSOLVE_BETWEEN: | |||||
| /* need to find first and last point selected */ | |||||
| first = -1; | |||||
| last = 0; | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| if (pt->flag & GP_SPOINT_SELECT) { | |||||
| if (first < 0) { | |||||
| first = i; | |||||
| } | |||||
| last = i; | |||||
| } | |||||
| } | |||||
| /* count unselected points in the range */ | |||||
| for (i = first, pt = gps->points + first; i < last; i++, pt++) { | |||||
| if ((pt->flag & GP_SPOINT_SELECT) == 0) { | |||||
| tot--; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case GP_DISSOLVE_UNSELECT: | |||||
| /* count number of unselected points */ | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| if ((pt->flag & GP_SPOINT_SELECT) == 0) { | |||||
| tot--; | |||||
| } | |||||
| } | |||||
| break; | |||||
| default: | |||||
| return false; | |||||
| break; | |||||
| } | |||||
| /* if no points are left, we simply delete the entire stroke */ | /* if no points are left, we simply delete the entire stroke */ | ||||
| if (tot <= 0) { | if (tot <= 0) { | ||||
| /* remove the entire stroke */ | /* remove the entire stroke */ | ||||
| if (gps->points) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | MEM_freeN(gps->points); | ||||
| } | |||||
| if (gps->triangles) { | if (gps->triangles) { | ||||
| MEM_freeN(gps->triangles); | MEM_freeN(gps->triangles); | ||||
| } | } | ||||
| BLI_freelinkN(&gpf->strokes, gps); | BLI_freelinkN(&gpf->strokes, gps); | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| } | } | ||||
| else { | else { | ||||
| /* just copy all unselected into a smaller buffer */ | /* just copy all points to keep into a smaller buffer */ | ||||
| bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); | bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); | ||||
| bGPDspoint *npt = new_points; | bGPDspoint *npt = new_points; | ||||
| switch (mode) { | |||||
| case GP_DISSOLVE_POINTS: | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| if ((pt->flag & GP_SPOINT_SELECT) == 0) { | if ((pt->flag & GP_SPOINT_SELECT) == 0) { | ||||
| *npt = *pt; | *npt = *pt; | ||||
| npt->weights = MEM_dupallocN(pt->weights); | |||||
| npt++; | |||||
| } | |||||
| } | |||||
| break; | |||||
| case GP_DISSOLVE_BETWEEN: | |||||
| /* copy first segment */ | |||||
| for (i = 0, pt = gps->points; i < first; i++, pt++) { | |||||
| *npt = *pt; | |||||
| npt->weights = MEM_dupallocN(pt->weights); | |||||
| npt++; | |||||
| } | |||||
| /* copy segment (selected points) */ | |||||
| for (i = first, pt = gps->points + first; i < last; i++, pt++) { | |||||
| if (pt->flag & GP_SPOINT_SELECT) { | |||||
| *npt = *pt; | |||||
| npt->weights = MEM_dupallocN(pt->weights); | |||||
| npt++; | |||||
| } | |||||
| } | |||||
| /* copy last segment */ | |||||
| for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) { | |||||
| *npt = *pt; | |||||
| npt->weights = MEM_dupallocN(pt->weights); | |||||
| npt++; | |||||
| } | |||||
| break; | |||||
| case GP_DISSOLVE_UNSELECT: | |||||
| /* copy any selected point */ | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| if (pt->flag & GP_SPOINT_SELECT) { | |||||
| *npt = *pt; | |||||
| npt->weights = MEM_dupallocN(pt->weights); | |||||
| npt++; | npt++; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| } | |||||
| /* free the old buffer */ | /* free the old buffer */ | ||||
| if (gps->points) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | MEM_freeN(gps->points); | ||||
| } | |||||
| /* save the new buffer */ | /* save the new buffer */ | ||||
| gps->points = new_points; | gps->points = new_points; | ||||
| gps->totpoints = tot; | gps->totpoints = tot; | ||||
| /* triangles cache needs to be recalculated */ | /* triangles cache needs to be recalculated */ | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| gps->tot_triangles = 0; | gps->tot_triangles = 0; | ||||
| /* deselect the stroke, since none of its selected points will still be selected */ | /* deselect the stroke, since none of its selected points will still be selected */ | ||||
| gps->flag &= ~GP_STROKE_SELECT; | gps->flag &= ~GP_STROKE_SELECT; | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| pt->flag &= ~GP_SPOINT_SELECT; | |||||
| } | |||||
| } | } | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| if (changed) { | if (changed) { | ||||
| 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; | ||||
| } | } | ||||
| else { | else { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| Show All 23 Lines | |||||
| * 2) Each island gets converted to a new stroke | * 2) Each island gets converted to a new stroke | ||||
| */ | */ | ||||
| void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags) | void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags) | ||||
| { | { | ||||
| tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands"); | tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands"); | ||||
| bool in_island = false; | bool in_island = false; | ||||
| int num_islands = 0; | int num_islands = 0; | ||||
| bGPDspoint *pt; | |||||
| int i; | |||||
| /* First Pass: Identify start/end of islands */ | /* First Pass: Identify start/end of islands */ | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | bGPDspoint *pt = gps->points; | ||||
| for (int i = 0; i < gps->totpoints; i++, pt++) { | |||||
| if (pt->flag & tag_flags) { | if (pt->flag & tag_flags) { | ||||
| /* selected - stop accumulating to island */ | /* selected - stop accumulating to island */ | ||||
| in_island = false; | in_island = false; | ||||
| } | } | ||||
| else { | else { | ||||
| /* unselected - start of a new island? */ | /* unselected - start of a new island? */ | ||||
| int idx; | int idx; | ||||
| Show All 26 Lines | for (idx = 0; idx < num_islands; idx++) { | ||||
| /* initialize triangle memory - to be calculated on next redraw */ | /* initialize triangle memory - to be calculated on next redraw */ | ||||
| new_stroke->triangles = NULL; | new_stroke->triangles = NULL; | ||||
| new_stroke->flag |= GP_STROKE_RECALC_CACHES; | new_stroke->flag |= GP_STROKE_RECALC_CACHES; | ||||
| new_stroke->tot_triangles = 0; | new_stroke->tot_triangles = 0; | ||||
| /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ | /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ | ||||
| new_stroke->totpoints = island->end_idx - island->start_idx + 1; | new_stroke->totpoints = island->end_idx - island->start_idx + 1; | ||||
| new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); | new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); | ||||
| /* Copy over the relevant points */ | /* Copy over the relevant points */ | ||||
| memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints); | memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints); | ||||
| /* Copy weights */ | |||||
| int e = island->start_idx; | |||||
| for (int i = 0; i < new_stroke->totpoints; ++i) { | |||||
| bGPDspoint *pt_src = &gps->points[e]; | |||||
| bGPDspoint *pt_dst = &new_stroke->points[i]; | |||||
| pt_dst->weights = MEM_dupallocN(pt_src->weights); | |||||
| ++e; | |||||
| } | |||||
| /* Each island corresponds to a new stroke. We must adjust the | /* Each island corresponds to a new stroke. We must adjust the | ||||
| * timings of these new strokes: | * timings of these new strokes: | ||||
| * | * | ||||
| * Each point's timing data is a delta from stroke's inittime, so as we erase some points from | * Each point's timing data is a delta from stroke's inittime, so as we erase some points from | ||||
| * the start of the stroke, we have to offset this inittime and all remaining points' delta values. | * the start of the stroke, we have to offset this inittime and all remaining points' delta values. | ||||
| * This way we get a new stroke with exactly the same timing as if user had started drawing from | * This way we get a new stroke with exactly the same timing as if user had started drawing from | ||||
| * the first non-removed point... | * the first non-removed point... | ||||
| Show All 20 Lines | for (idx = 0; idx < num_islands; idx++) { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* free islands */ | /* free islands */ | ||||
| MEM_freeN(islands); | MEM_freeN(islands); | ||||
| /* Delete the old stroke */ | /* Delete the old stroke */ | ||||
| if (gps->points) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | MEM_freeN(gps->points); | ||||
| } | |||||
| if (gps->triangles) { | if (gps->triangles) { | ||||
| MEM_freeN(gps->triangles); | MEM_freeN(gps->triangles); | ||||
| } | } | ||||
| BLI_freelinkN(&gpf->strokes, gps); | BLI_freelinkN(&gpf->strokes, gps); | ||||
| } | } | ||||
| /* Split selected strokes into segments, splitting on selected points */ | /* Split selected strokes into segments, splitting on selected points */ | ||||
| static int gp_delete_selected_points(bContext *C) | static int gp_delete_selected_points(bContext *C) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bool changed = false; | bool changed = false; | ||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| { | { | ||||
| bGPDframe *gpf = gpl->actframe; | bGPDframe *gpf = gpl->actframe; | ||||
| bGPDstroke *gps, *gpsn; | bGPDstroke *gps, *gpsn; | ||||
| if (gpf == NULL) | if (gpf == NULL) | ||||
| Show All 12 Lines | for (gps = gpf->strokes.first; gps; gps = gpsn) { | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| /* deselect old stroke, since it will be used as template for the new strokes */ | /* deselect old stroke, since it will be used as template for the new strokes */ | ||||
| gps->flag &= ~GP_STROKE_SELECT; | gps->flag &= ~GP_STROKE_SELECT; | ||||
| /* delete unwanted points by splitting stroke into several smaller ones */ | /* delete unwanted points by splitting stroke into several smaller ones */ | ||||
| gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT); | gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT); | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| if (changed) { | if (changed) { | ||||
| 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; | ||||
| } | } | ||||
| else { | else { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | void GPENCIL_OT_delete(wmOperatorType *ot) | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ||||
| /* props */ | /* props */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data"); | ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data"); | ||||
| } | } | ||||
| static int gp_dissolve_exec(bContext *C, wmOperator *UNUSED(op)) | static int gp_dissolve_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| return gp_dissolve_selected_points(C); | eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type"); | ||||
| return gp_dissolve_selected_points(C, mode); | |||||
| } | } | ||||
| void GPENCIL_OT_dissolve(wmOperatorType *ot) | void GPENCIL_OT_dissolve(wmOperatorType *ot) | ||||
| { | { | ||||
| static EnumPropertyItem prop_gpencil_dissolve_types[] = { | |||||
| { GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points" }, | |||||
| { GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points" }, | |||||
| { GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points" }, | |||||
| { 0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Dissolve"; | ot->name = "Dissolve"; | ||||
| ot->idname = "GPENCIL_OT_dissolve"; | ot->idname = "GPENCIL_OT_dissolve"; | ||||
| ot->description = "Delete selected points without splitting strokes"; | ot->description = "Delete selected points without splitting strokes"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->invoke = WM_menu_invoke; | |||||
| ot->exec = gp_dissolve_exec; | ot->exec = gp_dissolve_exec; | ||||
| ot->poll = gp_stroke_edit_poll; | ot->poll = gp_stroke_edit_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; | ||||
| /* props */ | |||||
| ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0, "Type", "Method used for disolving Stroke points"); | |||||
| } | } | ||||
| /* ****************** Snapping - Strokes <-> Cursor ************************ */ | /* ****************** Snapping - Strokes <-> Cursor ************************ */ | ||||
| /* Poll callback for snap operators */ | /* Poll callback for snap operators */ | ||||
| /* NOTE: For now, we only allow these in the 3D view, as other editors do not | /* NOTE: For now, we only allow these in the 3D view, as other editors do not | ||||
| * define a cursor or gridstep which can be used | * define a cursor or gridstep which can be used | ||||
| */ | */ | ||||
| static int gp_snap_poll(bContext *C) | static int gp_snap_poll(bContext *C) | ||||
| { | { | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | bGPdata *gpd = CTX_data_gpencil_data(C); | ||||
| ScrArea *sa = CTX_wm_area(C); | ScrArea *sa = CTX_wm_area(C); | ||||
| return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D)); | return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D)); | ||||
| } | } | ||||
| /* --------------------------------- */ | /* --------------------------------- */ | ||||
| static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) | static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| RegionView3D *rv3d = CTX_wm_region_data(C); | RegionView3D *rv3d = CTX_wm_region_data(C); | ||||
| Object *obact = CTX_data_active_object(C); | |||||
| const float gridf = rv3d->gridview; | const float gridf = rv3d->gridview; | ||||
| 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) && (gpl->actframe != NULL)) { | ||||
| bGPDframe *gpf = gpl->actframe; | bGPDframe *gpf = gpl->actframe; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix object */ | ||||
| if (gpl->parent != NULL) { | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | |||||
| } | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | if (ED_gpencil_stroke_color_use(gpl, gps) == false) | ||||
| continue; | continue; | ||||
| // TODO: if entire stroke is selected, offset entire stroke by same amount? | // TODO: if entire stroke is selected, offset entire stroke by same amount? | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| /* only if point is selected */ | /* only if point is selected */ | ||||
| if (pt->flag & GP_SPOINT_SELECT) { | if (pt->flag & GP_SPOINT_SELECT) { | ||||
| if (gpl->parent == NULL) { | |||||
| pt->x = gridf * floorf(0.5f + pt->x / gridf); | |||||
| pt->y = gridf * floorf(0.5f + pt->y / gridf); | |||||
| pt->z = gridf * floorf(0.5f + pt->z / gridf); | |||||
| } | |||||
| else { | |||||
| /* apply parent transformations */ | /* apply parent transformations */ | ||||
| float fpt[3]; | float fpt[3]; | ||||
| mul_v3_m4v3(fpt, diff_mat, &pt->x); | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); | fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); | ||||
| fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); | fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); | ||||
| fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); | fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); | ||||
| /* return data */ | /* return data */ | ||||
| copy_v3_v3(&pt->x, fpt); | copy_v3_v3(&pt->x, fpt); | ||||
| gp_apply_parent_point(gpl, pt); | gp_apply_parent_point(obact, gpd, gpl, pt); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| 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; | ||||
| } | } | ||||
| void GPENCIL_OT_snap_to_grid(wmOperatorType *ot) | void GPENCIL_OT_snap_to_grid(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Snap Selection to Grid"; | ot->name = "Snap Selection to Grid"; | ||||
| Show All 11 Lines | |||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| static int gp_snap_to_cursor(bContext *C, wmOperator *op) | static int gp_snap_to_cursor(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| View3D *v3d = CTX_wm_view3d(C); | View3D *v3d = CTX_wm_view3d(C); | ||||
| Object *obact = CTX_data_active_object(C); \ | |||||
| const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); | const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); | ||||
| const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d); | const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d); | ||||
| 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) && (gpl->actframe != NULL)) { | ||||
| bGPDframe *gpf = gpl->actframe; | bGPDframe *gpf = gpl->actframe; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix */ | ||||
| if (gpl->parent != NULL) { | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | |||||
| } | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| Show All 16 Lines | if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | ||||
| add_v3_v3(&pt->x, offset); | add_v3_v3(&pt->x, offset); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* affect each selected point */ | /* affect each selected point */ | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| if (pt->flag & GP_SPOINT_SELECT) { | if (pt->flag & GP_SPOINT_SELECT) { | ||||
| copy_v3_v3(&pt->x, cursor_global); | copy_v3_v3(&pt->x, cursor_global); | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(obact, gpd, gpl, pt); | ||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| 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; | ||||
| } | } | ||||
| void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot) | void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Snap Selection to Cursor"; | ot->name = "Snap Selection to Cursor"; | ||||
| Show All 15 Lines | |||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) | static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| View3D *v3d = CTX_wm_view3d(C); | View3D *v3d = CTX_wm_view3d(C); | ||||
| Object *obact = CTX_data_active_object(C); \ | |||||
| float *cursor = ED_view3d_cursor3d_get(scene, v3d); | float *cursor = ED_view3d_cursor3d_get(scene, v3d); | ||||
| float centroid[3] = {0.0f}; | float centroid[3] = {0.0f}; | ||||
| float min[3], max[3]; | float min[3], max[3]; | ||||
| size_t count = 0; | size_t count = 0; | ||||
| INIT_MINMAX(min, max); | INIT_MINMAX(min, max); | ||||
| /* calculate midpoints from selected points */ | /* calculate midpoints from selected points */ | ||||
| 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) && (gpl->actframe != NULL)) { | ||||
| bGPDframe *gpf = gpl->actframe; | bGPDframe *gpf = gpl->actframe; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix */ | ||||
| if (gpl->parent != NULL) { | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | |||||
| } | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | if (ED_gpencil_stroke_color_use(gpl, gps) == false) | ||||
| continue; | continue; | ||||
| /* only continue if this stroke is selected (editable doesn't guarantee this)... */ | /* only continue if this stroke is selected (editable doesn't guarantee this)... */ | ||||
| if ((gps->flag & GP_STROKE_SELECT) == 0) | if ((gps->flag & GP_STROKE_SELECT) == 0) | ||||
| continue; | continue; | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| if (pt->flag & GP_SPOINT_SELECT) { | if (pt->flag & GP_SPOINT_SELECT) { | ||||
| if (gpl->parent == NULL) { | |||||
| add_v3_v3(centroid, &pt->x); | |||||
| minmax_v3v3_v3(min, max, &pt->x); | |||||
| } | |||||
| else { | |||||
| /* apply parent transformations */ | /* apply parent transformations */ | ||||
| float fpt[3]; | float fpt[3]; | ||||
| mul_v3_m4v3(fpt, diff_mat, &pt->x); | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| add_v3_v3(centroid, fpt); | add_v3_v3(centroid, fpt); | ||||
| minmax_v3v3_v3(min, max, fpt); | minmax_v3v3_v3(min, max, fpt); | ||||
| } | |||||
| count++; | count++; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (v3d->around == V3D_AROUND_CENTER_MEAN && count) { | if (v3d->around == V3D_AROUND_CENTER_MEAN && count) { | ||||
| mul_v3_fl(centroid, 1.0f / (float)count); | mul_v3_fl(centroid, 1.0f / (float)count); | ||||
| copy_v3_v3(cursor, centroid); | copy_v3_v3(cursor, centroid); | ||||
| } | } | ||||
| else { | else { | ||||
| mid_v3_v3v3(cursor, min, max); | mid_v3_v3v3(cursor, min, max); | ||||
| } | } | ||||
| 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; | ||||
| } | } | ||||
| void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot) | void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Snap Cursor to Selected Points"; | ot->name = "Snap Cursor to Selected Points"; | ||||
| Show All 25 Lines | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| /* Apply thickness */ | /* Apply thickness */ | ||||
| gps->thickness = gps->thickness + gpl->thickness; | gps->thickness = gps->thickness + gpl->thickness; | ||||
| } | } | ||||
| } | } | ||||
| /* clear value */ | /* clear value */ | ||||
| gpl->thickness = 0.0f; | gpl->thickness = 0.0f; | ||||
| /* 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; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) | void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 25 Lines | static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) | ||||
| /* loop all selected strokes */ | /* loop all selected strokes */ | ||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| { | { | ||||
| if (gpl->actframe == NULL) | if (gpl->actframe == NULL) | ||||
| continue; | continue; | ||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | ||||
| bGPDpalettecolor *palcolor = gps->palcolor; | PaletteColor *palcolor = gps->palcolor; | ||||
| /* skip strokes that are not selected or invalid for current view */ | /* skip strokes that are not selected or invalid for current view */ | ||||
| if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) | if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* skip hidden or locked colors */ | /* skip hidden or locked colors */ | ||||
| if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) | if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) | ||||
| continue; | continue; | ||||
| Show All 14 Lines | for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | ||||
| BLI_assert(0); | BLI_assert(0); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | 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; | ||||
| } | } | ||||
| /** | /** | ||||
| * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with | * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with | ||||
| * option to force opened/closed strokes instead of just toggle behavior. | * option to force opened/closed strokes instead of just toggle behavior. | ||||
| ▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], | ||||
| newpoint = &gps->points[gps->totpoints - 1]; | newpoint = &gps->points[gps->totpoints - 1]; | ||||
| newpoint->x = point->x * delta[0]; | newpoint->x = point->x * delta[0]; | ||||
| newpoint->y = point->y * delta[1]; | newpoint->y = point->y * delta[1]; | ||||
| newpoint->z = point->z * delta[2]; | newpoint->z = point->z * delta[2]; | ||||
| newpoint->flag = point->flag; | newpoint->flag = point->flag; | ||||
| newpoint->pressure = pressure; | newpoint->pressure = pressure; | ||||
| newpoint->strength = strength; | newpoint->strength = strength; | ||||
| newpoint->time = point->time + deltatime; | newpoint->time = point->time + deltatime; | ||||
| newpoint->totweight = point->totweight; | |||||
| newpoint->weights = MEM_dupallocN(point->weights); | |||||
| } | } | ||||
| /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ | /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ | ||||
| static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps) | static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps) | ||||
| { | { | ||||
| bGPDspoint point; | bGPDspoint point; | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps) | ||||
| } | } | ||||
| } | } | ||||
| static int gp_stroke_join_exec(bContext *C, wmOperator *op) | static int gp_stroke_join_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); | bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); | ||||
| bGPDstroke *gps, *gpsn; | bGPDstroke *gps, *gpsn; | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Palette *palette = BKE_palette_get_active_from_context(C); | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | PaletteColor *palcolor = BKE_palette_color_get_active(palette); | ||||
| bGPDframe *gpf_a = NULL; | bGPDframe *gpf_a = NULL; | ||||
| bGPDstroke *stroke_a = NULL; | bGPDstroke *stroke_a = NULL; | ||||
| bGPDstroke *stroke_b = NULL; | bGPDstroke *stroke_b = NULL; | ||||
| bGPDstroke *new_stroke = NULL; | bGPDstroke *new_stroke = NULL; | ||||
| const int type = RNA_enum_get(op->ptr, "type"); | const int type = RNA_enum_get(op->ptr, "type"); | ||||
| const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps"); | const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps"); | ||||
| Show All 38 Lines | for (gps = gpf->strokes.first; gps; gps = gpsn) { | ||||
| } | } | ||||
| else { | else { | ||||
| stroke_b = gps; | stroke_b = gps; | ||||
| /* create a new stroke if was not created before (only created if something to join) */ | /* create a new stroke if was not created before (only created if something to join) */ | ||||
| if (new_stroke == NULL) { | if (new_stroke == NULL) { | ||||
| new_stroke = MEM_dupallocN(stroke_a); | new_stroke = MEM_dupallocN(stroke_a); | ||||
| new_stroke->points = MEM_dupallocN(stroke_a->points); | new_stroke->points = MEM_dupallocN(stroke_a->points); | ||||
| BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke); | |||||
| new_stroke->triangles = NULL; | new_stroke->triangles = NULL; | ||||
| new_stroke->tot_triangles = 0; | new_stroke->tot_triangles = 0; | ||||
| new_stroke->flag |= GP_STROKE_RECALC_CACHES; | new_stroke->flag |= GP_STROKE_RECALC_CACHES; | ||||
| /* if new, set current color */ | /* if new, set current color */ | ||||
| if (type == GP_STROKE_JOINCOPY) { | if (type == GP_STROKE_JOINCOPY) { | ||||
| new_stroke->palcolor = palcolor; | new_stroke->palcolor = palcolor; | ||||
| BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname)); | BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname)); | ||||
| new_stroke->flag |= GP_STROKE_RECALC_COLOR; | |||||
| } | } | ||||
| } | } | ||||
| /* join new_stroke and stroke B. New stroke will contain all the previous data */ | /* join new_stroke and stroke B. New stroke will contain all the previous data */ | ||||
| gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps); | gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps); | ||||
| /* if join only, delete old strokes */ | /* if join only, delete old strokes */ | ||||
| if (type == GP_STROKE_JOIN) { | if (type == GP_STROKE_JOIN) { | ||||
| Show All 22 Lines | if (new_stroke) { | ||||
| if (activegpl->actframe == NULL) | if (activegpl->actframe == NULL) | ||||
| activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); | activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); | ||||
| BLI_addtail(&activegpl->actframe->strokes, new_stroke); | BLI_addtail(&activegpl->actframe->strokes, new_stroke); | ||||
| } | } | ||||
| } | } | ||||
| /* 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; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_join(wmOperatorType *ot) | void GPENCIL_OT_stroke_join(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem join_type[] = { | static const EnumPropertyItem join_type[] = { | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| /* flip stroke */ | /* flip stroke */ | ||||
| gpencil_flip_stroke(gps); | gpencil_flip_stroke(gps); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | 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; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_flip(wmOperatorType *ot) | void GPENCIL_OT_stroke_flip(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 24 Lines | static int gp_strokes_reproject_poll(bContext *C) | ||||
| * - 1) Editable GP data | * - 1) Editable GP data | ||||
| * - 2) 3D View only (2D editors don't have projection issues) | * - 2) 3D View only (2D editors don't have projection issues) | ||||
| */ | */ | ||||
| return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); | return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); | ||||
| } | } | ||||
| static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) | static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| GP_SpaceConversion gsc = {NULL}; | GP_SpaceConversion gsc = {NULL}; | ||||
| eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type"); | eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type"); | ||||
| /* init space conversion stuff */ | /* init space conversion stuff */ | ||||
| gp_point_conversion_init(C, &gsc); | gp_point_conversion_init(C, &gsc); | ||||
| /* init autodist for geometry projection */ | /* init autodist for geometry projection */ | ||||
| Show All 13 Lines | static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| float inverse_diff_mat[4][4]; | float inverse_diff_mat[4][4]; | ||||
| /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ | /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ | ||||
| /* TODO: add this bit to the iteration macro? */ | /* TODO: add this bit to the iteration macro? */ | ||||
| if (gpl->parent) { | |||||
| invert_m4_m4(inverse_diff_mat, diff_mat); | invert_m4_m4(inverse_diff_mat, diff_mat); | ||||
| } | |||||
| /* Adjust each point */ | /* Adjust each point */ | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| float xy[2]; | float xy[2]; | ||||
| /* 3D to Screenspace */ | /* 3D to Screenspace */ | ||||
| /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace | /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace | ||||
| * coordinates, resulting in lost precision, which in turn causes stairstepping | * coordinates, resulting in lost precision, which in turn causes stairstepping | ||||
| * artifacts in the final points. | * artifacts in the final points. | ||||
| */ | */ | ||||
| if (gpl->parent == NULL) { | |||||
| gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); | |||||
| } | |||||
| else { | |||||
| bGPDspoint pt2; | bGPDspoint pt2; | ||||
| gp_point_to_parent_space(pt, diff_mat, &pt2); | gp_point_to_parent_space(pt, diff_mat, &pt2); | ||||
| gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); | gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); | ||||
| } | |||||
| /* Project screenspace back to 3D space (from current perspective) | /* Project screenspace back to 3D space (from current perspective) | ||||
| * so that all points have been treated the same way | * so that all points have been treated the same way | ||||
| */ | */ | ||||
| if (mode == GP_REPROJECT_PLANAR) { | if (mode == GP_REPROJECT_PLANAR) { | ||||
| /* Planar - All on same plane parallel to the viewplane */ | /* Planar - All on same plane parallel to the viewplane */ | ||||
| gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); | gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); | ||||
| } | } | ||||
| Show All 11 Lines | if (gps->flag & GP_STROKE_SELECT) { | ||||
| } | } | ||||
| else { | else { | ||||
| /* Default to planar */ | /* Default to planar */ | ||||
| gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); | gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); | ||||
| } | } | ||||
| } | } | ||||
| /* Unapply parent corrections */ | /* Unapply parent corrections */ | ||||
| if (gpl->parent) { | |||||
| mul_m4_v3(inverse_diff_mat, &pt->x); | mul_m4_v3(inverse_diff_mat, &pt->x); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| GP_EDITABLE_STROKES_END; | GP_EDITABLE_STROKES_END; | ||||
| 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; | ||||
| } | } | ||||
| void GPENCIL_OT_reproject(wmOperatorType *ot) | void GPENCIL_OT_reproject(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem reproject_type[] = { | static const EnumPropertyItem reproject_type[] = { | ||||
| {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", | {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | if (gps->flag & GP_STROKE_SELECT) { | ||||
| for (int s = 0; s < cuts; s++) { | for (int s = 0; s < cuts; s++) { | ||||
| totnewpoints = gp_count_subdivision_cuts(gps); | totnewpoints = gp_count_subdivision_cuts(gps); | ||||
| if (totnewpoints == 0) { | if (totnewpoints == 0) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* duplicate points in a temp area */ | /* duplicate points in a temp area */ | ||||
| temp_points = MEM_dupallocN(gps->points); | temp_points = MEM_dupallocN(gps->points); | ||||
| oldtotpoints = gps->totpoints; | oldtotpoints = gps->totpoints; | ||||
| // TODO | |||||
| /* resize the points arrys */ | /* resize the points arrys */ | ||||
| gps->totpoints += totnewpoints; | gps->totpoints += totnewpoints; | ||||
| gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); | gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| /* loop and interpolate */ | /* loop and interpolate */ | ||||
| i2 = 0; | i2 = 0; | ||||
| for (int i = 0; i < oldtotpoints; i++) { | for (int i = 0; i < oldtotpoints; i++) { | ||||
| bGPDspoint *pt = &temp_points[i]; | bGPDspoint *pt = &temp_points[i]; | ||||
| bGPDspoint *pt_final = &gps->points[i2]; | bGPDspoint *pt_final = &gps->points[i2]; | ||||
| /* copy current point */ | /* copy current point */ | ||||
| copy_v3_v3(&pt_final->x, &pt->x); | copy_v3_v3(&pt_final->x, &pt->x); | ||||
| pt_final->pressure = pt->pressure; | pt_final->pressure = pt->pressure; | ||||
| pt_final->strength = pt->strength; | pt_final->strength = pt->strength; | ||||
| pt_final->time = pt->time; | pt_final->time = pt->time; | ||||
| pt_final->flag = pt->flag; | pt_final->flag = pt->flag; | ||||
| pt_final->totweight = 0; | |||||
| pt_final->weights = NULL; | |||||
| ++i2; | ++i2; | ||||
| /* if next point is selected add a half way point */ | /* if next point is selected add a half way point */ | ||||
| if (pt->flag & GP_SPOINT_SELECT) { | if (pt->flag & GP_SPOINT_SELECT) { | ||||
| if (i + 1 < oldtotpoints) { | if (i + 1 < oldtotpoints) { | ||||
| if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { | if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { | ||||
| pt_final = &gps->points[i2]; | pt_final = &gps->points[i2]; | ||||
| /* Interpolate all values */ | /* Interpolate all values */ | ||||
| bGPDspoint *next = &temp_points[i + 1]; | bGPDspoint *next = &temp_points[i + 1]; | ||||
| interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); | interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); | ||||
| pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); | pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); | ||||
| pt_final->strength = interpf(pt->strength, next->strength, 0.5f); | pt_final->strength = interpf(pt->strength, next->strength, 0.5f); | ||||
| CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt_final->time = interpf(pt->time, next->time, 0.5f); | pt_final->time = interpf(pt->time, next->time, 0.5f); | ||||
| pt_final->flag |= GP_SPOINT_SELECT; | pt_final->flag |= GP_SPOINT_SELECT; | ||||
| pt_final->totweight = 0; | |||||
| pt_final->weights = NULL; | |||||
| ++i2; | ++i2; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* free temp memory */ | /* free temp memory */ | ||||
| MEM_freeN(temp_points); | MEM_freeN(temp_points); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| GP_EDITABLE_STROKES_END; | GP_EDITABLE_STROKES_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; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) | void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) | ||||
| { | { | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| Show All 11 Lines | void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; | ||||
| /* properties */ | /* properties */ | ||||
| prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5); | prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5); | ||||
| /* avoid re-using last var because it can cause _very_ high value and annoy users */ | /* avoid re-using last var because it can cause _very_ high value and annoy users */ | ||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | RNA_def_property_flag(prop, PROP_SKIP_SAVE); | ||||
| } | } | ||||
| /* ** simplify stroke *** */ | |||||
| static int gp_stroke_simplify_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| float factor = RNA_float_get(op->ptr, "factor"); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* Go through each editable + selected stroke */ | |||||
| GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) | |||||
| { | |||||
| if (gps->flag & GP_STROKE_SELECT) { | |||||
| /* simplify stroke using Ramer-Douglas-Peucker algorithm */ | |||||
| BKE_gpencil_simplify_stroke(gpl, gps, factor); | |||||
| } | |||||
| } | |||||
| GP_EDITABLE_STROKES_END; | |||||
| /* 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_simplify(wmOperatorType *ot) | |||||
| { | |||||
| PropertyRNA *prop; | |||||
| /* identifiers */ | |||||
| ot->name = "Simplify Stroke"; | |||||
| ot->idname = "GPENCIL_OT_stroke_simplify"; | |||||
| ot->description = "Simplify selected stroked reducing number of points"; | |||||
| /* api callbacks */ | |||||
| ot->exec = gp_stroke_simplify_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; | |||||
| /* properties */ | |||||
| prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); | |||||
| /* avoid re-using last var */ | |||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | |||||
| } | |||||