diff options
-rw-r--r-- | release/scripts/startup/bl_operators/presets.py | 1 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 1 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 2 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_gpencil.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/gpencil.c | 95 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 68 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_ops.c | 1 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_paint.c | 6 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 8 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 6 |
12 files changed, 190 insertions, 3 deletions
diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 1351dbbd42e..663de209c15 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -673,6 +673,7 @@ class AddPresetGpencilBrush(AddPresetBase, Operator): "settings.uv_random", "settings.pen_jitter", "settings.use_jitter_pressure", + "settings.trim", ] preset_subdir = "gpencil_brush" diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index b5937388634..2a4c4e94b0c 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -215,6 +215,7 @@ class GreasePencilStrokeEditPanel: row = col.row(align=True) row.operator("gpencil.stroke_simplify_fixed", text="Simplify") row.operator("gpencil.stroke_simplify", text="Adaptive") + row.operator("gpencil.stroke_trim", text="Trim") col.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 6a0ba6a713d..e73b2538bee 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -4007,6 +4007,7 @@ class VIEW3D_MT_edit_gpencil(Menu): layout.operator("gpencil.stroke_smooth", text="Smooth") layout.operator("gpencil.stroke_subdivide", text="Subdivide") layout.menu("VIEW3D_MT_gpencil_simplify") + layout.operator("gpencil.stroke_trim", text="Trim") layout.separator() @@ -5674,6 +5675,7 @@ class VIEW3D_MT_gpencil_edit_specials(Menu): layout.operator("gpencil.stroke_subdivide", text="Subdivide") layout.operator("gpencil.stroke_simplify_fixed", text="Simplify") layout.operator("gpencil.stroke_simplify", text="Simplify Adaptive") + layout.operator("gpencil.stroke_trim", text="Trim") layout.separator() layout.menu("GPENCIL_MT_separate", text="Separate") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 2ba84ac166e..254a893be52 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1599,6 +1599,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): col.prop(gp_settings, "pen_subdivision_steps") col.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) + col = layout.column(align=True) + col.prop(gp_settings, "trim") + class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): bl_context = ".greasepencil_paint" diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index a33f172b961..eb4101fa2f7 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -160,6 +160,7 @@ void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]); void BKE_gpencil_simplify_stroke(struct bGPDstroke *gps, float factor); void BKE_gpencil_simplify_fixed(struct bGPDstroke *gps); void BKE_gpencil_subdivide(struct bGPDstroke *gps, int level, int flag); +bool BKE_gpencil_trim_stroke(struct bGPDstroke *gps); void BKE_gpencil_stroke_2d_flat( const struct bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index ad41382f854..108192c5be9 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1700,3 +1700,98 @@ void BKE_gpencil_stroke_2d_flat_ref( /* Concave (-1), Convex (1), or Autodetect (0)? */ *r_direction = (int)locy[2]; } + +/** + * Trim stroke to the first intersection or loop + * \param gps: Stroke data + */ +bool BKE_gpencil_trim_stroke(bGPDstroke *gps) +{ + if (gps->totpoints < 4) { + return false; + } + bool intersect = false; + int start, end; + float point[3]; + /* loop segments from start until we have an intersection */ + for (int i = 0; i < gps->totpoints - 2; i++) { + start = i; + bGPDspoint *a = &gps->points[start]; + bGPDspoint *b = &gps->points[start + 1]; + for (int j = start + 2; j < gps->totpoints - 1; j++) { + end = j + 1; + bGPDspoint *c = &gps->points[j]; + bGPDspoint *d = &gps->points[end]; + float pointb[3]; + /* get intersection */ + if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) { + if (len_v3(point) > 0.0f) { + float closest[3]; + /* check intersection is on both lines */ + float lambda = closest_to_line_v3(closest, point, &a->x, &b->x); + if ((lambda <= 0.0f) || (lambda >= 1.0f)) { + continue; + } + lambda = closest_to_line_v3(closest, point, &c->x, &d->x); + if ((lambda <= 0.0f) || (lambda >= 1.0f)) { + continue; + } + else { + intersect = true; + break; + } + } + } + } + if (intersect) { + break; + } + } + + /* trim unwanted points */ + if (intersect) { + + /* save points */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *old_dvert = NULL; + MDeformVert *dvert_src = NULL; + + if (gps->dvert != NULL) { + old_dvert = MEM_dupallocN(gps->dvert); + } + + /* resize gps */ + int newtot = end - start + 1; + + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); + } + + for (int i = 0; i < newtot; i++) { + int idx = start + i; + bGPDspoint *pt_src = &old_points[idx]; + bGPDspoint *pt_new = &gps->points[i]; + memcpy(pt_new, pt_src, sizeof(bGPDspoint)); + if (gps->dvert != NULL) { + dvert_src = &old_dvert[idx]; + MDeformVert *dvert = &gps->dvert[i]; + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + if (dvert_src->dw) { + memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight)); + } + } + if (idx == start || idx == end) { + copy_v3_v3(&pt_new->x, point); + } + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + gps->totpoints = newtot; + + MEM_SAFE_FREE(old_points); + MEM_SAFE_FREE(old_dvert); + } + return intersect; +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index ebf2ca8c304..cfb32c5b5ef 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3562,6 +3562,74 @@ void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot) } +/* ******************* Stroke trim ************************** */ +static int gp_stroke_trim_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + BKE_gpencil_trim_stroke(gps); + } + } + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* 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_trim(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Trim Stroke"; + ot->idname = "GPENCIL_OT_stroke_trim"; + ot->description = "Trim selected stroke to first loop or intersection"; + + /* api callbacks */ + ot->exec = gp_stroke_trim_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ***************** Separate Strokes ********************** */ typedef enum eGP_SeparateModes { /* Points */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 971cc9e98be..9341133d520 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -467,6 +467,7 @@ void GPENCIL_OT_stroke_split(struct wmOperatorType *ot); void GPENCIL_OT_stroke_smooth(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge(struct wmOperatorType *ot); void GPENCIL_OT_stroke_cutter(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot); void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 67957e64e8f..60b2fb5d64e 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -310,6 +310,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_smooth); WM_operatortype_append(GPENCIL_OT_stroke_merge); WM_operatortype_append(GPENCIL_OT_stroke_cutter); + WM_operatortype_append(GPENCIL_OT_stroke_trim); WM_operatortype_append(GPENCIL_OT_brush_presets_create); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 731f85d1bea..63f1e495e6b 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1242,6 +1242,12 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } } + /* post process stroke */ + if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) { + BKE_gpencil_trim_stroke(gps); + } + gp_stroke_added_enable(p); } diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 7954dbf8501..584a43aad35 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -71,8 +71,7 @@ typedef struct BrushGpencilSettings { short draw_smoothlvl; /** Number of times to subdivide new strokes. */ short draw_subdivide; - /** Internal grease pencil drawing flags. */ - short flag; + short _pad; /** Number of times to apply thickness smooth factor to new strokes. */ short thick_smoothlvl; @@ -106,7 +105,8 @@ typedef struct BrushGpencilSettings { float era_strength_f; /** Factor to apply to thickness for soft eraser. */ float era_thickness_f; - char pad_2[4]; + /** Internal grease pencil drawing flags. */ + int flag; struct CurveMapping *curve_sensitivity; struct CurveMapping *curve_strength; @@ -147,6 +147,8 @@ typedef enum eGPDbrush_Flag { GP_BRUSH_DISSABLE_LASSO = (1 << 14), /* Do not erase strokes oLcluded */ GP_BRUSH_OCCLUDE_ERASER = (1 << 15), + /* Post process trim stroke */ + GP_BRUSH_TRIM_STROKE = (1 << 16), } eGPDbrush_Flag; /* BrushGpencilSettings->gp_fill_draw_mode */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index c35e5a95702..ed7d674e840 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1256,6 +1256,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to draw boundary limits"); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + prop = RNA_def_property(srna, "trim", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_TRIM_STROKE); + RNA_def_property_boolean_default(prop, false); + RNA_def_property_ui_text(prop, "Trim Stroke Ends", "Trim intersecting stroke ends"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + /* Material */ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Material"); |