diff options
-rw-r--r-- | source/blender/blenkernel/intern/gpencil_curve.c | 18 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 129 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_edit.c | 8 |
3 files changed, 140 insertions, 15 deletions
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 76901f91e34..f62bf72c490 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -563,6 +563,7 @@ void BKE_gpencil_convert_curve(Main *bmain, /** \name Editcurve kernel functions * \{ */ +/* Helper: generate curves with one or two curve points */ static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps, const float stroke_radius) { @@ -590,8 +591,8 @@ static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps, copy_v4_v4(cpt->vert_color, pt->vert_color); /* default handle type */ - bezt->h1 = HD_FREE; - bezt->h2 = HD_FREE; + bezt->h1 = HD_ALIGN; + bezt->h2 = HD_ALIGN; cpt->point_index = 0; @@ -624,10 +625,10 @@ static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps, copy_v4_v4(cpt->vert_color, pt->vert_color); /* default handle type */ - bezt->h1 = HD_VECT; - bezt->h2 = HD_VECT; + bezt->h1 = HD_ALIGN; + bezt->h2 = HD_ALIGN; - cpt->point_index = 0; + cpt->point_index = i; } return editcurve; @@ -755,7 +756,7 @@ bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps, */ void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) { - if (gps == NULL || gps->totpoints < 0) { + if (gps == NULL || gps->totpoints <= 0) { return; } @@ -768,9 +769,8 @@ void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstrok bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_generate( gps, gpd->curve_edit_threshold, gpd->curve_edit_corner_angle, stroke_radius); - if (editcurve == NULL) { - return; - } + + BLI_assert(editcurve != NULL); gps->editcurve = editcurve; } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e0adbfe578d..9f0271c2aa4 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -921,6 +921,98 @@ static void gpencil_duplicate_points(bGPdata *gpd, } } +static void gpencil_duplicate_selected_curve_points( + bGPdata *gpd, bGPDlayer *gpl, ListBase *new_strokes, bGPDstroke *gps, bGPDcurve *gpc) +{ + if (gpc == NULL) { + return; + } + const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC; + const int idx_last = gpc->tot_curve_points - 1; + bGPDstroke *gps_first = NULL; + bGPDstroke *gps_last = NULL; + + int idx_start = 0; + int idx_end = 0; + bool prev_selected = gpc->curve_points[0].flag & GP_CURVE_POINT_SELECT; + for (int i = 1; i < gpc->tot_curve_points; i++) { + bool selected = gpc->curve_points[i].flag & GP_CURVE_POINT_SELECT; + if (prev_selected == false && selected == true) { + idx_start = i; + } + /* Island ends if the current point is unselected or if we reached the end of the stroke. */ + if ((prev_selected == true && selected == false) || (selected == true && i == idx_last)) { + idx_end = selected ? i : i - 1; + int island_length = idx_end - idx_start + 1; + + bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false); + new_stroke->points = NULL; + if (island_length != gpc->tot_curve_points) { + new_stroke->flag &= ~GP_STROKE_CYCLIC; + } + new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length); + + if (gps_first == NULL) { + gps_first = new_stroke; + } + + bGPDcurve *new_gpc = new_stroke->editcurve; + memcpy(new_gpc->curve_points, + gpc->curve_points + idx_start, + sizeof(bGPDcurve_point) * island_length); + + BLI_strncpy( + new_stroke->runtime.tmp_layerinfo, gpl->info, sizeof(new_stroke->runtime.tmp_layerinfo)); + + BKE_gpencil_editcurve_recalculate_handles(new_stroke); + new_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpd, new_stroke); + + /* Insert stroke into frame. */ + BLI_addtail(new_strokes, new_stroke); + + gps_last = new_stroke; + } + prev_selected = selected; + } + + /* join first and last stroke if cyclic */ + if (is_cyclic && gps_first != NULL && gps_last != NULL && gps_first != gps_last) { + bGPDcurve *gpc_first = gps_first->editcurve; + bGPDcurve *gpc_last = gps_last->editcurve; + int first_tot_points = gpc_first->tot_curve_points; + int old_tot_points = gpc_last->tot_curve_points; + + gpc_last->tot_curve_points = first_tot_points + old_tot_points; + gpc_last->curve_points = MEM_recallocN(gpc_last->curve_points, + sizeof(bGPDcurve_point) * gpc_last->tot_curve_points); + /* copy data from first to last */ + memcpy(gpc_last->curve_points + old_tot_points, + gpc_first->curve_points, + sizeof(bGPDcurve_point) * first_tot_points); + + BKE_gpencil_editcurve_recalculate_handles(gps_last); + gps_last->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpd, gps_last); + + /* remove first one */ + BLI_remlink(new_strokes, gps_first); + BKE_gpencil_free_stroke(gps_first); + } + + /* Deselect curve. */ + for (int i = 0; i < gpc->tot_curve_points; i++) { + gpc->curve_points[i].flag &= ~GP_CURVE_POINT_SELECT; + BEZT_DESEL_ALL(&gpc->curve_points[i].bezt); + } + gpc->flag &= ~GP_CURVE_SELECT; + BKE_gpencil_stroke_editcurve_sync_selection(gps, gpc); +} + static int gpencil_duplicate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -938,7 +1030,31 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op) bool changed = false; if (is_curve_edit) { - BKE_report(op->reports, RPT_ERROR, "Not implemented!"); + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + ListBase new_strokes = {NULL, NULL}; + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + continue; + } + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (gps->editcurve == NULL) { + continue; + } + + bGPDcurve *gpc = gps->editcurve; + if (gpc->flag & GP_CURVE_SELECT) { + gpencil_duplicate_selected_curve_points(gpd, gpl, &new_strokes, gps, gpc); + changed = true; + } + } + + BLI_movelisttolist(&gpf->strokes, &new_strokes); + } + CTX_DATA_END; } else { /* for each visible (and editable) layer's selected strokes, @@ -1005,9 +1121,11 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op) CTX_DATA_END; } - /* updates */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + if (changed) { + /* updates */ + 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; } @@ -2872,6 +2990,7 @@ static void gpencil_curve_delete_tagged_points(bGPdata *gpd, idx_end = selected ? i - 1 : i; int island_length = idx_end - idx_start + 1; +#if 0 /* If an island has only a single curve point, there is no curve segment, so skip island */ if (island_length == 1) { if (is_cyclic) { @@ -2885,7 +3004,7 @@ static void gpencil_curve_delete_tagged_points(bGPdata *gpd, continue; } } - +#endif bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false); new_stroke->points = NULL; new_stroke->flag &= ~GP_STROKE_CYCLIC; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 8b9f0fdb972..032e3fea458 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -46,6 +46,7 @@ #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_font.h" +#include "BKE_gpencil_curve.h" #include "BKE_gpencil_geom.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -3050,9 +3051,14 @@ static int viewselected_exec(bContext *C, wmOperator *op) } if (is_gp_edit) { + const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_eval); CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { /* we're only interested in selected points here... */ - if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { + if (is_curve_edit && gps->editcurve != NULL && gps->editcurve->flag & GP_CURVE_SELECT) { + BKE_gpencil_stroke_editcurve_sync_selection(gps, gps->editcurve); + ok |= BKE_gpencil_stroke_minmax(gps, true, min, max); + } + else if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { ok |= BKE_gpencil_stroke_minmax(gps, true, min, max); } } |