diff options
author | Antonioya <blendergit@gmail.com> | 2019-06-30 22:03:24 +0300 |
---|---|---|
committer | Antonioya <blendergit@gmail.com> | 2019-06-30 22:06:10 +0300 |
commit | 789c6e94fb0dd711444a7cdd332291be979d5850 (patch) | |
tree | 04b230f0bd8c59ccffe3cdb0b67508e101546f81 | |
parent | 741967079c393a9eb6babd60c92a716fafa5d3e9 (diff) |
GPencil: Improve Cyclic operator to generate geometry
Now, when close a geometry with cyclic is possible generate new geometry for the gap.
The cyclic operator now supports multiframe edition too. Before only worked with active frame.
Also added the corresponding missing menu options and the new F keymap. All these features were missing, pending of the fix of the alpha glitches in stroke already done.
-rw-r--r-- | release/scripts/presets/keyconfig/keymap_data/blender_default.py | 3 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_gpencil.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/gpencil.c | 80 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 82 |
5 files changed, 144 insertions, 30 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 440916c209a..c888ce4402a 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3008,6 +3008,9 @@ def km_grease_pencil_stroke_edit_mode(params): ("gpencil.stroke_join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None), ("gpencil.stroke_join", {"type": 'J', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("type", 'JOINCOPY')]}), + # Close strokes + ("gpencil.stroke_cyclical_set", {"type": 'F', "value": 'PRESS'}, + {"properties": [("type", 'CLOSE'), ("geometry", True)]}), # Copy + paset ("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("gpencil.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 10bd910db99..8630fe49696 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -4542,6 +4542,9 @@ class VIEW3D_MT_edit_gpencil(Menu): layout.operator_menu_enum("gpencil.stroke_separate", "mode", text="Separate...") layout.operator("gpencil.stroke_split", text="Split") layout.operator("gpencil.stroke_merge", text="Merge") + op = layout.operator("gpencil.stroke_cyclical_set", text="Close") + op.type = 'CLOSE' + op.geometry = True layout.operator_menu_enum("gpencil.stroke_join", "type", text="Join...") layout.operator("gpencil.stroke_flip", text="Flip Direction") @@ -6295,7 +6298,10 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): layout.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' layout.menu("GPENCIL_MT_separate", text="Separate") layout.operator("gpencil.stroke_split", text="Split") - + op = layout.operator("gpencil.stroke_cyclical_set", text="Close") + op.type = 'CLOSE' + op.geometry = True + layout.separator() layout.menu("VIEW3D_MT_mirror") diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 0e3b0181fb9..ae1000d1b99 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -215,6 +215,7 @@ 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_thickness(struct bGPDstroke *gps, int point_index, float influence); bool BKE_gpencil_smooth_stroke_uv(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_close_stroke(struct bGPDstroke *gps); void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe); float BKE_gpencil_multiframe_falloff_calc( diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 842fa85bf76..ff638eaf082 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1958,3 +1958,83 @@ bool BKE_gpencil_trim_stroke(bGPDstroke *gps) } return intersect; } + +/** + * Close stroke + * \param gps: Stroke to close + */ +bool BKE_gpencil_close_stroke(bGPDstroke *gps) +{ + bGPDspoint *pt1 = NULL; + bGPDspoint *pt2 = NULL; + + /* Only can close a stroke with 3 points or more. */ + if (gps->totpoints < 3) { + return false; + } + + /* Calc average distance between points to get same level of sampling. */ + float dist_tot = 0.0f; + for (int i = 0; i < gps->totpoints - 1; i++) { + pt1 = &gps->points[i]; + pt2 = &gps->points[i + 1]; + dist_tot += len_v3v3(&pt1->x, &pt2->x); + } + /* Calc the average distance. */ + float dist_avg = dist_tot / (gps->totpoints - 1); + + /* Calc distance between last and first point. */ + pt1 = &gps->points[gps->totpoints - 1]; + pt2 = &gps->points[0]; + float dist_close = len_v3v3(&pt1->x, &pt2->x); + + /* Calc number of points required using the average distance. */ + int tot_newpoints = MAX2(dist_close / dist_avg, 1); + + /* Resize stroke array. */ + int old_tot = gps->totpoints; + gps->totpoints += tot_newpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } + + /* Generate new points */ + pt1 = &gps->points[old_tot - 1]; + pt2 = &gps->points[0]; + bGPDspoint *pt = &gps->points[old_tot]; + for (int i = 1; i < tot_newpoints + 1; i++, pt++) { + float step = ((float)i / (float)tot_newpoints); + /* Clamp last point to be near, but not on top of first point. */ + CLAMP(step, 0.0f, 0.99f); + + /* Average point. */ + interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step); + pt->pressure = interpf(pt2->pressure, pt1->pressure, step); + pt->strength = interpf(pt2->strength, pt1->strength, step); + pt->flag = 0; + + /* Set weights. */ + if (gps->dvert != NULL) { + MDeformVert *dvert1 = &gps->dvert[old_tot - 1]; + MDeformWeight *dw1 = defvert_verify_index(dvert1, 0); + float weight_1 = dw1 ? dw1->weight : 0.0f; + + MDeformVert *dvert2 = &gps->dvert[0]; + MDeformWeight *dw2 = defvert_verify_index(dvert2, 0); + float weight_2 = dw2 ? dw2->weight : 0.0f; + + MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1]; + dvert_final->totweight = 0; + MDeformWeight *dw = defvert_verify_index(dvert_final, 0); + if (dvert_final->dw) { + dw->weight = interpf(weight_2, weight_1, step); + } + } + } + + /* Enable cyclic flag. */ + gps->flag |= GP_STROKE_CYCLIC; + + return true; +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 5459cd09e53..44f4728adcd 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -2804,6 +2804,9 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); const int type = RNA_enum_get(op->ptr, "type"); + const bool geometry = RNA_boolean_get(op->ptr, "geometry"); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bGPDstroke *gps = NULL; /* sanity checks */ if (ELEM(NULL, gpd)) { @@ -2812,39 +2815,55 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) /* loop all selected strokes */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - if (gpl->actframe == NULL) { - continue; - } + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { + continue; + } - /* skip strokes that are not selected or invalid for current view */ - if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* skip hidden or locked colors */ - if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || - (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { - continue; - } + for (gps = gpf->strokes.first; gps; gps = gps->next) { + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || + ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* skip hidden or locked colors */ + if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || + (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + continue; + } - switch (type) { - case GP_STROKE_CYCLIC_CLOSE: - /* Close all (enable) */ - gps->flag |= GP_STROKE_CYCLIC; - break; - case GP_STROKE_CYCLIC_OPEN: - /* Open all (disable) */ - gps->flag &= ~GP_STROKE_CYCLIC; - break; - case GP_STROKE_CYCLIC_TOGGLE: - /* Just toggle flag... */ - gps->flag ^= GP_STROKE_CYCLIC; - break; - default: - BLI_assert(0); + switch (type) { + case GP_STROKE_CYCLIC_CLOSE: + /* Close all (enable) */ + gps->flag |= GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_OPEN: + /* Open all (disable) */ + gps->flag &= ~GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_TOGGLE: + /* Just toggle flag... */ + gps->flag ^= GP_STROKE_CYCLIC; + break; + default: + BLI_assert(0); + break; + } + + /* Create new geometry. */ + if ((gps->flag & GP_STROKE_CYCLIC) && (geometry)) { + BKE_gpencil_close_stroke(gps); + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { break; + } } } } @@ -2863,6 +2882,8 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) */ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem cyclic_type[] = { {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, @@ -2884,6 +2905,9 @@ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); + prop = RNA_def_boolean( + ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ******************* Flat Stroke Caps ************************** */ |