Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_edit.c
| Context not available. | |||||
| #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" | ||||
| Context not available. | |||||
| #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; | ||||
| Context not available. | |||||
| 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); | ||||
| Context not available. | |||||
| /* 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); | |||||
| } | } | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| Context not available. | |||||
| 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 */ | ||||
| Context not available. | |||||
| 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); | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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); | ||||
| } | } | ||||
| Context not available. | |||||
| /* 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) */ | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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) { | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| } | } | ||||
| /* updates */ | /* updates */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| /* updates */ | /* updates */ | ||||
| if (success) { | if (success) { | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | BKE_gpencil_batch_cache_dirty(gpd); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| else { | else { | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| /* 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; | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| 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; | ||||
| } | } | ||||
| Context not available. | |||||
| /* ----------------------------------- */ | /* ----------------------------------- */ | ||||
| /* 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) | ||||
| { | { | ||||
| Context not available. | |||||
| 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 */ | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | switch (mode) { | ||||
| if (pt->flag & GP_SPOINT_SELECT) { | case GP_DISSOLVE_POINTS: | ||||
| /* selected point - one of the points to remove */ | /* Count how many points are selected (i.e. how many to remove) */ | ||||
| tot--; | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| } | if (pt->flag & GP_SPOINT_SELECT) { | ||||
| /* selected point - one of the points to remove */ | |||||
| 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 */ | ||||
| MEM_freeN(gps->points); | if (gps->points) { | ||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| 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; | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | switch (mode) { | ||||
| if ((pt->flag & GP_SPOINT_SELECT) == 0) { | case GP_DISSOLVE_POINTS: | ||||
| *npt = *pt; | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| npt++; | if ((pt->flag & GP_SPOINT_SELECT) == 0) { | ||||
| } | *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++; | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | } | ||||
| /* free the old buffer */ | /* free the old buffer */ | ||||
| MEM_freeN(gps->points); | if (gps->points) { | ||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| 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; | ||||
| Context not available. | |||||
| /* 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; | ||||
| Context not available. | |||||
| 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; | ||||
| } | } | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| /* 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: | ||||
| Context not available. | |||||
| MEM_freeN(islands); | MEM_freeN(islands); | ||||
| /* Delete the old stroke */ | /* Delete the old stroke */ | ||||
| MEM_freeN(gps->points); | if (gps->points) { | ||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| if (gps->triangles) { | if (gps->triangles) { | ||||
| MEM_freeN(gps->triangles); | MEM_freeN(gps->triangles); | ||||
| } | } | ||||
| Context not available. | |||||
| /* 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) | ||||
| Context not available. | |||||
| /* 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; | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| 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; | ||||
| } | } | ||||
| Context not available. | |||||
| 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 ************************ */ | ||||
| Context not available. | |||||
| { | { | ||||
| 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) { | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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) { | /* apply parent transformations */ | ||||
| pt->x = gridf * floorf(0.5f + pt->x / gridf); | float fpt[3]; | ||||
| pt->y = gridf * floorf(0.5f + pt->y / gridf); | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| pt->z = gridf * floorf(0.5f + pt->z / gridf); | |||||
| } | fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); | ||||
| else { | fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); | ||||
| /* apply parent transformations */ | fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); | ||||
| float fpt[3]; | |||||
| mul_v3_m4v3(fpt, diff_mat, &pt->x); | /* return data */ | ||||
| copy_v3_v3(&pt->x, fpt); | |||||
| fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); | gp_apply_parent_point(obact, gpd, gpl, pt); | ||||
| fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); | |||||
| fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); | |||||
| /* return data */ | |||||
| copy_v3_v3(&pt->x, fpt); | |||||
| 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; | ||||
| } | } | ||||
| Context not available. | |||||
| 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); | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| Context not available. | |||||
| 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]; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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) { | /* apply parent transformations */ | ||||
| add_v3_v3(centroid, &pt->x); | float fpt[3]; | ||||
| minmax_v3v3_v3(min, max, &pt->x); | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| } | |||||
| else { | |||||
| /* apply parent transformations */ | |||||
| float fpt[3]; | |||||
| 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++; | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| } | } | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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) | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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 ) */ | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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; | |||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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"); | ||||
| Context not available. | |||||
| /* 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++) { | ||||
| Context not available. | |||||
| * 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) { | bGPDspoint pt2; | ||||
| gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); | gp_point_to_parent_space(pt, diff_mat, &pt2); | ||||
| } | gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); | ||||
| else { | |||||
| bGPDspoint pt2; | |||||
| gp_point_to_parent_space(pt, diff_mat, &pt2); | |||||
| 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 | ||||
| Context not available. | |||||
| } | } | ||||
| /* 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; | ||||
| } | } | ||||
| Context not available. | |||||
| /* 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); | ||||
| Context not available. | |||||
| 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 */ | ||||
| Context not available. | |||||
| 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; | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| 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; | ||||
| Context not available. | |||||
| 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); | |||||
| } | |||||
| Context not available. | |||||