From f944121700daf267766399ef368834be3cdec518 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 2 Jun 2021 16:41:13 +0200 Subject: GPencil: New operator to Normalize strokes Sometimes is required to reset the thickness or the opacity of the strokes. Actually this was done using a modifier, but this operators solves this. Reviewed By: mendio, filedescriptor Maniphest Tasks: T87427 Differential Revision: https://developer.blender.org/D11453 --- source/blender/editors/gpencil/gpencil_edit.c | 178 ++++++++++++++++++++++++ source/blender/editors/gpencil/gpencil_intern.h | 1 + source/blender/editors/gpencil/gpencil_ops.c | 1 + 3 files changed, 180 insertions(+) (limited to 'source') diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e65afa1abff..dc0508bd608 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -5347,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Normalize Operator + * \{ */ + +typedef enum eGP_NormalizeMode { + GP_NORMALIZE_THICKNESS = 0, + GP_NORMALIZE_OPACITY, +} eGP_NormalizeMode; + +static bool gpencil_stroke_normalize_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return false; + } + + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + + return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL)); +} + +static void gpencil_stroke_normalize_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE); + + if (mode == GP_NORMALIZE_THICKNESS) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE); + } + else if (mode == GP_NORMALIZE_OPACITY) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE); + } +} + +static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* Sanity checks. */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + const int value = RNA_int_get(op->ptr, "value"); + const float factor = RNA_float_get(op->ptr, "factor"); + + /* Go through each editable + selected stroke. */ + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + if (gpf == NULL) { + continue; + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT : + (gps->flag & GP_STROKE_SELECT); + if (!selected) { + continue; + } + + float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1); + /* Fill opacity need to be managed before. */ + if (mode == GP_NORMALIZE_OPACITY) { + gps->fill_opacity_fac = factor; + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); + } + + /* Loop all Polyline points. */ + if (!is_curve_edit) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + pt->strength = factor; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + } + else { + /* Loop all Bezier points. */ + for (int i = 0; i < gps->editcurve->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + gpc_pt->strength = factor; + CLAMP(gpc_pt->strength, 0.0f, 1.0f); + } + } + + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + } + /* If not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_normalize(wmOperatorType *ot) +{ + static const EnumPropertyItem prop_gpencil_normalize_modes[] = { + {GP_NORMALIZE_THICKNESS, + "THICKNESS", + 0, + "Thickness", + "Normalizes the stroke thickness by making all points use the same thickness value"}, + {GP_NORMALIZE_OPACITY, + "OPACITY", + 0, + "Opacity", + "Normalizes the stroke opacity by making all points use the same opacity value"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Normalize Stroke"; + ot->idname = "GPENCIL_OT_stroke_normalize"; + ot->description = "Normalize stroke attributes"; + + /* api callbacks */ + ot->exec = gpencil_stroke_normalize_exec; + ot->poll = gpencil_stroke_normalize_poll; + ot->ui = gpencil_stroke_normalize_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum( + ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized"); + RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000); +} + +/** \} */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 0e8fdc3c375..276e8c81e0f 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -488,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot); void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot); void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot); void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 0e9ce1d603f..35640cf3b66 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -649,6 +649,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance); WM_operatortype_append(GPENCIL_OT_stroke_merge_material); WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color); + WM_operatortype_append(GPENCIL_OT_stroke_normalize); WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); -- cgit v1.2.3