Commit: 29259a318de3a73a4df52a736b559244cd4dfa47 Author: Antonioya Date: Mon Jul 29 13:56:48 2019 +0200 Branches: greasepencil-object https://developer.blender.org/rB29259a318de3a73a4df52a736b559244cd4dfa47
GPencil: Merge by distance (WIP) Initial implementation of merge by distance GPencil: Fix duplicated CMake entry GPencil: Add missing code for operator in previous commit =================================================================== M release/scripts/startup/bl_ui/properties_grease_pencil_common.py M source/blender/blenkernel/BKE_gpencil.h M source/blender/blenkernel/intern/gpencil.c M source/blender/blenkernel/intern/gpencil_modifier.c M source/blender/editors/gpencil/annotate_paint.c M source/blender/editors/gpencil/gpencil_edit.c M source/blender/editors/gpencil/gpencil_intern.h M source/blender/editors/gpencil/gpencil_merge.c M source/blender/editors/gpencil/gpencil_ops.c M source/blender/editors/gpencil/gpencil_paint.c M source/blender/gpencil_modifiers/CMakeLists.txt =================================================================== 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 112fb4361d6..3a623604934 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -616,6 +616,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 c145d8acd15..2a33c486288 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -277,6 +277,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, @@ -309,6 +313,14 @@ bool BKE_gpencil_smooth_stroke_thickness(struct bGPDstroke *gps, int point_index 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_delete_tagged_points(struct bGPDframe *gpf, + struct bGPDstroke *gps, + struct bGPDstroke *next_stroke, + int tag_flags, + bool select, + int limit); +void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag); + void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe); float BKE_gpencil_multiframe_falloff_calc( struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 0d935f47805..78c1caf5385 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2647,6 +2647,340 @@ bool BKE_gpencil_close_stroke(bGPDstroke *gps) return true; } +/* Temp data for storing information about an "island" of points + * that should be kept when splitting up a stroke. Used in: + * BKE_gpencil_delete_tagged_points() + */ +typedef struct tGPDeleteIsland { + int start_idx; + int end_idx; +} tGPDeleteIsland; + +static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last) +{ + bGPDspoint *pt = NULL; + bGPDspoint *pt_final = NULL; + const int totpoints = gps_first->totpoints + gps_last->totpoints; + + /* create new stroke */ + bGPDstroke *join_stroke = MEM_dupallocN(gps_first); + + join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); + join_stroke->totpoints = totpoints; + join_stroke->flag &= ~GP_STROKE_CYCLIC; + + /* copy points (last before) */ + int e1 = 0; + int e2 = 0; + float delta = 0.0f; + + for (int i = 0; i < totpoints; i++) { + pt_final = &join_stroke->points[i]; + if (i < gps_last->totpoints) { + pt = &gps_last->points[e1]; + e1++; + } + else { + pt = &gps_first->points[e2]; + e2++; + } + + /* copy current point */ + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = delta; + pt_final->flag = pt->flag; + + /* retiming with fixed time interval (we cannot determine real time) */ + delta += 0.01f; + } + + /* Copy over vertex weight data (if available) */ + if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) { + join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__); + MDeformVert *dvert_src = NULL; + MDeformVert *dvert_dst = NULL; + + /* Copy weights (last before)*/ + e1 = 0; + e2 = 0; + for (int i = 0; i < totpoints; i++) { + dvert_dst = &join_stroke->dvert[i]; + dvert_src = NULL; + if (i < gps_last->totpoints) { + if (gps_last->dvert) { + dvert_src = &gps_last->dvert[e1]; + e1++; + } + } + else { + if (gps_first->dvert) { + dvert_src = &gps_first->dvert[e2]; + e2++; + } + } + + if ((dvert_src) && (dvert_src->dw)) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + } + } + + /* add new stroke at head */ + BLI_addhead(&gpf->strokes, join_stroke); + + /* remove first stroke */ + BLI_remlink(&gpf->strokes, gps_first); + BKE_gpencil_free_stroke(gps_first); + + /* remove last stroke */ + BLI_remlink(&gpf->strokes, gps_last); + BKE_gpencil_free_stroke(gps_last); +} + +/* Split the given stroke into several new strokes, partitioning + * it based on whether the stroke points have a particular flag + * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always) + * + * The algorithm used here is as follows: + * 1) We firstly identify the number of "islands" of non-tagged points + * which will all end up being in new strokes. + * - In the most extreme case (i.e. every other vert is a 1-vert island), + * we have at most n / 2 islands + * - Once we start having larger islands than that, the number required + * becomes much less + * 2) Each island gets converted to a new stroke + * If the number of points is <= limit, the stroke is deleted + */ +void BKE_gpencil_delete_tagged_points(bGPDframe *gpf, + bGPDstroke *gps, + bGPDstroke *next_stroke, + int tag_flags, + bool select, + int limit) +{ + tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, + "gp_point_islands"); + bool in_island = false; + int num_islands = 0; + + bGPDstroke *gps_first = NULL; + const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC); + + /* First Pass: Identify start/end of islands */ + bGPDspoint *pt = gps->points; + for (int i = 0; i < gps->totpoints; i++, pt++) { + if (pt->flag & tag_flags) { + /* selected - stop accumulating to island */ + in_island = false; + } + else { + /* unselected - start of a new island? */ + int idx; + + if (in_island) { + /* extend existing island */ + idx = num_islands - 1; + islands[idx].end_idx = i; + } + else { + /* start of new island */ + in_island = true; + num_islands++; + + idx = num_islands - 1; + islands[idx].start_idx = islands[idx].end_idx = i; + } + } + } + + /* Watch out for special case where No islands = All points selected = Delete Stroke only */ + if (num_islands) { + /* There are islands, so create a series of new strokes, + * adding them before the "next" stroke. */ + int idx; + bGPDstroke *new_stroke = NULL; + + /* Create each new stroke... */ + for (idx = 0; idx < num_islands; idx++) { + tGPDeleteIsland *island = &islands[idx]; + new_stroke = MEM_dupallocN(gps); + + /* if cyclic and first stroke, save to join later */ + if ((is_cyclic) && (gps_first == NULL)) { + gps_first = new_stroke; + } + + /* initialize triangle memory - to be calculated on next redraw */ + new_stroke->triangles = NULL; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + new_stroke->flag &= ~GP_STROKE_CYCLIC; + new_stroke->tot_triangles = 0; + + /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ + new_stroke->totpoints = island->end_idx - island->start_idx + 1; + + /* Copy over the relevant point data */ + new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, + "gp delete stroke fragment"); + memcpy(new_stroke->points, + gps->points + island->start_idx, + sizeof(bGPDspoint) * new_stroke->totpoints); + + /* Copy over vertex weight data (if available) */ + if (gps->dvert != NULL) { + /* Copy over the relevant vertex-weight points */ + new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, + "gp delete stroke fragment weight"); + memcpy(new_stroke->dvert, + gps->dvert + island->start_idx, + sizeof(MDeformVert) * new_stroke->totpoints); + + /* Copy weights */ + int e = island->start_idx; + for (int i = 0; i < new_stroke->totpoints; i++) { + MDeformVert *dvert_src = &gps->dvert[e]; + MDeformVert *dvert_dst = &new_stroke->dvert[i]; + if (dvert_src->dw) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + e++; + } + } + /* Each island corresponds to a new stroke. + * We must adjust the timings of these new strokes: + * + * Each point's timing data is a delta from stroke's inittime, so as we erase some points + * from the start of the stroke, we have to offset this inittime and all remaining points' + * delta values. This way we get a new stroke with exactly the same timing as if user had + * started drawing from the first non-removed point. + */ + { + bGPDspoint *pts; + float delta = gps->points[island->start_idx].time; + int j; + + new_stroke->inittime += (double)delta; + + pts = new_stroke-> @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org https://lists.blender.org/mailman/listinfo/bf-blender-cvs