GPencil: Improve Thickness handling for Outline operator

Actually, when you increase the thickness of the stroke in the outline conversion, the shape of the stroke changes and becomes thicker.

This commit includes a new algorithm to correct this problem. A new `Keep Shape`  parameter allows you to disable it because, for artist reasons, it may be good to keep the old algorithm and change the shape.
This commit is contained in:
Antonio Vazquez 2022-08-30 17:03:13 +02:00
parent 9cfa74087e
commit 38cf0d7d13
7 changed files with 27 additions and 10 deletions

View File

@ -488,7 +488,8 @@ struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *r
const struct bGPDlayer *gpl,
struct bGPDstroke *gps,
int subdivisions,
const float diff_mat[4][4]);
const float diff_mat[4][4],
const float thickness_chg);
/**
* Get average pressure.
*/

View File

@ -3960,6 +3960,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
const bGPDlayer *gpl,
const bGPDstroke *gps,
int subdivisions,
const float thickness_chg,
int *r_num_perimeter_points)
{
/* sanity check */
@ -3968,7 +3969,9 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
}
float defaultpixsize = 1000.0f / gpd->pixfactor;
float ovr_radius = thickness_chg / defaultpixsize / 2.0f;
float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
stroke_radius = max_ff(stroke_radius - ovr_radius, 0.0f);
ListBase *perimeter_right_side = MEM_cnew<ListBase>(__func__);
ListBase *perimeter_left_side = MEM_cnew<ListBase>(__func__);
@ -4202,7 +4205,8 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
const bGPDlayer *gpl,
bGPDstroke *gps,
const int subdivisions,
const float diff_mat[4][4])
const float diff_mat[4][4],
const float thickness_chg)
{
if (gps->totpoints == 0) {
return nullptr;
@ -4234,7 +4238,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
BKE_gpencil_stroke_to_view_space(rv3d, gps_temp, diff_mat);
int num_perimeter_points = 0;
ListBase *perimeter_points = gpencil_stroke_perimeter_ex(
gpd, gpl, gps_temp, subdivisions, &num_perimeter_points);
gpd, gpl, gps_temp, subdivisions, thickness_chg, &num_perimeter_points);
if (num_perimeter_points == 0) {
return nullptr;

View File

@ -3987,6 +3987,7 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (bGPdata *)ob->data;
const int subdivisions = RNA_int_get(op->ptr, "subdivisions");
const float length = RNA_float_get(op->ptr, "length");
const bool keep = RNA_boolean_get(op->ptr, "keep");
const int thickness = RNA_int_get(op->ptr, "thickness");
const int view_mode = RNA_enum_get(op->ptr, "view_mode");
@ -4104,8 +4105,9 @@ static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op)
CLAMP_MIN(gps_duplicate->thickness, 1.0f);
/* Stroke. */
const float ovr_thickness = keep ? thickness : 0.0f;
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
rv3d, gpd, gpl, gps_duplicate, subdivisions, diff_mat);
rv3d, gpd, gpl, gps_duplicate, subdivisions, diff_mat, ovr_thickness);
gps_perimeter->flag &= ~GP_STROKE_SELECT;
/* Assign material. */
switch (material_mode) {
@ -4216,8 +4218,12 @@ void GPENCIL_OT_stroke_outline(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_enum(ot->srna, "view_mode", view_mode, GP_PERIMETER_VIEW, "View", "");
RNA_def_enum(
ot->srna, "material_mode", material_mode, GP_STROKE_USE_ACTIVE_MATERIAL, "Material Mode", "");
RNA_def_enum(ot->srna,
"material_mode",
material_mode,
GP_STROKE_USE_ACTIVE_MATERIAL,
"Material Mode",
"");
RNA_def_int(ot->srna,
"thickness",
@ -4228,6 +4234,12 @@ void GPENCIL_OT_stroke_outline(wmOperatorType *ot)
"Thickness of the stroke perimeter",
1,
1000);
RNA_def_boolean(ot->srna,
"keep",
true,
"Keep Shape",
"Try to keep global shape when the stroke thickness change");
RNA_def_int(ot->srna, "subdivisions", 3, 0, 10, "Subdivisions", "", 0, 10);
RNA_def_float(ot->srna, "length", 0.0f, 0.0f, 100.0f, "Sample Length", "", 0.0f, 100.0f);

View File

@ -944,7 +944,7 @@ static bGPDstroke *gpencil_stroke_to_outline(tGPsdata *p, bGPDstroke *gps)
float diff_mat[4][4];
unit_m4(diff_mat);
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
rv3d, p->gpd, gpl, gps_duplicate, 3, diff_mat);
rv3d, p->gpd, gpl, gps_duplicate, 3, diff_mat, 0.0f);
/* Assign material. */
if (gpencil_settings->material_alt == NULL) {
gps_perimeter->mat_nr = gps->mat_nr;

View File

@ -257,7 +257,7 @@ float GpencilIO::stroke_point_radius_get(bGPDlayer *gpl, bGPDstroke *gps)
/* Radius. */
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
rv3d_, gpd_, gpl, gps, 3, diff_mat_.values);
rv3d_, gpd_, gpl, gps, 3, diff_mat_.values, 0.0f);
pt = &gps_perimeter->points[0];
const float2 screen_ex = gpencil_3D_point_to_2D(&pt->x);

View File

@ -192,7 +192,7 @@ void GpencilExporterPDF::export_gpencil_layers()
}
else {
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f);
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {

View File

@ -217,7 +217,7 @@ void GpencilExporterSVG::export_gpencil_layers()
}
else {
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f);
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {