GPencil: New Simplify modifier mode Sample and operator
This mode simplify the stroke doing a resampling of the points and generate new geometry at the distance defined. Sample function developed by @NicksBest New Resample Stroke operator This operator recreates the stroke geometry with a predefined length between points. The operator uses the same code used in Simplify modifier. Reviewers: @mendio
This commit is contained in:
parent
5ca3bc7a14
commit
179e886ab3
|
@ -1785,13 +1785,13 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
|
||||||
|
|
||||||
col = split.column()
|
col = split.column()
|
||||||
col.label(text="Settings:")
|
col.label(text="Settings:")
|
||||||
row = col.row(align=True)
|
|
||||||
row.enabled = md.mode == 'FIXED'
|
|
||||||
row.prop(md, "step")
|
|
||||||
|
|
||||||
row = col.row(align=True)
|
if md.mode == 'FIXED':
|
||||||
row.enabled = not md.mode == 'FIXED'
|
col.prop(md, "step")
|
||||||
row.prop(md, "factor")
|
elif md.mode == 'ADAPTIVE':
|
||||||
|
col.prop(md, "factor")
|
||||||
|
elif md.mode == 'SAMPLE':
|
||||||
|
col.prop(md, "length")
|
||||||
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
|
@ -4447,6 +4447,7 @@ class VIEW3D_MT_gpencil_simplify(Menu):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.operator("gpencil.stroke_simplify_fixed", text="Fixed")
|
layout.operator("gpencil.stroke_simplify_fixed", text="Fixed")
|
||||||
layout.operator("gpencil.stroke_simplify", text="Adaptive")
|
layout.operator("gpencil.stroke_simplify", text="Adaptive")
|
||||||
|
layout.operator("gpencil.stroke_sample", text="Sample")
|
||||||
|
|
||||||
|
|
||||||
class VIEW3D_MT_paint_gpencil(Menu):
|
class VIEW3D_MT_paint_gpencil(Menu):
|
||||||
|
@ -6322,8 +6323,7 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu):
|
||||||
if is_3d_view:
|
if is_3d_view:
|
||||||
layout.menu("GPENCIL_MT_cleanup")
|
layout.menu("GPENCIL_MT_cleanup")
|
||||||
|
|
||||||
layout.operator("gpencil.stroke_simplify_fixed", text="Simplify")
|
layout.menu("VIEW3D_MT_gpencil_simplify")
|
||||||
layout.operator("gpencil.stroke_simplify", text="Simplify Adaptive")
|
|
||||||
layout.operator("gpencil.stroke_merge", text="Merge")
|
layout.operator("gpencil.stroke_merge", text="Merge")
|
||||||
layout.menu("VIEW3D_MT_edit_gpencil_delete")
|
layout.menu("VIEW3D_MT_edit_gpencil_delete")
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,7 @@ void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points,
|
||||||
|
|
||||||
void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]);
|
void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]);
|
||||||
|
|
||||||
|
bool BKE_gpencil_sample_stroke(struct bGPDstroke *gps, const float dist, const bool select);
|
||||||
bool BKE_gpencil_smooth_stroke(struct bGPDstroke *gps, int i, float inf);
|
bool BKE_gpencil_smooth_stroke(struct bGPDstroke *gps, int i, float inf);
|
||||||
bool BKE_gpencil_smooth_stroke_strength(struct bGPDstroke *gps, int point_index, float influence);
|
bool BKE_gpencil_smooth_stroke_strength(struct bGPDstroke *gps, int point_index, float influence);
|
||||||
bool BKE_gpencil_smooth_stroke_thickness(struct bGPDstroke *gps, int point_index, float influence);
|
bool BKE_gpencil_smooth_stroke_thickness(struct bGPDstroke *gps, int point_index, float influence);
|
||||||
|
|
|
@ -1404,6 +1404,324 @@ void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
|
||||||
|
|
||||||
/* ************************************************** */
|
/* ************************************************** */
|
||||||
|
|
||||||
|
static void stroke_defvert_create_nr_list(MDeformVert *dv_list,
|
||||||
|
int count,
|
||||||
|
ListBase *result,
|
||||||
|
int *totweight)
|
||||||
|
{
|
||||||
|
LinkData *ld;
|
||||||
|
MDeformVert *dv;
|
||||||
|
MDeformWeight *dw;
|
||||||
|
int i, j;
|
||||||
|
int tw = 0;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
dv = &dv_list[i];
|
||||||
|
|
||||||
|
/* find def_nr in list, if not exist, then create one */
|
||||||
|
for (j = 0; j < dv->totweight; j++) {
|
||||||
|
int found = 0;
|
||||||
|
dw = &dv->dw[j];
|
||||||
|
for (ld = result->first; ld; ld = ld->next) {
|
||||||
|
if (ld->data == (void *)dw->def_nr) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
ld = MEM_callocN(sizeof(LinkData), "def_nr_item");
|
||||||
|
ld->data = (void *)dw->def_nr;
|
||||||
|
BLI_addtail(result, ld);
|
||||||
|
tw++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*totweight = tw;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
LinkData *ld;
|
||||||
|
MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
|
||||||
|
|
||||||
|
dst->totweight = totweight;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight");
|
||||||
|
j = 0;
|
||||||
|
/* re-assign deform groups */
|
||||||
|
for (ld = def_nr_list->first; ld; ld = ld->next) {
|
||||||
|
dst[i].dw[j].def_nr = (int)ld->data;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float stroke_defvert_get_nr_weight(MDeformVert *dv, int def_nr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < dv->totweight; i++) {
|
||||||
|
if (dv->dw[i].def_nr == def_nr) {
|
||||||
|
return dv->dw[i].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stroke_interpolate_deform_weights(
|
||||||
|
bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
|
||||||
|
{
|
||||||
|
MDeformVert *vl = &gps->dvert[index_from];
|
||||||
|
MDeformVert *vr = &gps->dvert[index_to];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < vert->totweight; i++) {
|
||||||
|
float wl = stroke_defvert_get_nr_weight(vl, vert->dw[i].def_nr);
|
||||||
|
float wr = stroke_defvert_get_nr_weight(vr, vert->dw[i].def_nr);
|
||||||
|
vert->dw[i].weight = interpf(wr, wl, ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stroke_march_next_point(const bGPDstroke *gps,
|
||||||
|
const int index_next_pt,
|
||||||
|
const float *current,
|
||||||
|
const float dist,
|
||||||
|
float *result,
|
||||||
|
float *pressure,
|
||||||
|
float *strength,
|
||||||
|
float *ratio_result,
|
||||||
|
int *index_from,
|
||||||
|
int *index_to)
|
||||||
|
{
|
||||||
|
float remaining_till_next = 0.0f;
|
||||||
|
float remaining_march = dist;
|
||||||
|
float step_start[3];
|
||||||
|
float point[3];
|
||||||
|
int next_point_index = index_next_pt;
|
||||||
|
bGPDspoint *pt = NULL;
|
||||||
|
|
||||||
|
if (!(next_point_index < gps->totpoints)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_v3_v3(step_start, current);
|
||||||
|
pt = &gps->points[next_point_index];
|
||||||
|
copy_v3_v3(point, &pt->x);
|
||||||
|
remaining_till_next = len_v3v3(point, step_start);
|
||||||
|
|
||||||
|
while (remaining_till_next < remaining_march) {
|
||||||
|
remaining_march -= remaining_till_next;
|
||||||
|
pt = &gps->points[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;
|
||||||
|
}
|
||||||
|
pt = &gps->points[next_point_index];
|
||||||
|
copy_v3_v3(point, &pt->x);
|
||||||
|
remaining_till_next = len_v3v3(point, step_start);
|
||||||
|
}
|
||||||
|
if (remaining_till_next < remaining_march) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
*index_from = next_point_index - 1;
|
||||||
|
*index_to = next_point_index;
|
||||||
|
*ratio_result = 1.0f;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float ratio = remaining_march / remaining_till_next;
|
||||||
|
interp_v3_v3v3(result, step_start, point, ratio);
|
||||||
|
*pressure = interpf(
|
||||||
|
gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
|
||||||
|
*strength = interpf(
|
||||||
|
gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
|
||||||
|
|
||||||
|
*index_from = next_point_index - 1;
|
||||||
|
*index_to = next_point_index;
|
||||||
|
*ratio_result = ratio;
|
||||||
|
|
||||||
|
return 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,
|
||||||
|
float *result)
|
||||||
|
{
|
||||||
|
float remaining_till_next = 0.0f;
|
||||||
|
float remaining_march = dist;
|
||||||
|
float step_start[3];
|
||||||
|
float point[3];
|
||||||
|
int next_point_index = index_next_pt;
|
||||||
|
bGPDspoint *pt = NULL;
|
||||||
|
|
||||||
|
if (!(next_point_index < gps->totpoints)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_v3_v3(step_start, current);
|
||||||
|
pt = &gps->points[next_point_index];
|
||||||
|
copy_v3_v3(point, &pt->x);
|
||||||
|
remaining_till_next = len_v3v3(point, step_start);
|
||||||
|
|
||||||
|
while (remaining_till_next < remaining_march) {
|
||||||
|
remaining_march -= remaining_till_next;
|
||||||
|
pt = &gps->points[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;
|
||||||
|
}
|
||||||
|
pt = &gps->points[next_point_index];
|
||||||
|
copy_v3_v3(point, &pt->x);
|
||||||
|
remaining_till_next = len_v3v3(point, step_start);
|
||||||
|
}
|
||||||
|
if (remaining_till_next < remaining_march) {
|
||||||
|
pt = &gps->points[next_point_index];
|
||||||
|
copy_v3_v3(result, &pt->x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float ratio = remaining_march / remaining_till_next;
|
||||||
|
interp_v3_v3v3(result, step_start, point, ratio);
|
||||||
|
return next_point_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stroke_march_count(const bGPDstroke *gps, const float dist)
|
||||||
|
{
|
||||||
|
int point_count = 0;
|
||||||
|
float point[3];
|
||||||
|
int next_point_index = 1;
|
||||||
|
bGPDspoint *pt = NULL;
|
||||||
|
|
||||||
|
pt = &gps->points[0];
|
||||||
|
copy_v3_v3(point, &pt->x);
|
||||||
|
point_count++;
|
||||||
|
|
||||||
|
while ((next_point_index = stroke_march_next_point_no_interp(
|
||||||
|
gps, next_point_index, point, dist, point)) > -1) {
|
||||||
|
point_count++;
|
||||||
|
if (next_point_index == 0) {
|
||||||
|
break; /* last point finished */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return point_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resample a stroke
|
||||||
|
* \param gps: Stroke to sample
|
||||||
|
* \param dist: Distance of one segment
|
||||||
|
*/
|
||||||
|
bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool select)
|
||||||
|
{
|
||||||
|
bGPDspoint *pt = gps->points;
|
||||||
|
bGPDspoint *pt1 = NULL;
|
||||||
|
bGPDspoint *pt2 = NULL;
|
||||||
|
int i;
|
||||||
|
LinkData *ld;
|
||||||
|
ListBase def_nr_list = {0};
|
||||||
|
|
||||||
|
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* TODO: Implement feature point preservation. */
|
||||||
|
int count = stroke_march_count(gps, dist);
|
||||||
|
|
||||||
|
bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled");
|
||||||
|
MDeformVert *new_dv = NULL;
|
||||||
|
|
||||||
|
int result_totweight;
|
||||||
|
|
||||||
|
if (gps->dvert != NULL) {
|
||||||
|
stroke_defvert_create_nr_list(gps->dvert, count, &def_nr_list, &result_totweight);
|
||||||
|
new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
int next_point_index = 1;
|
||||||
|
i = 0;
|
||||||
|
float pressure, strength, ratio_result;
|
||||||
|
int index_from, index_to;
|
||||||
|
float last_coord[3];
|
||||||
|
|
||||||
|
/* 1st point is always at the start */
|
||||||
|
pt1 = &gps->points[0];
|
||||||
|
copy_v3_v3(last_coord, &pt1->x);
|
||||||
|
pt2 = &new_pt[i];
|
||||||
|
copy_v3_v3(&pt2->x, last_coord);
|
||||||
|
new_pt[i].pressure = pt[0].pressure;
|
||||||
|
new_pt[i].strength = pt[0].strength;
|
||||||
|
if (select) {
|
||||||
|
new_pt[i].flag |= GP_SPOINT_SELECT;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (new_dv) {
|
||||||
|
stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the rest */
|
||||||
|
while ((next_point_index = stroke_march_next_point(gps,
|
||||||
|
next_point_index,
|
||||||
|
last_coord,
|
||||||
|
dist,
|
||||||
|
last_coord,
|
||||||
|
&pressure,
|
||||||
|
&strength,
|
||||||
|
&ratio_result,
|
||||||
|
&index_from,
|
||||||
|
&index_to)) > -1) {
|
||||||
|
pt2 = &new_pt[i];
|
||||||
|
copy_v3_v3(&pt2->x, last_coord);
|
||||||
|
new_pt[i].pressure = pressure;
|
||||||
|
new_pt[i].strength = strength;
|
||||||
|
if (select) {
|
||||||
|
new_pt[i].flag |= GP_SPOINT_SELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_dv) {
|
||||||
|
stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
if (next_point_index == 0) {
|
||||||
|
break; /* last point finished */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gps->points = new_pt;
|
||||||
|
gps->totpoints = i;
|
||||||
|
MEM_freeN(pt); /* original */
|
||||||
|
|
||||||
|
if (new_dv) {
|
||||||
|
BKE_gpencil_free_stroke_weights(gps);
|
||||||
|
while (ld = BLI_pophead(&def_nr_list)) {
|
||||||
|
MEM_freeN(ld);
|
||||||
|
}
|
||||||
|
gps->dvert = new_dv;
|
||||||
|
}
|
||||||
|
|
||||||
|
gps->flag |= GP_STROKE_RECALC_GEOMETRY;
|
||||||
|
gps->tot_triangles = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply smooth to stroke point
|
* Apply smooth to stroke point
|
||||||
* \param gps: Stroke to smooth
|
* \param gps: Stroke to smooth
|
||||||
|
|
|
@ -3847,6 +3847,54 @@ void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot)
|
||||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ** Resample stroke *** */
|
||||||
|
static int gp_stroke_sample_exec(bContext *C, wmOperator *op)
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||||
|
const float length = RNA_float_get(op->ptr, "length");
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (ELEM(NULL, gpd)) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go through each editable + selected stroke */
|
||||||
|
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
|
||||||
|
if (gps->flag & GP_STROKE_SELECT) {
|
||||||
|
BKE_gpencil_sample_stroke(gps, length, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GP_EDITABLE_STROKES_END(gpstroke_iter);
|
||||||
|
|
||||||
|
/* 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_sample(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Sample Stroke";
|
||||||
|
ot->idname = "GPENCIL_OT_stroke_sample";
|
||||||
|
ot->description = "Sample stroke points to predefined segment length";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = gp_stroke_sample_exec;
|
||||||
|
ot->poll = gp_active_layer_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
/* properties */
|
||||||
|
prop = RNA_def_float(ot->srna, "length", 0.1f, 0.0f, 100.0f, "Length", "", 0.0f, 100.0f);
|
||||||
|
/* avoid re-using last var */
|
||||||
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||||
|
}
|
||||||
|
|
||||||
/* ******************* Stroke trim ************************** */
|
/* ******************* Stroke trim ************************** */
|
||||||
static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
|
static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
|
||||||
{
|
{
|
||||||
|
|
|
@ -486,6 +486,7 @@ void GPENCIL_OT_stroke_simplify_fixed(struct wmOperatorType *ot);
|
||||||
void GPENCIL_OT_stroke_separate(struct wmOperatorType *ot);
|
void GPENCIL_OT_stroke_separate(struct wmOperatorType *ot);
|
||||||
void GPENCIL_OT_stroke_split(struct wmOperatorType *ot);
|
void GPENCIL_OT_stroke_split(struct wmOperatorType *ot);
|
||||||
void GPENCIL_OT_stroke_smooth(struct wmOperatorType *ot);
|
void GPENCIL_OT_stroke_smooth(struct wmOperatorType *ot);
|
||||||
|
void GPENCIL_OT_stroke_sample(struct wmOperatorType *ot);
|
||||||
void GPENCIL_OT_stroke_merge(struct wmOperatorType *ot);
|
void GPENCIL_OT_stroke_merge(struct wmOperatorType *ot);
|
||||||
void GPENCIL_OT_stroke_cutter(struct wmOperatorType *ot);
|
void GPENCIL_OT_stroke_cutter(struct wmOperatorType *ot);
|
||||||
void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot);
|
void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot);
|
||||||
|
|
|
@ -314,6 +314,7 @@ void ED_operatortypes_gpencil(void)
|
||||||
WM_operatortype_append(GPENCIL_OT_stroke_separate);
|
WM_operatortype_append(GPENCIL_OT_stroke_separate);
|
||||||
WM_operatortype_append(GPENCIL_OT_stroke_split);
|
WM_operatortype_append(GPENCIL_OT_stroke_split);
|
||||||
WM_operatortype_append(GPENCIL_OT_stroke_smooth);
|
WM_operatortype_append(GPENCIL_OT_stroke_smooth);
|
||||||
|
WM_operatortype_append(GPENCIL_OT_stroke_sample);
|
||||||
WM_operatortype_append(GPENCIL_OT_stroke_merge);
|
WM_operatortype_append(GPENCIL_OT_stroke_merge);
|
||||||
WM_operatortype_append(GPENCIL_OT_stroke_cutter);
|
WM_operatortype_append(GPENCIL_OT_stroke_cutter);
|
||||||
WM_operatortype_append(GPENCIL_OT_stroke_trim);
|
WM_operatortype_append(GPENCIL_OT_stroke_trim);
|
||||||
|
|
|
@ -66,7 +66,7 @@ static void deformStroke(GpencilModifierData *md,
|
||||||
mmd->layername,
|
mmd->layername,
|
||||||
mmd->pass_index,
|
mmd->pass_index,
|
||||||
mmd->layer_pass,
|
mmd->layer_pass,
|
||||||
4,
|
mmd->mode == GP_SIMPLIFY_SAMPLE ? 3 : 4,
|
||||||
gpl,
|
gpl,
|
||||||
gps,
|
gps,
|
||||||
mmd->flag & GP_SIMPLIFY_INVERT_LAYER,
|
mmd->flag & GP_SIMPLIFY_INVERT_LAYER,
|
||||||
|
@ -75,14 +75,25 @@ static void deformStroke(GpencilModifierData *md,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mmd->mode == GP_SIMPLIFY_FIXED) {
|
/* Select simplification mode. */
|
||||||
for (int i = 0; i < mmd->step; i++) {
|
switch (mmd->mode) {
|
||||||
BKE_gpencil_simplify_fixed(gps);
|
case GP_SIMPLIFY_FIXED: {
|
||||||
|
for (int i = 0; i < mmd->step; i++) {
|
||||||
|
BKE_gpencil_simplify_fixed(gps);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
case GP_SIMPLIFY_ADAPTIVE: {
|
||||||
else {
|
/* simplify stroke using Ramer-Douglas-Peucker algorithm */
|
||||||
/* simplify stroke using Ramer-Douglas-Peucker algorithm */
|
BKE_gpencil_simplify_stroke(gps, mmd->factor);
|
||||||
BKE_gpencil_simplify_stroke(gps, mmd->factor);
|
break;
|
||||||
|
}
|
||||||
|
case GP_SIMPLIFY_SAMPLE: {
|
||||||
|
BKE_gpencil_sample_stroke(gps, mmd->length, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -502,7 +502,8 @@ typedef struct SimplifyGpencilModifierData {
|
||||||
short step;
|
short step;
|
||||||
/** Custom index for passes. */
|
/** Custom index for passes. */
|
||||||
int layer_pass;
|
int layer_pass;
|
||||||
char _pad[4];
|
/* Sample length */
|
||||||
|
float length;
|
||||||
} SimplifyGpencilModifierData;
|
} SimplifyGpencilModifierData;
|
||||||
|
|
||||||
typedef enum eSimplifyGpencil_Flag {
|
typedef enum eSimplifyGpencil_Flag {
|
||||||
|
@ -516,6 +517,8 @@ typedef enum eSimplifyGpencil_Mode {
|
||||||
GP_SIMPLIFY_FIXED = 0,
|
GP_SIMPLIFY_FIXED = 0,
|
||||||
/* Use RDP algorithm */
|
/* Use RDP algorithm */
|
||||||
GP_SIMPLIFY_ADAPTIVE = 1,
|
GP_SIMPLIFY_ADAPTIVE = 1,
|
||||||
|
/* Sample the stroke using a fixed length */
|
||||||
|
GP_SIMPLIFY_SAMPLE = 2,
|
||||||
} eSimplifyGpencil_Mode;
|
} eSimplifyGpencil_Mode;
|
||||||
|
|
||||||
typedef struct OffsetGpencilModifierData {
|
typedef struct OffsetGpencilModifierData {
|
||||||
|
|
|
@ -617,6 +617,11 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
|
||||||
ICON_IPO_EASE_IN_OUT,
|
ICON_IPO_EASE_IN_OUT,
|
||||||
"Adaptive",
|
"Adaptive",
|
||||||
"Use a RDP algorithm to simplify"},
|
"Use a RDP algorithm to simplify"},
|
||||||
|
{GP_SIMPLIFY_SAMPLE,
|
||||||
|
"SAMPLE",
|
||||||
|
ICON_IPO_EASE_IN_OUT,
|
||||||
|
"Sample",
|
||||||
|
"Sample a curve using a fixed length"},
|
||||||
{0, NULL, 0, NULL, NULL},
|
{0, NULL, 0, NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -675,6 +680,13 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
|
||||||
RNA_def_property_range(prop, 1, 50);
|
RNA_def_property_range(prop, 1, 50);
|
||||||
RNA_def_property_ui_text(prop, "Iterations", "Number of times to apply simplify");
|
RNA_def_property_ui_text(prop, "Iterations", "Number of times to apply simplify");
|
||||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
/* Sample */
|
||||||
|
prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "length");
|
||||||
|
RNA_def_property_range(prop, 0, 10);
|
||||||
|
RNA_def_property_ui_text(prop, "Length", "Length of each segment");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
|
static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
|
||||||
|
|
Loading…
Reference in New Issue