Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py3
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py8
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h1
-rw-r--r--source/blender/blenkernel/intern/gpencil.c80
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c82
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 ************************** */