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
path: root/source
diff options
context:
space:
mode:
authorCharlie Jolly <mistajolly@gmail.com>2019-02-26 19:04:27 +0300
committerCharlie Jolly <mistajolly@gmail.com>2019-02-26 19:04:27 +0300
commit65de468396aaf5f43fffdc6d42e304412f75fcb8 (patch)
tree0afd57b0052df8705fd4d9183c720205e7b33caa /source
parentcb8614e398d395180a615e7e256a25cfe6f7c9eb (diff)
GP: Draw: Stroke Trim
New edit mode operator and post-processing brush option. Trim works on a single GP stroke. It removes trailing points before and after the first intersection (or loop) nearest to the start of the stroke.
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h1
-rw-r--r--source/blender/blenkernel/intern/gpencil.c95
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c68
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h1
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c6
-rw-r--r--source/blender/makesdna/DNA_brush_types.h8
-rw-r--r--source/blender/makesrna/intern/rna_brush.c6
8 files changed, 183 insertions, 3 deletions
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");