From 1184649d9426c4618be02c77b0a6d04f4c7bc59b Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 9 Feb 2023 16:10:53 +0100 Subject: [PATCH] GPencil: Improve simplify modifier --- source/blender/blenkernel/BKE_gpencil_geom.h | 5 +- .../blender/blenkernel/intern/gpencil_geom.cc | 146 ++++++++++-------- source/blender/editors/gpencil/gpencil_edit.c | 2 +- .../blender/editors/gpencil/gpencil_paint.c | 3 +- .../intern/MOD_gpencilsimplify.c | 6 +- .../makesdna/DNA_gpencil_modifier_defaults.h | 3 + .../makesdna/DNA_gpencil_modifier_types.h | 4 + .../makesrna/intern/rna_gpencil_modifier.c | 21 +++ 8 files changed, 123 insertions(+), 67 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index 70b12477b43..3ea703e5587 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -82,7 +82,10 @@ void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]); */ void BKE_gpencil_stroke_simplify_adaptive(struct bGPdata *gpd, struct bGPDstroke *gps, - float epsilon); + float epsilon, + float fac_position, + float fac_thickness, + float fac_strength); /** * Simplify alternate vertex of stroke except extremes. * \param gpd: Grease pencil data-block diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 8115920f938..cf03b55305d 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -15,6 +15,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_array_utils.h" #include "BLI_blenlib.h" #include "BLI_ghash.h" @@ -24,6 +25,7 @@ #include "BLI_math_vector_types.hh" #include "BLI_polyfill_2d.h" #include "BLI_span.hh" +#include "BLI_stack.hh" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" @@ -48,8 +50,12 @@ #include "DEG_depsgraph_query.h" +using blender::Array; +using blender::float2; using blender::float3; +using blender::MutableSpan; using blender::Span; +using blender::Stack; /* -------------------------------------------------------------------- */ /** \name Grease Pencil Object: Bound-box Support @@ -2015,70 +2021,86 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3]) /** \} */ +/** + * Iterative implementation of the Ramer–Douglas–Peucker algorithm + * (https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) + * Note: Assumes \param r_marked_keep has the same size as \param points. + */ +static void simplify_point_buffer(Span points, + int start_idx, + int end_idx, + float epsilon, + float fac_position, + float fac_thickness, + float fac_strength, + MutableSpan r_marked_keep) +{ + Stack> stack; + stack.push({start_idx, end_idx}); + + while (!stack.is_empty()) { + auto [start, end] = stack.pop(); + + float max_dist = 0.0f; + int max_index = -1; + for (int i = start + 1; i < end; i++) { + if (r_marked_keep[i - start_idx]) { + float3 point_on_line; + float lambda = closest_to_line_segment_v3( + point_on_line, &points[i].x, &points[start].x, &points[end].x); + float interp_thickness = interpf(points[end].pressure, points[start].pressure, lambda); + float interp_strength = interpf(points[end].strength, points[start].strength, lambda); + + float dist_position = len_v3v3(point_on_line, &points[i].x) * fac_position; + float dist_thickness = fabsf(points[i].pressure - interp_thickness) * fac_thickness; + float dist_strength = fabsf(points[i].strength - interp_strength) * fac_strength; + + float dist = std::max({dist_position, dist_thickness, dist_strength}); + if (dist > max_dist) { + max_dist = dist; + max_index = i; + } + } + } + + if (max_dist > epsilon) { + stack.push({start, max_index}); + stack.push({max_index, end}); + } + else { + for (int i = start + 1; i < end; i++) { + r_marked_keep[i - start_idx] = false; + } + } + } +} + /* -------------------------------------------------------------------- */ /** \name Stroke Simplify * \{ */ -void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon) +void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, + bGPDstroke *gps, + float epsilon, + float fac_position, + float fac_thickness, + float fac_strength) { bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points); + int totpoints = gps->totpoints; - char *marked = nullptr; - char work; - int start = 0; - int end = gps->totpoints - 1; + Span points(old_points, totpoints); - marked = (char *)MEM_callocN(totpoints, "GP marked array"); - marked[start] = 1; - marked[end] = 1; - - work = 1; - int totmarked = 0; - /* while still reducing */ - while (work) { - int ls, le; - work = 0; - - ls = start; - le = start + 1; - - /* while not over interval */ - while (ls < end) { - int max_i = 0; - /* divided to get more control */ - float max_dist = epsilon / 10.0f; - - /* find the next marked point */ - while (marked[le] == 0) { - le++; - } - - for (int i = ls + 1; i < le; i++) { - float point_on_line[3]; - float dist; - - closest_to_line_segment_v3( - point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x); - - dist = len_v3v3(point_on_line, &old_points[i].x); - - if (dist > max_dist) { - max_dist = dist; - max_i = i; - } - } - - if (max_i != 0) { - work = 1; - marked[max_i] = 1; - totmarked++; - } - - ls = le; - le = ls + 1; - } - } + Array marked_as_keep(totpoints, true); + simplify_point_buffer(points, + 0, + totpoints - 1, + epsilon, + fac_position, + fac_thickness, + fac_strength, + marked_as_keep); /* adding points marked */ MDeformVert *old_dvert = nullptr; @@ -2088,22 +2110,21 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert); } /* resize gps */ - int j = 0; + int count = 0; for (int i = 0; i < totpoints; i++) { bGPDspoint *pt_src = &old_points[i]; - bGPDspoint *pt = &gps->points[j]; - - if ((marked[i]) || (i == 0) || (i == totpoints - 1)) { + bGPDspoint *pt = &gps->points[count]; + if (marked_as_keep[i]) { *pt = blender::dna::shallow_copy(*pt_src); if (gps->dvert != nullptr) { dvert_src = &old_dvert[i]; - MDeformVert *dvert = &gps->dvert[j]; + MDeformVert *dvert = &gps->dvert[count]; memcpy(dvert, dvert_src, sizeof(MDeformVert)); if (dvert_src->dw) { memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight)); } } - j++; + count++; } else { if (gps->dvert != nullptr) { @@ -2113,14 +2134,13 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e } } - gps->totpoints = j; + gps->totpoints = count; /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gpd, gps); MEM_SAFE_FREE(old_points); MEM_SAFE_FREE(old_dvert); - MEM_SAFE_FREE(marked); } void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index c6108f761a8..6fa77751801 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4748,7 +4748,7 @@ static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { /* simplify stroke using Ramer-Douglas-Peucker algorithm */ - BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor); + BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor, 10, 1, 1); changed = true; } } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 5814248f24c..62e9137d842 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1280,7 +1280,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* Simplify adaptive */ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (brush->gpencil_settings->simplify_f > 0.0f)) { - BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f); + BKE_gpencil_stroke_simplify_adaptive( + gpd, gps, brush->gpencil_settings->simplify_f, 10, 0, 0); } /* Set material index. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index f1826c3a59d..e0cd10491c2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -85,7 +85,8 @@ static void deformStroke(GpencilModifierData *md, } case GP_SIMPLIFY_ADAPTIVE: { /* simplify stroke using Ramer-Douglas-Peucker algorithm */ - BKE_gpencil_stroke_simplify_adaptive(gpd, gps, mmd->factor); + BKE_gpencil_stroke_simplify_adaptive( + gpd, gps, mmd->factor, mmd->fac_position, mmd->fac_thickness, mmd->fac_strength); break; } case GP_SIMPLIFY_SAMPLE: { @@ -133,6 +134,9 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) } else if (mode == GP_SIMPLIFY_ADAPTIVE) { uiItemR(layout, ptr, "factor", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "fac_position", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "fac_thickness", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "fac_strength", 0, NULL, ICON_NONE); } else if (mode == GP_SIMPLIFY_SAMPLE) { uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE); diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 00543b9df9a..276aa1fa0c4 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -198,6 +198,9 @@ .pass_index = 0, \ .flag = 0, \ .factor = 0.0f, \ + .fac_position = 10.0f, \ + .fac_thickness = 0.0f, \ + .fac_strength = 0.0f, \ .mode = 0, \ .step = 1, \ .layer_pass = 0, \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 146d4bc94bc..07be2b0031b 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -742,6 +742,9 @@ typedef struct SimplifyGpencilModifierData { int flag; /** Factor of simplify. */ float factor; + float fac_position; + float fac_thickness; + float fac_strength; /** Type of simplify. */ short mode; /** Every n vertex to keep. */ @@ -754,6 +757,7 @@ typedef struct SimplifyGpencilModifierData { float sharp_threshold; /** Merge distance */ float distance; + char _pad[4]; } SimplifyGpencilModifierData; typedef enum eSimplifyGpencil_Flag { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 8c8a9dc4980..170f98c9882 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1378,6 +1378,27 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Factor", "Factor of Simplify"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "fac_position", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "fac_position"); + RNA_def_property_range(prop, 0, 100.0); + RNA_def_property_ui_range(prop, 0, 20.0f, 1.0f, 1); + RNA_def_property_ui_text(prop, "Factor Position", "Factor of Position"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fac_thickness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "fac_thickness"); + RNA_def_property_range(prop, 0, 100.0); + RNA_def_property_ui_range(prop, 0, 20.0f, 1.0f, 1); + RNA_def_property_ui_text(prop, "Factor Thickness", "Factor of Thickness"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "fac_strength", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "fac_strength"); + RNA_def_property_range(prop, 0, 100.0); + RNA_def_property_ui_range(prop, 0, 20.0f, 1.0f, 1); + RNA_def_property_ui_text(prop, "Factor Strength", "Factor of Strength"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); -- 2.30.2