diff options
author | YimingWu <xp8110@outlook.com> | 2022-02-22 07:54:35 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2022-02-22 07:54:35 +0300 |
commit | 2234bfbcdb14299aa2ed28ed3ee6682c6d9a7a0c (patch) | |
tree | 909692b6362d5c49e0908f397e78ceeb409da985 /source | |
parent | 0f2e0a25e13d2d67f3d08d068ba11e255794bb6b (diff) |
GPencil: Simplify sample modifier improvements.
1. Now handles cyclic strokes correctly.
2. Added a sharp threshold value to allow preservation of sharp corners.
Reviewed By: Antonio Vazquez (antoniov), Aleš Jelovčan (frogstomp)
Ref D14044
Diffstat (limited to 'source')
10 files changed, 94 insertions, 35 deletions
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index eb61bd32eee..4127030e96f 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -208,11 +208,13 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd, * \param gpd: Grease pencil data-block * \param gps: Stroke to sample * \param dist: Distance of one segment + * \param sharp_threshold: Threshold for preserving sharp corners */ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, - float dist, - bool select); + const float dist, + const bool select, + const float sharp_threshold); /** * Apply smooth position to stroke point. * \param gps: Stroke to smooth diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 5ce45f6df5a..ee6b77e6463 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -443,7 +443,7 @@ static void gpencil_convert_spline(Main *bmain, } if (sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd, gps, sample, false); + BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0); } /* Recalc fill geometry. */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 365171b300f..1823d84d48d 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -202,8 +202,8 @@ static int stroke_march_next_point(const bGPDstroke *gps, int next_point_index = index_next_pt; bGPDspoint *pt = nullptr; - if (!(next_point_index < gps->totpoints)) { - return -1; + if (next_point_index == gps->totpoints) { + next_point_index = 0; } copy_v3_v3(step_start, current); @@ -211,15 +211,33 @@ static int stroke_march_next_point(const bGPDstroke *gps, copy_v3_v3(point, &pt->x); remaining_till_next = len_v3v3(point, step_start); - while (remaining_till_next < remaining_march) { + while (remaining_till_next < remaining_march && next_point_index) { remaining_march -= remaining_till_next; pt = &gps->points[next_point_index]; + if (pt->flag & GP_SPOINT_TEMP_TAG) { + pt = &gps->points[next_point_index]; + copy_v3_v3(result, &pt->x); + *pressure = gps->points[next_point_index].pressure; + *strength = gps->points[next_point_index].strength; + memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4])); + + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); + *index_to = next_point_index; + *ratio_result = 1.0f; + next_point_index++; + return next_point_index == 0 ? gps->totpoints : next_point_index; + } + next_point_index++; copy_v3_v3(point, &pt->x); copy_v3_v3(step_start, point); - next_point_index++; if (!(next_point_index < gps->totpoints)) { - next_point_index = gps->totpoints - 1; - break; + if (gps->flag & GP_STROKE_CYCLIC) { + next_point_index = 0; + } + else { + next_point_index = gps->totpoints - 1; + break; + } } pt = &gps->points[next_point_index]; copy_v3_v3(point, &pt->x); @@ -232,35 +250,37 @@ static int stroke_march_next_point(const bGPDstroke *gps, *strength = gps->points[next_point_index].strength; memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4])); - *index_from = next_point_index - 1; + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); *index_to = next_point_index; *ratio_result = 1.0f; return 0; } + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); + *index_to = next_point_index; + float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); + *ratio_result = ratio; + *pressure = interpf( - gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio); + gps->points[next_point_index].pressure, gps->points[*index_from].pressure, ratio); *strength = interpf( - gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio); + gps->points[next_point_index].strength, gps->points[*index_from].strength, ratio); interp_v4_v4v4(vert_color, - gps->points[next_point_index - 1].vert_color, + gps->points[*index_from].vert_color, gps->points[next_point_index].vert_color, ratio); - *index_from = next_point_index - 1; - *index_to = next_point_index; - *ratio_result = ratio; - - return next_point_index; + return next_point_index == 0 ? gps->totpoints : next_point_index; } static int stroke_march_next_point_no_interp(const bGPDstroke *gps, const int index_next_pt, const float *current, const float dist, + const float sharp_threshold, float *result) { float remaining_till_next = 0.0f; @@ -270,8 +290,8 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, int next_point_index = index_next_pt; bGPDspoint *pt = nullptr; - if (!(next_point_index < gps->totpoints)) { - return -1; + if (next_point_index == gps->totpoints) { + next_point_index = 0; } copy_v3_v3(step_start, current); @@ -279,15 +299,29 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, copy_v3_v3(point, &pt->x); remaining_till_next = len_v3v3(point, step_start); - while (remaining_till_next < remaining_march) { + while (remaining_till_next < remaining_march && next_point_index) { remaining_march -= remaining_till_next; pt = &gps->points[next_point_index]; + if (next_point_index < gps->totpoints - 1 && + angle_v3v3v3(&gps->points[next_point_index - 1].x, + &gps->points[next_point_index].x, + &gps->points[next_point_index + 1].x) < sharp_threshold) { + copy_v3_v3(result, &pt->x); + pt->flag |= GP_SPOINT_TEMP_TAG; + next_point_index++; + return next_point_index == 0 ? gps->totpoints : next_point_index; + } + next_point_index++; copy_v3_v3(point, &pt->x); copy_v3_v3(step_start, point); - next_point_index++; if (!(next_point_index < gps->totpoints)) { - next_point_index = gps->totpoints - 1; - break; + if (gps->flag & GP_STROKE_CYCLIC) { + next_point_index = 0; + } + else { + next_point_index = gps->totpoints - 1; + break; + } } pt = &gps->points[next_point_index]; copy_v3_v3(point, &pt->x); @@ -296,15 +330,16 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, if (remaining_till_next < remaining_march) { pt = &gps->points[next_point_index]; copy_v3_v3(result, &pt->x); + /* Stroke marching only terminates here. */ return 0; } float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); - return next_point_index; + return next_point_index == 0 ? gps->totpoints : next_point_index; } -static int stroke_march_count(const bGPDstroke *gps, const float dist) +static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold) { int point_count = 0; float point[3]; @@ -315,8 +350,13 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist) copy_v3_v3(point, &pt->x); point_count++; + /* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */ + for (int i = 0; i < gps->totpoints; i++) { + gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG); + } + while ((next_point_index = stroke_march_next_point_no_interp( - gps, next_point_index, point, dist, point)) > -1) { + gps, next_point_index, point, dist, sharp_threshold, point)) > -1) { point_count++; if (next_point_index == 0) { break; /* last point finished */ @@ -394,7 +434,11 @@ static void stroke_interpolate_deform_weights( } } -bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select) +bool BKE_gpencil_stroke_sample(bGPdata *gpd, + bGPDstroke *gps, + const float dist, + const bool select, + const float sharp_threshold) { bGPDspoint *pt = gps->points; bGPDspoint *pt1 = nullptr; @@ -406,7 +450,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return false; } /* TODO: Implement feature point preservation. */ - int count = stroke_march_count(gps, dist); + int count = stroke_march_count(gps, dist, sharp_threshold); bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled"); @@ -491,6 +535,8 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, gps->totpoints = i; + gps->flag &= (~GP_STROKE_CYCLIC); + /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gpd, gps); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 680b313c47b..d734fb2678e 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4356,6 +4356,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); const float length = RNA_float_get(op->ptr, "length"); + const float sharp_threshold = RNA_float_get(op->ptr, "sharp_threshold"); /* sanity checks */ if (ELEM(NULL, gpd)) { @@ -4365,7 +4366,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op) /* Go through each editable + selected stroke */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_stroke_sample(gpd, gps, length, true); + BKE_gpencil_stroke_sample(gpd, gps, length, true, sharp_threshold); } } GP_EDITABLE_STROKES_END(gpstroke_iter); diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c index 10be8c3e91e..735759d40ec 100644 --- a/source/blender/editors/gpencil/gpencil_trace_utils.c +++ b/source/blender/editors/gpencil/gpencil_trace_utils.c @@ -314,7 +314,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, if (sample > 0.0f) { /* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because * the sample function already call that. */ - BKE_gpencil_stroke_sample(gpd, gps, sample, false); + BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0); } else { BKE_gpencil_stroke_geometry_update(gpd, gps); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index 3b7b82e3085..365b9afe348 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -88,7 +88,7 @@ static void deformStroke(GpencilModifierData *md, break; } case GP_SIMPLIFY_SAMPLE: { - BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false); + BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false, mmd->sharp_threshold); break; } case GP_SIMPLIFY_MERGE: { @@ -143,6 +143,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) } else if (mode == GP_SIMPLIFY_SAMPLE) { uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "sharp_threshold", 0, NULL, ICON_NONE); } else if (mode == GP_SIMPLIFY_MERGE) { uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index 3c4676beb75..cc3eab02e07 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -196,7 +196,7 @@ void GpencilExporterPDF::export_gpencil_layers() /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false); + BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0); } export_stroke_to_polyline(gpl, gps_perimeter, is_stroke, false, false); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc index d21af4c09eb..f8d30546e39 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc @@ -221,7 +221,7 @@ void GpencilExporterSVG::export_gpencil_layers() /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false); + BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0); } export_stroke_to_path(gpl, gps_perimeter, node_gpl, false); diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 64099de5a5e..e30dd4e1c8f 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -649,9 +649,10 @@ typedef struct SimplifyGpencilModifierData { int layer_pass; /** Sample length */ float length; + /** Sample sharp threshold */ + 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 9c755bbb053..a619d179a33 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1223,6 +1223,14 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Length", "Length of each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "sharp_threshold"); + RNA_def_property_range(prop, 0, M_PI); + RNA_def_property_ui_range(prop, 0, M_PI, 1.0, 1); + RNA_def_property_ui_text( + prop, "Sharp Threshold", "Preserve corners that have sharper angle than this threshold"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + /* Merge */ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "distance"); |