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:
authorAntonioya <blendergit@gmail.com>2019-08-08 11:23:05 +0300
committerAntonioya <blendergit@gmail.com>2019-08-08 15:13:01 +0300
commitb9d0f33530f095fb318890dca4f8933a9afd1904 (patch)
tree3506e4f5a951d591f871474caa5292b0868ab2d0
parent9d4a8cbd887f03bf1acfaae5e8e3342f3c7ed040 (diff)
Fix T67545: GPencil - New Merge by Distance operator
Merge points when the distance is less than a predefined value. The method to interpolate the position created a wrong merge. Now, always the secondary point is merged with the first one (merge at first), except the last point.
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py1
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h4
-rw-r--r--source/blender/blenkernel/intern/gpencil.c78
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c70
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h1
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c1
6 files changed, 155 insertions, 0 deletions
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 1e5aac4cc0d..bef4f395233 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -606,6 +606,7 @@ class GPENCIL_MT_cleanup(Menu):
def draw(self, _context):
layout = self.layout
+ layout.operator("gpencil.stroke_merge_by_distance", text="Merge by Distance")
layout.operator("gpencil.frame_clean_loose", text="Loose Points")
layout.separator()
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 9ec872f3676..3dfd6eb466c 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -197,6 +197,10 @@ 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_merge_distance_stroke(struct bGPDframe *gpf,
+ struct bGPDstroke *gps,
+ const float threshold,
+ const bool use_unselected);
void BKE_gpencil_stroke_2d_flat(const struct bGPDspoint *points,
int totpoints,
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 3ae20642b15..efdb35d4409 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -2138,3 +2138,81 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta
gps->tot_triangles = 0;
}
}
+
+/* Merge by distance ------------------------------------- */
+/* Reduce a series of points when the distance is below a threshold.
+ * Special case for first and last points (both are keeped) for other points,
+ * the merge point always is at first point.
+ * \param gpf: Grease Pencil frame
+ * \param gps: Grease Pencil stroke
+ * \param threshold: Distance between points
+ * \param use_unselected: Set to true to analyze all stroke and not only selected points
+ */
+void BKE_gpencil_merge_distance_stroke(bGPDframe *gpf,
+ bGPDstroke *gps,
+ const float threshold,
+ const bool use_unselected)
+{
+ bGPDspoint *pt = NULL;
+ bGPDspoint *pt_next = NULL;
+ float tagged = false;
+ /* Use square distance to speed up loop */
+ const float th_square = threshold * threshold;
+ /* Need to have something to merge. */
+ if (gps->totpoints < 2) {
+ return;
+ }
+ int i = 0;
+ int step = 1;
+ while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
+ pt = &gps->points[i];
+ if (pt->flag & GP_SPOINT_TAG) {
+ i++;
+ step = 1;
+ continue;
+ }
+ pt_next = &gps->points[i + step];
+ /* Do not recalc tagged points. */
+ if (pt_next->flag & GP_SPOINT_TAG) {
+ step++;
+ continue;
+ }
+ /* Check if contiguous points are selected. */
+ if (!use_unselected) {
+ if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
+ i++;
+ step = 1;
+ continue;
+ }
+ }
+ float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
+ if (len_square <= th_square) {
+ tagged = true;
+ if (i != gps->totpoints - 1) {
+ /* Tag second point for delete. */
+ pt_next->flag |= GP_SPOINT_TAG;
+ }
+ else {
+ pt->flag |= GP_SPOINT_TAG;
+ }
+ /* Jump to next pair of points, keeping first point segment equals.*/
+ step++;
+ }
+ else {
+ /* Analyze next point. */
+ i++;
+ step = 1;
+ }
+ }
+
+ /* Always untag extremes. */
+ pt = &gps->points[0];
+ pt->flag &= ~GP_SPOINT_TAG;
+ pt = &gps->points[gps->totpoints - 1];
+ pt->flag &= ~GP_SPOINT_TAG;
+
+ /* Dissolve tagged points */
+ if (tagged) {
+ BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG);
+ }
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 277628f4363..a7b9bb24bf8 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -4522,3 +4522,73 @@ bool ED_object_gpencil_exit(struct Main *bmain, Object *ob)
}
return ok;
}
+
+/* ** merge by distance *** */
+bool gp_merge_by_distance_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if (ob == NULL) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (gpd == NULL) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+
+ return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
+}
+
+static int gp_merge_by_distance_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const bool unselected = RNA_boolean_get(op->ptr, "use_unselected");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Go through each editable selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_merge_distance_stroke(gpf_, gps, threshold, unselected);
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* 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_merge_by_distance(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Merge by Distance";
+ ot->idname = "GPENCIL_OT_stroke_merge_by_distance";
+ ot->description = "Merge points by distance";
+
+ /* api callbacks */
+ ot->exec = gp_merge_by_distance_exec;
+ ot->poll = gp_merge_by_distance_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, 100.0f, "Threshold", "", 0.0f, 100.0f);
+ /* avoid re-using last var */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(
+ ot->srna, "use_unselected", 0, "Unselected", "Use whole stroke, not only selected points");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 1c20da4bed3..3776dd09eb5 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -489,6 +489,7 @@ 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_stroke_merge_by_distance(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 a1645a5c67d..54e99802c54 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -317,6 +317,7 @@ void ED_operatortypes_gpencil(void)
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_stroke_merge_by_distance);
WM_operatortype_append(GPENCIL_OT_brush_presets_create);