WIP: GPencil: Improve simplify modifier #104521
|
@ -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,
|
void BKE_gpencil_stroke_simplify_adaptive(struct bGPdata *gpd,
|
||||||
struct bGPDstroke *gps,
|
struct bGPDstroke *gps,
|
||||||
float epsilon);
|
float epsilon,
|
||||||
|
float fac_position,
|
||||||
|
float fac_thickness,
|
||||||
|
float fac_strength);
|
||||||
/**
|
/**
|
||||||
* Simplify alternate vertex of stroke except extremes.
|
* Simplify alternate vertex of stroke except extremes.
|
||||||
* \param gpd: Grease pencil data-block
|
* \param gpd: Grease pencil data-block
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_array.hh"
|
||||||
#include "BLI_array_utils.h"
|
#include "BLI_array_utils.h"
|
||||||
#include "BLI_blenlib.h"
|
#include "BLI_blenlib.h"
|
||||||
#include "BLI_ghash.h"
|
#include "BLI_ghash.h"
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include "BLI_math_vector_types.hh"
|
#include "BLI_math_vector_types.hh"
|
||||||
#include "BLI_polyfill_2d.h"
|
#include "BLI_polyfill_2d.h"
|
||||||
#include "BLI_span.hh"
|
#include "BLI_span.hh"
|
||||||
|
#include "BLI_stack.hh"
|
||||||
|
|
||||||
#include "DNA_gpencil_modifier_types.h"
|
#include "DNA_gpencil_modifier_types.h"
|
||||||
#include "DNA_gpencil_types.h"
|
#include "DNA_gpencil_types.h"
|
||||||
|
@ -48,8 +50,12 @@
|
||||||
|
|
||||||
#include "DEG_depsgraph_query.h"
|
#include "DEG_depsgraph_query.h"
|
||||||
|
|
||||||
|
using blender::Array;
|
||||||
|
using blender::float2;
|
||||||
using blender::float3;
|
using blender::float3;
|
||||||
|
using blender::MutableSpan;
|
||||||
using blender::Span;
|
using blender::Span;
|
||||||
|
using blender::Stack;
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/** \name Grease Pencil Object: Bound-box Support
|
/** \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<bGPDspoint> points,
|
||||||
|
int start_idx,
|
||||||
|
int end_idx,
|
||||||
|
float epsilon,
|
||||||
|
float fac_position,
|
||||||
|
float fac_thickness,
|
||||||
|
float fac_strength,
|
||||||
|
MutableSpan<bool> r_marked_keep)
|
||||||
|
{
|
||||||
|
Stack<std::pair<int, int>> 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
|
/** \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);
|
bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
|
||||||
|
|
||||||
int totpoints = gps->totpoints;
|
int totpoints = gps->totpoints;
|
||||||
char *marked = nullptr;
|
|
||||||
char work;
|
|
||||||
|
|
||||||
int start = 0;
|
Span<bGPDspoint> points(old_points, totpoints);
|
||||||
int end = gps->totpoints - 1;
|
|
||||||
|
|
||||||
marked = (char *)MEM_callocN(totpoints, "GP marked array");
|
Array<bool> marked_as_keep(totpoints, true);
|
||||||
marked[start] = 1;
|
simplify_point_buffer(points,
|
||||||
marked[end] = 1;
|
0,
|
||||||
|
totpoints - 1,
|
||||||
work = 1;
|
epsilon,
|
||||||
int totmarked = 0;
|
fac_position,
|
||||||
/* while still reducing */
|
fac_thickness,
|
||||||
while (work) {
|
fac_strength,
|
||||||
int ls, le;
|
marked_as_keep);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* adding points marked */
|
/* adding points marked */
|
||||||
MDeformVert *old_dvert = nullptr;
|
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);
|
old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
|
||||||
}
|
}
|
||||||
/* resize gps */
|
/* resize gps */
|
||||||
int j = 0;
|
int count = 0;
|
||||||
for (int i = 0; i < totpoints; i++) {
|
for (int i = 0; i < totpoints; i++) {
|
||||||
bGPDspoint *pt_src = &old_points[i];
|
bGPDspoint *pt_src = &old_points[i];
|
||||||
bGPDspoint *pt = &gps->points[j];
|
bGPDspoint *pt = &gps->points[count];
|
||||||
|
if (marked_as_keep[i]) {
|
||||||
if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
|
|
||||||
*pt = blender::dna::shallow_copy(*pt_src);
|
*pt = blender::dna::shallow_copy(*pt_src);
|
||||||
if (gps->dvert != nullptr) {
|
if (gps->dvert != nullptr) {
|
||||||
dvert_src = &old_dvert[i];
|
dvert_src = &old_dvert[i];
|
||||||
MDeformVert *dvert = &gps->dvert[j];
|
MDeformVert *dvert = &gps->dvert[count];
|
||||||
memcpy(dvert, dvert_src, sizeof(MDeformVert));
|
memcpy(dvert, dvert_src, sizeof(MDeformVert));
|
||||||
if (dvert_src->dw) {
|
if (dvert_src->dw) {
|
||||||
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
|
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
j++;
|
count++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (gps->dvert != nullptr) {
|
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. */
|
/* Calc geometry data. */
|
||||||
BKE_gpencil_stroke_geometry_update(gpd, gps);
|
BKE_gpencil_stroke_geometry_update(gpd, gps);
|
||||||
|
|
||||||
MEM_SAFE_FREE(old_points);
|
MEM_SAFE_FREE(old_points);
|
||||||
MEM_SAFE_FREE(old_dvert);
|
MEM_SAFE_FREE(old_dvert);
|
||||||
MEM_SAFE_FREE(marked);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
|
void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
|
||||||
|
|
|
@ -4748,7 +4748,7 @@ static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
|
||||||
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
|
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
|
||||||
if (gps->flag & GP_STROKE_SELECT) {
|
if (gps->flag & GP_STROKE_SELECT) {
|
||||||
/* simplify stroke using Ramer-Douglas-Peucker algorithm */
|
/* 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;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1280,7 +1280,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
|
||||||
/* Simplify adaptive */
|
/* Simplify adaptive */
|
||||||
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
|
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
|
||||||
(brush->gpencil_settings->simplify_f > 0.0f)) {
|
(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. */
|
/* Set material index. */
|
||||||
|
|
|
@ -85,7 +85,8 @@ static void deformStroke(GpencilModifierData *md,
|
||||||
}
|
}
|
||||||
case GP_SIMPLIFY_ADAPTIVE: {
|
case GP_SIMPLIFY_ADAPTIVE: {
|
||||||
/* simplify stroke using Ramer-Douglas-Peucker algorithm */
|
/* 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;
|
break;
|
||||||
}
|
}
|
||||||
case GP_SIMPLIFY_SAMPLE: {
|
case GP_SIMPLIFY_SAMPLE: {
|
||||||
|
@ -133,6 +134,9 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||||
}
|
}
|
||||||
else if (mode == GP_SIMPLIFY_ADAPTIVE) {
|
else if (mode == GP_SIMPLIFY_ADAPTIVE) {
|
||||||
uiItemR(layout, ptr, "factor", 0, NULL, ICON_NONE);
|
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) {
|
else if (mode == GP_SIMPLIFY_SAMPLE) {
|
||||||
uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE);
|
uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE);
|
||||||
|
|
|
@ -198,6 +198,9 @@
|
||||||
.pass_index = 0, \
|
.pass_index = 0, \
|
||||||
.flag = 0, \
|
.flag = 0, \
|
||||||
.factor = 0.0f, \
|
.factor = 0.0f, \
|
||||||
|
.fac_position = 10.0f, \
|
||||||
|
.fac_thickness = 0.0f, \
|
||||||
|
.fac_strength = 0.0f, \
|
||||||
.mode = 0, \
|
.mode = 0, \
|
||||||
.step = 1, \
|
.step = 1, \
|
||||||
.layer_pass = 0, \
|
.layer_pass = 0, \
|
||||||
|
|
|
@ -742,6 +742,9 @@ typedef struct SimplifyGpencilModifierData {
|
||||||
int flag;
|
int flag;
|
||||||
/** Factor of simplify. */
|
/** Factor of simplify. */
|
||||||
float factor;
|
float factor;
|
||||||
|
float fac_position;
|
||||||
|
float fac_thickness;
|
||||||
|
float fac_strength;
|
||||||
/** Type of simplify. */
|
/** Type of simplify. */
|
||||||
short mode;
|
short mode;
|
||||||
/** Every n vertex to keep. */
|
/** Every n vertex to keep. */
|
||||||
|
@ -754,6 +757,7 @@ typedef struct SimplifyGpencilModifierData {
|
||||||
float sharp_threshold;
|
float sharp_threshold;
|
||||||
/** Merge distance */
|
/** Merge distance */
|
||||||
float distance;
|
float distance;
|
||||||
|
char _pad[4];
|
||||||
} SimplifyGpencilModifierData;
|
} SimplifyGpencilModifierData;
|
||||||
|
|
||||||
typedef enum eSimplifyGpencil_Flag {
|
typedef enum eSimplifyGpencil_Flag {
|
||||||
|
|
|
@ -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_ui_text(prop, "Factor", "Factor of Simplify");
|
||||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
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);
|
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
|
||||||
RNA_def_property_int_sdna(prop, NULL, "pass_index");
|
RNA_def_property_int_sdna(prop, NULL, "pass_index");
|
||||||
RNA_def_property_range(prop, 0, 100);
|
RNA_def_property_range(prop, 0, 100);
|
||||||
|
|
Loading…
Reference in New Issue