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:
authorFalk David <falkdavid@gmx.de>2020-11-13 23:43:00 +0300
committerFalk David <falkdavid@gmx.de>2020-11-13 23:43:00 +0300
commit0be88c7d15d2ad1af284c6283370173647ae74eb (patch)
tree5fff573c512e284547ebe0c921ecffdae2c377c4 /source/blender/editors
parent9d28353b525ecfbcca1501be72e4276dfb2bbc2a (diff)
GPencil: Merge GSoC curve edit mode
Differential Revision: https://developer.blender.org/D8660 This patch is the result of the GSoC 2020 "Editing Grease Pencil Strokes Using Curves" project. It adds a submode to greasepencil edit mode that allows for the transformation of greasepencil strokes using bezier curves. More information about the project can be found here: https://wiki.blender.org/wiki/User:Filedescriptor/GSoC_2020.
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c6
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c56
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c51
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c1588
-rw-r--r--source/blender/editors/gpencil/gpencil_edit_curve.c214
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h59
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c14
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c22
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c1571
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c5
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c101
-rw-r--r--source/blender/editors/gpencil/gpencil_uv.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c25
-rw-r--r--source/blender/editors/include/ED_gpencil.h4
-rw-r--r--source/blender/editors/screen/area.c4
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c436
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c42
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c14
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c14
29 files changed, 3322 insertions, 955 deletions
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index b2c618073f4..1e91edbb3c0 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -42,6 +42,7 @@ set(SRC
gpencil_convert.c
gpencil_data.c
gpencil_edit.c
+ gpencil_edit_curve.c
gpencil_fill.c
gpencil_interpolate.c
gpencil_merge.c
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index a9f9625db7a..a73bf8154d9 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -910,7 +910,7 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p)
int totarrowpoints = runtime.arrow_end_style;
/* Setting up arrow stroke. */
- bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false);
+ bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false, false);
annotation_stroke_arrow_allocate(e_arrow_gps, totarrowpoints);
/* Set pointer to first non-initialized point. */
@@ -931,7 +931,7 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p)
int totarrowpoints = runtime.arrow_start_style;
/* Setting up arrow stroke. */
- bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false);
+ bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false, false);
annotation_stroke_arrow_allocate(s_arrow_gps, totarrowpoints);
/* Set pointer to first non-initialized point. */
@@ -1198,7 +1198,7 @@ static void annotation_stroke_eraser_dostroke(tGPsdata *p,
/* Second Pass: Remove any points that are tagged */
if (do_cull) {
- gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
+ gpencil_stroke_delete_tagged_points(p->gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
}
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
index d4f3c4d7f5e..aae31b11025 100644
--- a/source/blender/editors/gpencil/editaction_gpencil.c
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -486,7 +486,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
*/
for (gps = gpfs->strokes.first; gps; gps = gps->next) {
/* make a copy of stroke, then of its points array */
- gpsn = BKE_gpencil_stroke_duplicate(gps, true);
+ gpsn = BKE_gpencil_stroke_duplicate(gps, true, true);
/* append stroke to frame */
BLI_addtail(&gpf->strokes, gpsn);
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 23ca5241866..315b3c281da 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -861,115 +861,115 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* generate strokes */
gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 270, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data1, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false);
BKE_gpencil_stroke_add_points(gps, data2, 18, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data3, 64, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data4, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data5, 64, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data6, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data7, 18, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data8, 49, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data9, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data10, 49, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data11, 18, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data12, 18, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data13, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data14, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false);
BKE_gpencil_stroke_add_points(gps, data15, 65, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false);
BKE_gpencil_stroke_add_points(gps, data16, 34, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data17, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false);
BKE_gpencil_stroke_add_points(gps, data18, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false);
BKE_gpencil_stroke_add_points(gps, data19, 34, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data20, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data21, 64, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data22, 26, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data23, 26, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data24, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data25, 18, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data26, 18, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data27, 33, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 39a2d594c13..f26fd936d40 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -249,7 +249,7 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
/* generate stroke */
gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 175, mat);
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 237b5839c42..293ce370a0b 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1855,12 +1855,12 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (bGPdata *)ob->data;
bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
- done = BKE_gpencil_from_image(sima, gpf, size, is_mask);
+ done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
if (done) {
/* Delete any selected point. */
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
- gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ gpencil_stroke_delete_tagged_points(gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
BKE_reportf(op->reports, RPT_INFO, "Object created");
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 2cee06c36ad..a963632a01b 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -580,7 +580,7 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* Make copy of source stroke. */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
/* Check if material is in destination object,
* otherwise add the slot with the material. */
@@ -1531,6 +1531,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
const int direction = RNA_enum_get(op->ptr, "direction");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
/* temp listbase to store selected strokes */
ListBase selected = {NULL};
@@ -1589,7 +1590,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
break;
/* Bring Forward */
case GP_STROKE_MOVE_UP:
- for (LinkData *link = selected.last; link; link = link->prev) {
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
gps = link->data;
BLI_listbase_link_move(&gpf->strokes, gps, 1);
}
@@ -1603,7 +1604,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
break;
/* Send to Back */
case GP_STROKE_MOVE_BOTTOM:
- for (LinkData *link = selected.last; link; link = link->prev) {
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
gps = link->data;
BLI_remlink(&gpf->strokes, gps);
BLI_addhead(&gpf->strokes, gps);
@@ -1612,6 +1613,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
default:
BLI_assert(0);
break;
+ changed = true;
}
}
BLI_freelistN(&selected);
@@ -1625,9 +1627,11 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
}
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);
+ if (changed) {
+ /* 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;
}
@@ -1693,6 +1697,7 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ bool changed = false;
/* loop all strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
@@ -1717,6 +1722,8 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
/* assign new color */
gps->mat_nr = idx;
+
+ changed = true;
}
}
}
@@ -1728,9 +1735,11 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
}
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);
+ if (changed) {
+ /* 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;
}
@@ -1757,9 +1766,7 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
-
Object *ob = CTX_data_active_object(C);
-
short *totcol = BKE_object_material_len_p(ob);
/* sanity checks */
@@ -1776,6 +1783,7 @@ static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op
}
}
+ bool changed = false;
/* loop all selected strokes and unlock any color */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
@@ -1793,19 +1801,24 @@ static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op
tmp_ma->gp_style->flag &= ~GP_MATERIAL_LOCKED;
DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE);
}
+
+ changed = true;
}
}
}
}
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* 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);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ /* 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;
}
@@ -3476,7 +3489,6 @@ static int gpencil_set_active_material_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bool changed = false;
/* Sanity checks. */
if (gpd == NULL) {
@@ -3484,6 +3496,7 @@ static int gpencil_set_active_material_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ bool changed = false;
/* Loop all selected strokes. */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 4ba75bcd604..f2c3804a067 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -52,6 +52,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@@ -145,6 +146,18 @@ static bool gpencil_editmode_toggle_poll(bContext *C)
return ED_gpencil_data_get_active(C) != NULL;
}
+static bool gpencil_stroke_not_in_curve_edit_mode(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+
+ return (gpl != NULL && !GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -195,6 +208,22 @@ static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
ob->mode = mode;
}
+ /* Recalculate editcurves for strokes where the geometry/vertex colors have changed */
+ if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (gpc->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+
/* setup other modes */
ED_gpencil_setup_modes(C, gpd, mode);
/* set cache as dirty */
@@ -813,7 +842,8 @@ void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
* \{ */
/* Make copies of selected point segments in a selected stroke */
-static void gpencil_duplicate_points(const bGPDstroke *gps,
+static void gpencil_duplicate_points(bGPdata *gpd,
+ const bGPDstroke *gps,
ListBase *new_strokes,
const char *layername)
{
@@ -853,7 +883,7 @@ static void gpencil_duplicate_points(const bGPDstroke *gps,
bGPDstroke *gpsd;
/* make a stupid copy first of the entire stroke (to get the flags too) */
- gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false);
+ gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, true);
/* saves original layer name */
BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
@@ -877,7 +907,7 @@ static void gpencil_duplicate_points(const bGPDstroke *gps,
}
}
- BKE_gpencil_stroke_geometry_update(gpsd);
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd);
/* add to temp buffer */
gpsd->next = gpsd->prev = NULL;
@@ -894,6 +924,7 @@ static void gpencil_duplicate_points(const bGPDstroke *gps,
static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -905,69 +936,80 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* for each visible (and editable) layer's selected strokes,
- * copy the strokes into a temporary buffer, then append
- * once all done
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- ListBase new_strokes = {NULL, NULL};
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
-
- if (gpf == NULL) {
- continue;
- }
+ bool changed = false;
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* for each visible (and editable) layer's selected strokes,
+ * copy the strokes into a temporary buffer, then append
+ * once all done
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ ListBase new_strokes = {NULL, NULL};
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
- /* make copies of selected strokes, and deselect these once we're done */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ if (gpf == NULL) {
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
+ /* make copies of selected strokes, and deselect these once we're done */
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
- /* make direct copies of the stroke and its points */
- gpsd = BKE_gpencil_stroke_duplicate(gps, true);
+ if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* Initialize triangle information. */
- BKE_gpencil_stroke_geometry_update(gpsd);
+ BLI_strncpy(
+ gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&new_strokes, gpsd);
- }
- else {
- /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gpencil_duplicate_points(gps, &new_strokes, gpl->info);
- }
+ /* Initialize triangle information. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd);
- /* deselect original stroke, or else the originals get moved too
- * (when using the copy + move macro)
- */
- bGPDspoint *pt;
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&new_strokes, gpsd);
+ }
+ else {
+ /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
+ gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
+ }
+
+ /* deselect original stroke, or else the originals get moved too
+ * (when using the copy + move macro)
+ */
+ bGPDspoint *pt;
+ int i;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
+
+ changed = true;
}
- gps->flag &= ~GP_STROKE_SELECT;
}
- }
- /* add all new strokes in temp buffer to the frame (preventing double-copies) */
- BLI_movelisttolist(&gpf->strokes, &new_strokes);
- BLI_assert(new_strokes.first == NULL);
+ /* add all new strokes in temp buffer to the frame (preventing double-copies) */
+ BLI_movelisttolist(&gpf->strokes, &new_strokes);
+ BLI_assert(new_strokes.first == NULL);
+ }
+ CTX_DATA_END;
}
- CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ /* updates */
+ 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;
}
@@ -1028,7 +1070,7 @@ static void gpencil_copy_move_point(bGPDstroke *gps,
}
}
-static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
+static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
{
bGPDspoint *temp_points = NULL;
MDeformVert *temp_dverts = NULL;
@@ -1049,7 +1091,7 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
pt = &gps->points[i];
if (pt->flag == GP_SPOINT_SELECT) {
/* duplicate original stroke data */
- bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false);
+ bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false, true);
gps_new->prev = gps_new->next = NULL;
/* add new points array */
@@ -1067,8 +1109,8 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
gpencil_copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
- BKE_gpencil_stroke_geometry_update(gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new);
/* deselect orinal point */
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1143,7 +1185,7 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
@@ -1155,10 +1197,100 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
}
}
+static void gpencil_curve_extrude_points(bGPdata *gpd,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ bGPDcurve *gpc)
+{
+ const int old_num_points = gpc->tot_curve_points;
+ const bool first_select = gpc->curve_points[0].flag & GP_CURVE_POINT_SELECT;
+ bool last_select = gpc->curve_points[old_num_points - 1].flag & GP_CURVE_POINT_SELECT;
+
+ /* iterate over middle points */
+ for (int i = 1; i < gpc->tot_curve_points - 1; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+
+ /* Create new stroke if selected point */
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false, false);
+ gps_new->points = NULL;
+ gps_new->flag &= ~GP_STROKE_CYCLIC;
+ gps_new->prev = gps_new->next = NULL;
+
+ gps_new->editcurve = BKE_gpencil_stroke_editcurve_new(2);
+ bGPDcurve *new_gpc = gps_new->editcurve;
+ for (int j = 0; j < new_gpc->tot_curve_points; j++) {
+ bGPDcurve_point *gpc_pt_new = &new_gpc->curve_points[j];
+ memcpy(gpc_pt_new, gpc_pt, sizeof(bGPDcurve_point));
+ gpc_pt_new->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt_new->bezt);
+ }
+
+ /* select last point */
+ bGPDcurve_point *gpc_pt_last = &new_gpc->curve_points[1];
+ gpc_pt_last->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(&gpc_pt_last->bezt, 1);
+ gps_new->editcurve->flag |= GP_CURVE_SELECT;
+
+ BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
+
+ gps_new->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+ }
+ }
+
+ /* Edgcase for single curve point. */
+ if (gpc->tot_curve_points == 1) {
+ last_select = false;
+ }
+
+ if (first_select || last_select) {
+ int new_num_points = old_num_points;
+
+ if (first_select) {
+ new_num_points++;
+ }
+ if (last_select) {
+ new_num_points++;
+ }
+
+ /* Grow the array */
+ gpc->tot_curve_points = new_num_points;
+ gpc->curve_points = MEM_recallocN(gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
+
+ if (first_select) {
+ /* shift points by one */
+ memmove(
+ &gpc->curve_points[1], &gpc->curve_points[0], sizeof(bGPDcurve_point) * old_num_points);
+
+ bGPDcurve_point *old_first = &gpc->curve_points[1];
+
+ old_first->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&old_first->bezt);
+ }
+
+ if (last_select) {
+ bGPDcurve_point *old_last = &gpc->curve_points[gpc->tot_curve_points - 2];
+ bGPDcurve_point *new_last = &gpc->curve_points[gpc->tot_curve_points - 1];
+ memcpy(new_last, old_last, sizeof(bGPDcurve_point));
+
+ old_last->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&old_last->bezt);
+ }
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+}
+
static int gpencil_extrude_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)obact->data;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bGPDstroke *gps = NULL;
@@ -1167,6 +1299,7 @@ static int gpencil_extrude_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
@@ -1182,9 +1315,22 @@ static int gpencil_extrude_exec(bContext *C, wmOperator *op)
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
- gpencil_add_move_points(gpf, gps);
+ if (is_curve_edit) {
+ if (gps->editcurve == NULL) {
+ continue;
+ }
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ gpencil_curve_extrude_points(gpd, gpf, gps, gpc);
+ }
+ }
+ else {
+ if (gps->flag & GP_STROKE_SELECT) {
+ gpencil_add_move_points(gpd, gpf, gps);
+ }
}
+
+ changed = true;
}
/* if not multiedit, exit loop*/
if (!is_multiedit) {
@@ -1195,10 +1341,13 @@ static int gpencil_extrude_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
return OPERATOR_FINISHED;
}
@@ -1352,6 +1501,7 @@ static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -1366,56 +1516,62 @@ static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
/* clear the buffer first */
ED_gpencil_strokes_copybuf_free();
- /* for each visible (and editable) layer's selected strokes,
- * copy the strokes into a temporary buffer, then append
- * once all done
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
-
- if (gpf == NULL) {
- continue;
- }
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* for each visible (and editable) layer's selected strokes,
+ * copy the strokes into a temporary buffer, then append
+ * once all done
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
- /* make copies of selected strokes, and deselect these once we're done */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ if (gpf == NULL) {
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
-
- /* make direct copies of the stroke and its points */
- gpsd = BKE_gpencil_stroke_duplicate(gps, false);
-
- /* saves original layer name */
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- gpsd->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
- }
+ /* make copies of selected strokes, and deselect these once we're done */
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpsd);
+ if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
+
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, false, true);
+
+ /* saves original layer name */
+ BLI_strncpy(
+ gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+ gpsd->points = MEM_dupallocN(gps->points);
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_dupallocN(gps->dvert);
+ BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
+ }
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&gpencil_strokes_copypastebuf, gpsd);
- }
- else {
- /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gpencil_duplicate_points(gps, &gpencil_strokes_copypastebuf, gpl->info);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&gpencil_strokes_copypastebuf, gpsd);
+ }
+ else {
+ /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
+ gpencil_duplicate_points(gpd, gps, &gpencil_strokes_copypastebuf, gpl->info);
+ }
}
}
}
+ CTX_DATA_END;
}
- CTX_DATA_END;
/* Build up hash of material colors used in these strokes */
if (gpencil_strokes_copypastebuf.first) {
@@ -1493,6 +1649,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only use active for copy merge */
Scene *scene = CTX_data_scene(C);
bGPDframe *gpf;
@@ -1556,46 +1713,52 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
/* Ensure that all the necessary colors exist */
new_colors = gpencil_copybuf_validate_colormap(C);
- /* Copy over the strokes from the buffer (and adjust the colors) */
- bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
- gpencil_strokes_copypastebuf.last;
- for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- /* Need to verify if layer exists */
- if (type != GP_COPY_TO_ACTIVE) {
- gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
- if (gpl == NULL) {
- /* no layer - use active (only if layer deleted before paste) */
- gpl = BKE_gpencil_layer_active_get(gpd);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* Copy over the strokes from the buffer (and adjust the colors) */
+ bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
+ gpencil_strokes_copypastebuf.last;
+ for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
+ if (ED_gpencil_stroke_can_use(C, gps)) {
+ /* Need to verify if layer exists */
+ if (type != GP_COPY_TO_ACTIVE) {
+ gpl = BLI_findstring(
+ &gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
+ if (gpl == NULL) {
+ /* no layer - use active (only if layer deleted before paste) */
+ gpl = BKE_gpencil_layer_active_get(gpd);
+ }
}
- }
- /* Ensure we have a frame to draw into
- * NOTE: Since this is an op which creates strokes,
- * we are obliged to add a new frame if one
- * doesn't exist already
- */
- gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
- if (gpf) {
- /* Create new stroke */
- bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true);
- new_stroke->runtime.tmp_layerinfo[0] = '\0';
- new_stroke->next = new_stroke->prev = NULL;
+ /* Ensure we have a frame to draw into
+ * NOTE: Since this is an op which creates strokes,
+ * we are obliged to add a new frame if one
+ * doesn't exist already
+ */
+ gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ if (gpf) {
+ /* Create new stroke */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
+ new_stroke->runtime.tmp_layerinfo[0] = '\0';
+ new_stroke->next = new_stroke->prev = NULL;
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(new_stroke);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
- if (on_back) {
- BLI_addhead(&gpf->strokes, new_stroke);
- }
- else {
- BLI_addtail(&gpf->strokes, new_stroke);
- }
+ if (on_back) {
+ BLI_addhead(&gpf->strokes, new_stroke);
+ }
+ else {
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
- /* Remap material */
- Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
- new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
- CLAMP_MIN(new_stroke->mat_nr, 0);
+ /* Remap material */
+ Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
+ new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
+ CLAMP_MIN(new_stroke->mat_nr, 0);
+ }
}
}
}
@@ -2018,10 +2181,10 @@ typedef enum eGP_DissolveMode {
/* Delete selected strokes */
static int gpencil_delete_selected_strokes(bContext *C)
{
- bool changed = false;
bGPdata *gpd = ED_gpencil_data_get_active(C);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
@@ -2042,16 +2205,9 @@ static int gpencil_delete_selected_strokes(bContext *C)
/* free stroke if selected */
if (gps->flag & GP_STROKE_SELECT) {
+ BLI_remlink(&gpf->strokes, gps);
/* free stroke memory arrays, then stroke itself */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
- MEM_SAFE_FREE(gps->triangles);
- BLI_freelinkN(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
changed = true;
}
@@ -2071,11 +2227,144 @@ static int gpencil_delete_selected_strokes(bContext *C)
/* ----------------------------------- */
-/* Delete selected points but keep the stroke */
-static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
+static bool gpencil_dissolve_selected_curve_points(bContext *C,
+ bGPdata *gpd,
+ eGP_DissolveMode mode)
+{
+ bool changed = false;
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (gpc->flag & GP_CURVE_SELECT) {
+ int first = 0, last = 0;
+ int num_points_remaining = gpc->tot_curve_points;
+
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ num_points_remaining--;
+ }
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ first = -1;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ if (first < 0) {
+ first = i;
+ }
+ last = i;
+ }
+ }
+
+ for (int i = first + 1; i < last; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ num_points_remaining--;
+ }
+ }
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ num_points_remaining--;
+ }
+ }
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ if (num_points_remaining < 1) {
+ /* Delete stroke */
+ BLI_remlink(&gpf_->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ else {
+ bGPDcurve_point *new_points = MEM_callocN(sizeof(bGPDcurve_point) * num_points_remaining,
+ __func__);
+
+ int idx = 0;
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *new_cpt = &new_points[idx];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ *new_cpt = *cpt;
+ idx++;
+ }
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ for (int i = 0; i < first; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *new_cpt = &new_points[idx];
+
+ *new_cpt = *cpt;
+ idx++;
+ }
+
+ for (int i = first; i < last; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *new_cpt = &new_points[idx];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ *new_cpt = *cpt;
+ idx++;
+ }
+ }
+
+ for (int i = last; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *new_cpt = &new_points[idx];
+
+ *new_cpt = *cpt;
+ idx++;
+ }
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *new_cpt = &new_points[idx];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ *new_cpt = *cpt;
+ idx++;
+ }
+ }
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ if (gpc->curve_points != NULL) {
+ MEM_freeN(gpc->curve_points);
+ }
+
+ gpc->curve_points = new_points;
+ gpc->tot_curve_points = num_points_remaining;
+
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
+ }
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+
+ return changed;
+}
+
+static bool gpencil_dissolve_selected_stroke_points(bContext *C,
+ bGPdata *gpd,
+ eGP_DissolveMode mode)
{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = (bGPdata *)ob->data;
bool changed = false;
int first = 0;
int last = 0;
@@ -2135,18 +2424,8 @@ static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
/* if no points are left, we simply delete the entire stroke */
if (tot <= 0) {
/* remove the entire stroke */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
- if (gps->triangles) {
- MEM_freeN(gps->triangles);
- }
- BLI_freelinkN(&gpf_->strokes, gps);
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ BLI_remlink(&gpf_->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
}
else {
/* just copy all points to keep into a smaller buffer */
@@ -2263,7 +2542,7 @@ static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
gps->totpoints = tot;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
/* deselect the stroke, since none of its selected points will still be selected */
gps->flag &= ~GP_STROKE_SELECT;
@@ -2277,6 +2556,24 @@ static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
+ return changed;
+}
+
+/* Delete selected points but keep the stroke */
+static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ bool changed = false;
+
+ if (is_curve_edit) {
+ changed = gpencil_dissolve_selected_curve_points(C, gpd, mode);
+ }
+ else {
+ changed = gpencil_dissolve_selected_stroke_points(C, gpd, mode);
+ }
+
if (changed) {
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -2289,14 +2586,15 @@ static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
/* Temp data for storing information about an "island" of points
* that should be kept when splitting up a stroke. Used in:
- * gp_stroke_delete_tagged_points()
+ * gpencil_stroke_delete_tagged_points()
*/
typedef struct tGPDeleteIsland {
int start_idx;
int end_idx;
} tGPDeleteIsland;
-static void gpencil_stroke_join_islands(bGPDframe *gpf,
+static void gpencil_stroke_join_islands(bGPdata *gpd,
+ bGPDframe *gpf,
bGPDstroke *gps_first,
bGPDstroke *gps_last)
{
@@ -2305,7 +2603,7 @@ static void gpencil_stroke_join_islands(bGPDframe *gpf,
const int totpoints = gps_first->totpoints + gps_last->totpoints;
/* create new stroke */
- bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false);
+ bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false, true);
join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
join_stroke->totpoints = totpoints;
@@ -2373,7 +2671,7 @@ static void gpencil_stroke_join_islands(bGPDframe *gpf,
/* add new stroke at head */
BLI_addhead(&gpf->strokes, join_stroke);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(join_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, join_stroke);
/* remove first stroke */
BLI_remlink(&gpf->strokes, gps_first);
@@ -2398,7 +2696,8 @@ static void gpencil_stroke_join_islands(bGPDframe *gpf,
* 2) Each island gets converted to a new stroke
* If the number of points is <= limit, the stroke is deleted
*/
-void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
+void gpencil_stroke_delete_tagged_points(bGPdata *gpd,
+ bGPDframe *gpf,
bGPDstroke *gps,
bGPDstroke *next_stroke,
int tag_flags,
@@ -2450,7 +2749,7 @@ void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
/* Create each new stroke... */
for (idx = 0; idx < num_islands; idx++) {
tGPDeleteIsland *island = &islands[idx];
- new_stroke = BKE_gpencil_stroke_duplicate(gps, false);
+ new_stroke = BKE_gpencil_stroke_duplicate(gps, false, true);
/* if cyclic and first stroke, save to join later */
if ((is_cyclic) && (gps_first == NULL)) {
@@ -2521,7 +2820,7 @@ void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
}
else {
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
if (next_stroke) {
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
@@ -2533,7 +2832,7 @@ void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
}
/* if cyclic, need to join last stroke with first stroke */
if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) {
- gpencil_stroke_join_islands(gpf, gps_first, new_stroke);
+ gpencil_stroke_join_islands(gpd, gpf, gps_first, new_stroke);
}
}
@@ -2545,11 +2844,118 @@ void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
BKE_gpencil_free_stroke(gps);
}
+static void gpencil_curve_delete_tagged_points(bGPdata *gpd,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ bGPDstroke *next_stroke,
+ bGPDcurve *gpc,
+ int tag_flags)
+{
+ if (gpc == NULL) {
+ return;
+ }
+ const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+ const int idx_last = gpc->tot_curve_points - 1;
+ bGPDstroke *gps_first = NULL;
+ bGPDstroke *gps_last = NULL;
+
+ int idx_start = 0;
+ int idx_end = 0;
+ bool prev_selected = gpc->curve_points[0].flag & tag_flags;
+ for (int i = 1; i < gpc->tot_curve_points; i++) {
+ bool selected = gpc->curve_points[i].flag & tag_flags;
+ if (prev_selected == true && selected == false) {
+ idx_start = i;
+ }
+ /* Island ends if the current point is selected or if we reached the end of the stroke */
+ if ((prev_selected == false && selected == true) || (selected == false && i == idx_last)) {
+
+ idx_end = selected ? i - 1 : i;
+ int island_length = idx_end - idx_start + 1;
+
+ /* If an island has only a single curve point, there is no curve segment, so skip island */
+ if (island_length == 1) {
+ if (is_cyclic) {
+ if (idx_start > 0 && idx_end < idx_last) {
+ prev_selected = selected;
+ continue;
+ }
+ }
+ else {
+ prev_selected = selected;
+ continue;
+ }
+ }
+
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false);
+ new_stroke->points = NULL;
+ new_stroke->flag &= ~GP_STROKE_CYCLIC;
+ new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length);
+
+ if (gps_first == NULL) {
+ gps_first = new_stroke;
+ }
+
+ bGPDcurve *new_gpc = new_stroke->editcurve;
+ memcpy(new_gpc->curve_points,
+ gpc->curve_points + idx_start,
+ sizeof(bGPDcurve_point) * island_length);
+
+ BKE_gpencil_editcurve_recalculate_handles(new_stroke);
+ new_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+
+ if (next_stroke) {
+ BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
+ }
+ else {
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
+
+ gps_last = new_stroke;
+ }
+ prev_selected = selected;
+ }
+
+ /* join first and last stroke if cyclic */
+ if (is_cyclic && gps_first != NULL && gps_last != NULL && gps_first != gps_last) {
+ bGPDcurve *gpc_first = gps_first->editcurve;
+ bGPDcurve *gpc_last = gps_last->editcurve;
+ int first_tot_points = gpc_first->tot_curve_points;
+ int old_tot_points = gpc_last->tot_curve_points;
+
+ gpc_last->tot_curve_points = first_tot_points + old_tot_points;
+ gpc_last->curve_points = MEM_recallocN(gpc_last->curve_points,
+ sizeof(bGPDcurve_point) * gpc_last->tot_curve_points);
+ /* copy data from first to last */
+ memcpy(gpc_last->curve_points + old_tot_points,
+ gpc_first->curve_points,
+ sizeof(bGPDcurve_point) * first_tot_points);
+
+ BKE_gpencil_editcurve_recalculate_handles(gps_last);
+ gps_last->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps_last);
+
+ /* remove first one */
+ BLI_remlink(&gpf->strokes, gps_first);
+ BKE_gpencil_free_stroke(gps_first);
+ }
+
+ /* Delete the old stroke */
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+}
+
/* Split selected strokes into segments, splitting on selected points */
static int gpencil_delete_selected_points(bContext *C)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bool changed = false;
@@ -2579,8 +2985,16 @@ static int gpencil_delete_selected_points(bContext *C)
/* deselect old stroke, since it will be used as template for the new strokes */
gps->flag &= ~GP_STROKE_SELECT;
- /* delete unwanted points by splitting stroke into several smaller ones */
- gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ if (is_curve_edit) {
+ bGPDcurve *gpc = gps->editcurve;
+ gpencil_curve_delete_tagged_points(
+ gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
+ }
+ else {
+ /* delete unwanted points by splitting stroke into several smaller ones */
+ gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ }
changed = true;
}
@@ -2743,7 +3157,9 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact = CTX_data_active_object(C);
const float gridf = ED_view3d_grid_view_scale(scene, v3d, region, NULL);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ bool changed = false;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
@@ -2754,9 +3170,6 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *pt;
- int i;
-
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -2766,30 +3179,82 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
continue;
}
- /* TODO: if entire stroke is selected, offset entire stroke by same amount? */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* only if point is selected */
- if (pt->flag & GP_SPOINT_SELECT) {
- /* apply parent transformations */
- float fpt[3];
- mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ if (is_curve_edit) {
+ if (gps->editcurve == NULL) {
+ continue;
+ }
+ float inv_diff_mat[4][4];
+ invert_m4_m4_safe(inv_diff_mat, diff_mat);
+
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ float tmp0[3], tmp1[3], tmp2[3], offset[3];
+ mul_v3_m4v3(tmp0, diff_mat, bezt->vec[0]);
+ mul_v3_m4v3(tmp1, diff_mat, bezt->vec[1]);
+ mul_v3_m4v3(tmp2, diff_mat, bezt->vec[2]);
+
+ /* calculate the offset vector */
+ offset[0] = gridf * floorf(0.5f + tmp1[0] / gridf) - tmp1[0];
+ offset[1] = gridf * floorf(0.5f + tmp1[1] / gridf) - tmp1[1];
+ offset[2] = gridf * floorf(0.5f + tmp1[2] / gridf) - tmp1[2];
+
+ /* shift bezTriple */
+ add_v3_v3(bezt->vec[0], offset);
+ add_v3_v3(bezt->vec[1], offset);
+ add_v3_v3(bezt->vec[2], offset);
+
+ mul_v3_m4v3(tmp0, inv_diff_mat, bezt->vec[0]);
+ mul_v3_m4v3(tmp1, inv_diff_mat, bezt->vec[1]);
+ mul_v3_m4v3(tmp2, inv_diff_mat, bezt->vec[2]);
+ copy_v3_v3(bezt->vec[0], tmp0);
+ copy_v3_v3(bezt->vec[1], tmp1);
+ copy_v3_v3(bezt->vec[2], tmp2);
+
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ else {
+ /* TODO: if entire stroke is selected, offset entire stroke by same amount? */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ /* only if point is selected */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
+ fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
+ fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
- fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
- fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
- fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
+ /* return data */
+ copy_v3_v3(&pt->x, fpt);
+ gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
- /* return data */
- copy_v3_v3(&pt->x, fpt);
- gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
+ changed = true;
+ }
}
}
}
}
}
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
return OPERATOR_FINISHED;
}
@@ -2817,7 +3282,7 @@ void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
-
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact = CTX_data_active_object(C);
@@ -2825,50 +3290,60 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
const float *cursor_global = scene->cursor.location;
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* only editable and visible layers are considered */
- if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *gpf = gpl->actframe;
- float diff_mat[4][4];
+ bool changed = false;
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* only editable and visible layers are considered */
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ bGPDframe *gpf = gpl->actframe;
+ float diff_mat[4][4];
- /* calculate difference matrix */
- BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
+ /* calculate difference matrix */
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *pt;
- int i;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDspoint *pt;
+ int i;
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
- continue;
- }
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0) {
- continue;
- }
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
+ continue;
+ }
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
- if (use_offset) {
- float offset[3];
+ if (use_offset) {
+ float offset[3];
- /* compute offset from first point of stroke to cursor */
- /* TODO: Allow using midpoint instead? */
- sub_v3_v3v3(offset, cursor_global, &gps->points->x);
+ /* compute offset from first point of stroke to cursor */
+ /* TODO: Allow using midpoint instead? */
+ sub_v3_v3v3(offset, cursor_global, &gps->points->x);
- /* apply offset to all points in the stroke */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- add_v3_v3(&pt->x, offset);
+ /* apply offset to all points in the stroke */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ add_v3_v3(&pt->x, offset);
+ }
+
+ changed = true;
}
- }
- else {
- /* affect each selected point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- copy_v3_v3(&pt->x, cursor_global);
- gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
+ else {
+ /* affect each selected point */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v3_v3(&pt->x, cursor_global);
+ gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
+
+ changed = true;
+ }
}
}
}
@@ -2876,9 +3351,12 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
}
}
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
return OPERATOR_FINISHED;
}
@@ -2910,23 +3388,16 @@ void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
/** \name Snapping Cursor to Selection Operator
* \{ */
-static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
+static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
+ bContext *C,
+ Object *obact,
+ bGPdata *gpd,
+ float r_centroid[3],
+ float r_min[3],
+ float r_max[3],
+ size_t *count)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Object *obact = CTX_data_active_object(C);
- Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
- bGPdata *gpd = (bGPdata *)ob_eval->data;
-
- Scene *scene = CTX_data_scene(C);
- View3D *v3d = CTX_wm_view3d(C);
-
- float *cursor = scene->cursor.location;
- float centroid[3] = {0.0f};
- float min[3], max[3];
- size_t count = 0;
-
- INIT_MINMAX(min, max);
-
+ bool changed = false;
/* calculate midpoints from selected points */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
@@ -2960,29 +3431,61 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
float fpt[3];
mul_v3_m4v3(fpt, diff_mat, &pt->x);
- add_v3_v3(centroid, fpt);
- minmax_v3v3_v3(min, max, fpt);
+ add_v3_v3(r_centroid, fpt);
+ minmax_v3v3_v3(r_min, r_max, fpt);
- count++;
+ (*count)++;
}
}
+
+ changed = true;
}
}
}
- if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
- mid_v3_v3v3(cursor, min, max);
+ return changed;
+}
+
+static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Object *obact = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ float *cursor = scene->cursor.location;
+ float centroid[3] = {0.0f};
+ float min[3], max[3];
+ size_t count = 0;
+
+ INIT_MINMAX(min, max);
+
+ bool changed = false;
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
}
- else { /* #V3D_AROUND_CENTER_MEDIAN. */
- zero_v3(cursor);
- if (count) {
- mul_v3_fl(centroid, 1.0f / (float)count);
- copy_v3_v3(cursor, centroid);
- }
+ else {
+ changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
}
- DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ if (changed) {
+ if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
+ mid_v3_v3v3(cursor, min, max);
+ }
+ else { /* #V3D_AROUND_CENTER_MEDIAN. */
+ zero_v3(cursor);
+ if (count) {
+ mul_v3_fl(centroid, 1.0f / (float)count);
+ copy_v3_v3(cursor, centroid);
+ }
+ }
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ }
return OPERATOR_FINISHED;
}
@@ -3030,6 +3533,7 @@ static int gpencil_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(o
}
}
}
+
/* clear value */
gpl->thickness = 0.0f;
gpl->line_change = 0;
@@ -3073,6 +3577,7 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
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);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
bGPDstroke *gps = NULL;
/* sanity checks */
@@ -3080,6 +3585,7 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ bool changed = false;
/* loop all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
@@ -3103,6 +3609,7 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
continue;
}
+ bool before = (bool)(gps->flag & GP_STROKE_CYCLIC);
switch (type) {
case GP_STROKE_CYCLIC_CLOSE:
/* Close all (enable) */
@@ -3121,9 +3628,19 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
break;
}
- /* Create new geometry. */
- if ((gps->flag & GP_STROKE_CYCLIC) && (geometry)) {
- BKE_gpencil_stroke_close(gps);
+ if (before != (gps->flag & GP_STROKE_CYCLIC)) {
+ /* Create new geometry. */
+ if (is_curve_edit) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ else if ((gps->flag & GP_STROKE_CYCLIC) && geometry) {
+ BKE_gpencil_stroke_close(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
}
}
@@ -3136,13 +3653,31 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
}
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);
+ if (changed) {
+ /* 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;
}
+static bool gpencil_cyclical_set_curve_edit_poll_property(const bContext *C,
+ wmOperator *UNUSED(op),
+ const PropertyRNA *prop)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
+ const char *prop_id = RNA_property_identifier(prop);
+ /* Only show type in curve edit mode */
+ if (!STREQ(prop_id, "type")) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
/**
* Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
* option to force opened/closed strokes instead of just toggle behavior.
@@ -3166,6 +3701,7 @@ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_stroke_cyclical_set_exec;
ot->poll = gpencil_active_layer_poll;
+ ot->poll_property = gpencil_cyclical_set_curve_edit_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3194,7 +3730,6 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
-
const int type = RNA_enum_get(op->ptr, "type");
/* sanity checks */
@@ -3202,6 +3737,7 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ bool changed = false;
/* loop all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
if (gpl->actframe == NULL) {
@@ -3221,6 +3757,9 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
continue;
}
+ short prev_first = gps->caps[0];
+ short prev_last = gps->caps[1];
+
if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) {
++gps->caps[0];
if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
@@ -3237,13 +3776,19 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
gps->caps[0] = GP_STROKE_CAP_ROUND;
gps->caps[1] = GP_STROKE_CAP_ROUND;
}
+
+ if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
+ changed = true;
+ }
}
}
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);
+ if (changed) {
+ /* 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;
}
@@ -3539,6 +4084,11 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ if (is_curve_edit) {
+ return OPERATOR_CANCELLED;
+ }
+
if (activegpl->flag & GP_LAYER_LOCKED) {
return OPERATOR_CANCELLED;
}
@@ -3597,7 +4147,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
elem->used = true;
/* Create a new stroke. */
- bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(elem->gps, true);
+ bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(elem->gps, true, true);
gps_new->flag &= ~GP_STROKE_CYCLIC;
BLI_insertlinkbefore(&elem->gpf->strokes, elem->gps, gps_new);
@@ -3614,7 +4164,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
}
/* Calc geometry data for new stroke. */
- BKE_gpencil_stroke_geometry_update(gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new);
/* If join only, delete old strokes. */
if (type == GP_STROKE_JOIN) {
@@ -3670,7 +4220,7 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot)
/** \name Stroke Flip Operator
* \{ */
-static int gpencil_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -3680,6 +4230,8 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ bool changed = false;
/* read all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *gpf = gpl->actframe;
@@ -3698,16 +4250,25 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
continue;
}
- /* flip stroke */
- gpencil_flip_stroke(gps);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* flip stroke */
+ gpencil_flip_stroke(gps);
+ }
+
+ changed = true;
}
}
}
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);
+ if (changed) {
+ /* 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;
}
@@ -3742,19 +4303,26 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
int oldframe = (int)DEG_get_ctime(depsgraph);
const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
- /* Init space conversion stuff. */
- GP_SpaceConversion gsc = {NULL};
- SnapObjectContext *sctx = NULL;
- gpencil_point_conversion_init(C, &gsc);
/* Init snap context for geometry projection. */
+ SnapObjectContext *sctx = NULL;
sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C));
+ bool changed = false;
+ /* Init space conversion stuff. */
+ GP_SpaceConversion gsc = {NULL};
+ gpencil_point_conversion_init(C, &gsc);
int cfra_prv = INT_MIN;
/* Go through each editable + selected stroke, adjusting each of its points one by one... */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
+ bool curve_select = false;
+ if (is_curve_edit && gps->editcurve != NULL) {
+ curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ }
+
+ if (gps->flag & GP_STROKE_SELECT || curve_select) {
/* update frame to get the new location of objects */
if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
@@ -3764,6 +4332,17 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
}
ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
+
+ if (is_curve_edit && gps->editcurve != NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -3776,9 +4355,12 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
ED_transform_snap_object_context_destroy(sctx);
}
- /* update changed data */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ /* update changed data */
+ 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;
}
@@ -3846,10 +4428,11 @@ static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
+
/* update changed data */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -3943,134 +4526,176 @@ static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
return totnewpoints;
}
-static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
+static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDspoint *temp_points;
- const int cuts = RNA_int_get(op->ptr, "number_cuts");
-
int totnewpoints, oldtotpoints;
int i2;
+ /* loop as many times as cuts */
+ for (int s = 0; s < cuts; s++) {
+ totnewpoints = gpencil_count_subdivision_cuts(gps);
+ if (totnewpoints == 0) {
+ continue;
+ }
+ /* duplicate points in a temp area */
+ temp_points = MEM_dupallocN(gps->points);
+ oldtotpoints = gps->totpoints;
- /* sanity checks */
- if (ELEM(NULL, gpd)) {
- return OPERATOR_CANCELLED;
- }
+ MDeformVert *temp_dverts = NULL;
+ MDeformVert *dvert_final = NULL;
+ MDeformVert *dvert = NULL;
+ MDeformVert *dvert_next = NULL;
+ if (gps->dvert != NULL) {
+ temp_dverts = MEM_dupallocN(gps->dvert);
+ }
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* loop as many times as cuts */
- for (int s = 0; s < cuts; s++) {
- totnewpoints = gpencil_count_subdivision_cuts(gps);
- if (totnewpoints == 0) {
- continue;
- }
- /* duplicate points in a temp area */
- temp_points = MEM_dupallocN(gps->points);
- oldtotpoints = gps->totpoints;
-
- MDeformVert *temp_dverts = NULL;
- MDeformVert *dvert_final = NULL;
- MDeformVert *dvert = NULL;
- MDeformVert *dvert_next = NULL;
- if (gps->dvert != NULL) {
- temp_dverts = MEM_dupallocN(gps->dvert);
- }
+ /* resize the points arrays */
+ gps->totpoints += totnewpoints;
+ 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);
+ }
- /* resize the points arrays */
- gps->totpoints += totnewpoints;
- 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);
- }
+ /* loop and interpolate */
+ i2 = 0;
+ for (int i = 0; i < oldtotpoints; i++) {
+ bGPDspoint *pt = &temp_points[i];
+ bGPDspoint *pt_final = &gps->points[i2];
- /* loop and interpolate */
- i2 = 0;
- for (int i = 0; i < oldtotpoints; i++) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *pt_final = &gps->points[i2];
-
- /* copy current point */
- copy_v3_v3(&pt_final->x, &pt->x);
- pt_final->pressure = pt->pressure;
- pt_final->strength = pt->strength;
- pt_final->time = pt->time;
- pt_final->flag = pt->flag;
- copy_v4_v4(pt_final->vert_color, pt->vert_color);
-
- if (gps->dvert != NULL) {
- dvert = &temp_dverts[i];
- dvert_final = &gps->dvert[i2];
- dvert_final->totweight = dvert->totweight;
- dvert_final->dw = dvert->dw;
- }
- i2++;
+ /* copy current point */
+ copy_v3_v3(&pt_final->x, &pt->x);
+ pt_final->pressure = pt->pressure;
+ pt_final->strength = pt->strength;
+ pt_final->time = pt->time;
+ pt_final->flag = pt->flag;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
- /* if next point is selected add a half way point */
- if (pt->flag & GP_SPOINT_SELECT) {
- if (i + 1 < oldtotpoints) {
- if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
- pt_final = &gps->points[i2];
- if (gps->dvert != NULL) {
- dvert_final = &gps->dvert[i2];
- }
- /* Interpolate all values */
- bGPDspoint *next = &temp_points[i + 1];
- interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
- pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
- pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
- CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
- interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
- pt_final->time = interpf(pt->time, next->time, 0.5f);
- pt_final->flag |= GP_SPOINT_SELECT;
-
- /* interpolate weights */
- if (gps->dvert != NULL) {
- dvert = &temp_dverts[i];
- dvert_next = &temp_dverts[i + 1];
- dvert_final = &gps->dvert[i2];
-
- dvert_final->totweight = dvert->totweight;
- dvert_final->dw = MEM_dupallocN(dvert->dw);
-
- /* interpolate weight values */
- for (int d = 0; d < dvert->totweight; d++) {
- MDeformWeight *dw_a = &dvert->dw[d];
- if (dvert_next->totweight > d) {
- MDeformWeight *dw_b = &dvert_next->dw[d];
- MDeformWeight *dw_final = &dvert_final->dw[d];
- dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
- }
- }
- }
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[i];
+ dvert_final = &gps->dvert[i2];
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = dvert->dw;
+ }
+ i2++;
- i2++;
+ /* if next point is selected add a half way point */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (i + 1 < oldtotpoints) {
+ if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
+ pt_final = &gps->points[i2];
+ if (gps->dvert != NULL) {
+ dvert_final = &gps->dvert[i2];
+ }
+ /* Interpolate all values */
+ bGPDspoint *next = &temp_points[i + 1];
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->flag |= GP_SPOINT_SELECT;
+
+ /* interpolate weights */
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[i];
+ dvert_next = &temp_dverts[i + 1];
+ dvert_final = &gps->dvert[i2];
+
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+
+ /* interpolate weight values */
+ for (int d = 0; d < dvert->totweight; d++) {
+ MDeformWeight *dw_a = &dvert->dw[d];
+ if (dvert_next->totweight > d) {
+ MDeformWeight *dw_b = &dvert_next->dw[d];
+ MDeformWeight *dw_final = &dvert_final->dw[d];
+ dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
+ }
}
}
+
+ i2++;
}
}
- /* free temp memory */
- MEM_SAFE_FREE(temp_points);
- MEM_SAFE_FREE(temp_dverts);
}
+ }
+ /* free temp memory */
+ MEM_SAFE_FREE(temp_points);
+ MEM_SAFE_FREE(temp_dverts);
+ }
+}
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const int cuts = RNA_int_get(op->ptr, "number_cuts");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ bool changed = false;
+ if (is_curve_edit) {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (gpc->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_subdivide(gps, cuts);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ changed = true;
+ }
}
+ GP_EDITABLE_CURVES_END(gps_iter);
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else {
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ gpencil_stroke_subdivide(gps, cuts);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ changed = true;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
- /* smooth stroke */
- gpencil_smooth_stroke(C, op);
+ if (changed) {
+ /* smooth stroke */
+ gpencil_smooth_stroke(C, op);
+ }
+ }
- /* 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);
+ if (changed) {
+ /* 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;
}
+static bool gpencil_subdivide_curve_edit_poll_property(const bContext *C,
+ wmOperator *UNUSED(op),
+ const PropertyRNA *prop)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
+ const char *prop_id = RNA_property_identifier(prop);
+ /* Only show number_cuts in curve edit mode */
+ if (!STREQ(prop_id, "number_cuts")) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -4085,6 +4710,7 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_stroke_subdivide_exec;
ot->poll = gpencil_active_layer_poll;
+ ot->poll_property = gpencil_subdivide_curve_edit_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4120,18 +4746,29 @@ static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* simplify stroke using Ramer-Douglas-Peucker algorithm */
- BKE_gpencil_stroke_simplify_adaptive(gps, factor);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ bool changed = false;
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* simplify stroke using Ramer-Douglas-Peucker algorithm */
+ BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor);
+ changed = true;
+ }
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
}
- 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);
+ if (changed) {
+ /* 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;
}
@@ -4169,19 +4806,30 @@ static int gpencil_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
- for (int i = 0; i < steps; i++) {
- BKE_gpencil_stroke_simplify_fixed(gps);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ bool changed = false;
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ changed |= true;
+ for (int i = 0; i < steps; i++) {
+ BKE_gpencil_stroke_simplify_fixed(gpd, gps);
+ }
}
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
}
- 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);
+ if (changed) {
+ /* 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;
}
@@ -4223,7 +4871,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
/* Go through each editable + selected stroke */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_stroke_sample(gps, length, true);
+ BKE_gpencil_stroke_sample(gpd, gps, length, true);
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -4246,7 +4894,7 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_stroke_sample_exec;
- ot->poll = gpencil_active_layer_poll;
+ ot->poll = gpencil_stroke_not_in_curve_edit_mode;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4263,7 +4911,7 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot)
/** \name Stroke Trim Operator
* \{ */
-static int gpencil_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_trim_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -4274,6 +4922,7 @@ static int gpencil_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
/* Go through each editable + selected stroke */
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
@@ -4293,7 +4942,12 @@ static int gpencil_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
}
if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_stroke_trim(gps);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ BKE_gpencil_stroke_trim(gpd, gps);
+ }
}
}
/* if not multiedit, exit loop*/
@@ -4373,6 +5027,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
}
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
/* Create a new object. */
/* Take into account user preferences for duplicating actions. */
@@ -4434,27 +5089,32 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
/* selected points mode */
if (mode == GP_SEPARATE_POINT) {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* make copy of source stroke */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* Reassign material. */
- gps_dst->mat_nr = idx;
+ /* Reassign material. */
+ gps_dst->mat_nr = idx;
- /* link to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* link to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
- /* Invert selection status of all points in destination stroke */
- for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
- pt->flag ^= GP_SPOINT_SELECT;
- }
+ /* Invert selection status of all points in destination stroke */
+ for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
+ pt->flag ^= GP_SPOINT_SELECT;
+ }
- /* delete selected points from destination stroke */
- gpencil_stroke_delete_tagged_points(
- gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+ /* delete selected points from destination stroke */
+ gpencil_stroke_delete_tagged_points(
+ gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
- /* delete selected points from origin stroke */
- gpencil_stroke_delete_tagged_points(
- gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ /* delete selected points from origin stroke */
+ gpencil_stroke_delete_tagged_points(
+ gpd_src, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ }
}
/* selected strokes mode */
else if (mode == GP_SEPARATE_STROKE) {
@@ -4574,7 +5234,7 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
/** \name Stroke Split Operator
* \{ */
-static int gpencil_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
@@ -4586,6 +5246,7 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* loop strokes and split parts */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -4610,22 +5271,29 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
}
/* Split selected strokes. */
if (gps->flag & GP_STROKE_SELECT) {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* make copy of source stroke */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* link to same frame */
- BLI_addtail(&gpf->strokes, gps_dst);
+ /* link to same frame */
+ BLI_addtail(&gpf->strokes, gps_dst);
- /* invert selection status of all points in destination stroke */
- for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
- pt->flag ^= GP_SPOINT_SELECT;
- }
+ /* invert selection status of all points in destination stroke */
+ for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
+ pt->flag ^= GP_SPOINT_SELECT;
+ }
- /* delete selected points from destination stroke */
- gpencil_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
+ /* delete selected points from destination stroke */
+ gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
- /* delete selected points from origin stroke */
- gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ /* delete selected points from origin stroke */
+ gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ }
}
}
/* select again tagged points */
@@ -4705,7 +5373,7 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_stroke_smooth_exec;
- ot->poll = gpencil_active_layer_poll;
+ ot->poll = gpencil_stroke_not_in_curve_edit_mode;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4761,7 +5429,8 @@ typedef bool (*GPencilTestFn)(bGPDstroke *gps,
const float diff_mat[4][4],
void *user_data);
-static void gpencil_cutter_dissolve(bGPDlayer *hit_layer,
+static void gpencil_cutter_dissolve(bGPdata *gpd,
+ bGPDlayer *hit_layer,
bGPDstroke *hit_stroke,
const bool flat_caps)
{
@@ -4819,7 +5488,7 @@ static void gpencil_cutter_dissolve(bGPDlayer *hit_layer,
}
gpencil_stroke_delete_tagged_points(
- hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
+ gpd, hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
}
}
@@ -4877,7 +5546,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
gps->flag |= GP_STROKE_SELECT;
float r_hita[3], r_hitb[3];
if (gps->totpoints > 1) {
- ED_gpencil_select_stroke_segment(gpl, gps, pt, true, true, scale, r_hita, r_hitb);
+ ED_gpencil_select_stroke_segment(gpd, gpl, gps, pt, true, true, scale, r_hita, r_hitb);
}
/* avoid infinite loops */
if (gps->totpoints > oldtot) {
@@ -4907,7 +5576,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
}
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_SELECT) {
- gpencil_cutter_dissolve(gpl, gps, flat_caps);
+ gpencil_cutter_dissolve(gpd, gpl, gps, flat_caps);
}
}
}
@@ -5039,13 +5708,20 @@ static int gpencil_merge_by_distance_exec(bContext *C, wmOperator *op)
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_stroke_merge_distance(gpf_, gps, threshold, unselected);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ if (is_curve_edit) {
+ /* TODO: merge curve points by distance */
+ }
+ else {
+ /* Go through each editable selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_merge_distance(gpd, gpf_, gps, threshold, unselected);
+ }
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c
new file mode 100644
index 00000000000..60d1d2169b4
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_edit_curve.c
@@ -0,0 +1,214 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ * Operators for editing Grease Pencil strokes
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
+#include "BKE_gpencil_geom.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_gpencil.h"
+
+#include "DEG_depsgraph.h"
+
+#include "gpencil_intern.h"
+
+/* Poll callback for checking if there is an active layer and we are in curve edit mode. */
+static bool gpencil_curve_edit_mode_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (!GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ return (gpl != NULL);
+}
+
+static int gpencil_stroke_enter_editcurve_mode_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
+ float error_threshold = RNA_float_get(op->ptr, "error_threshold");
+ gpd->curve_edit_threshold = error_threshold;
+
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ if (gpf == gpl->actframe) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* only allow selected and non-converted strokes to be transformed */
+ if ((gps->flag & GP_STROKE_SELECT && gps->editcurve == NULL) ||
+ (gps->editcurve != NULL && gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE)) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ }
+ }
+ }
+
+ gpd->flag |= GP_DATA_CURVE_EDIT_MODE;
+
+ /* 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_enter_editcurve_mode(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Enter curve edit mode";
+ ot->idname = "GPENCIL_OT_stroke_enter_editcurve_mode";
+ ot->description = "Called to transform a stroke into a curve";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_enter_editcurve_mode_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_float(ot->srna,
+ "error_threshold",
+ 0.1f,
+ FLT_MIN,
+ 100.0f,
+ "Error Threshold",
+ "Threshold on the maximum deviation from the actual stroke",
+ FLT_MIN,
+ 10.f);
+ RNA_def_property_ui_range(prop, FLT_MIN, 10.0f, 0.1f, 5);
+}
+
+static int gpencil_editcurve_set_handle_type_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ const int handle_type = RNA_enum_get(op->ptr, "type");
+
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (bezt->f2 & SELECT) {
+ bezt->h1 = handle_type;
+ bezt->h2 = handle_type;
+ }
+ else {
+ if (bezt->f1 & SELECT) {
+ bezt->h1 = handle_type;
+ }
+ if (bezt->f3 & SELECT) {
+ bezt->h2 = handle_type;
+ }
+ }
+ }
+ }
+
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ GP_EDITABLE_CURVES_END(gps_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_editcurve_set_handle_type(wmOperatorType *ot)
+{
+ static const EnumPropertyItem editcurve_handle_type_items[] = {
+ {HD_FREE, "FREE", 0, "Free", ""},
+ {HD_AUTO, "AUTOMATIC", 0, "Automatic", ""},
+ {HD_VECT, "VECTOR", 0, "Vector", ""},
+ {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Set handle type";
+ ot->idname = "GPENCIL_OT_stroke_editcurve_set_handle_type";
+ ot->description = "Set the type of a edit curve handle";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = gpencil_editcurve_set_handle_type_exec;
+ ot->poll = gpencil_curve_edit_mode_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
+}
+
+/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index f06a1d6b6c8..93941ea3766 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1299,11 +1299,11 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
/* simplify stroke */
for (int b = 0; b < tgpf->fill_simplylvl; b++) {
- BKE_gpencil_stroke_simplify_fixed(gps);
+ BKE_gpencil_stroke_simplify_fixed(tgpf->gpd, gps);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps);
}
/* ----------------------- */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index e3e2199f8a3..e4e9a3ae0ab 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -343,7 +343,8 @@ struct GHash *gpencil_copybuf_validate_colormap(struct bContext *C);
/* Stroke Editing ------------------------------------ */
-void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
+void gpencil_stroke_delete_tagged_points(bGPdata *gpd,
+ bGPDframe *gpf,
bGPDstroke *gps,
bGPDstroke *next_stroke,
int tag_flags,
@@ -351,7 +352,7 @@ void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
int limit);
int gpencil_delete_selected_point_wrap(bContext *C);
-void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide);
+void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, const int subdivide);
/* Layers Enums -------------------------------------- */
@@ -447,6 +448,11 @@ void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot);
void GPENCIL_OT_reproject(struct wmOperatorType *ot);
void GPENCIL_OT_recalc_geometry(struct wmOperatorType *ot);
+/* stroke editcurve */
+
+void GPENCIL_OT_stroke_enter_editcurve_mode(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_editcurve_set_handle_type(struct wmOperatorType *ot);
+
/* stroke sculpting -- */
void GPENCIL_OT_sculpt_paint(struct wmOperatorType *ot);
@@ -693,6 +699,55 @@ struct GP_EditableStrokes_Iter {
(void)0
/**
+ * Iterate over all editable editcurves in the current context,
+ * stopping on each usable layer + stroke + curve pair (i.e. gpl, gps and gpc)
+ * to perform some operations on the curve.
+ *
+ * \param gpl: The identifier to use for the layer of the stroke being processed.
+ * Choose a suitable value to avoid name clashes.
+ * \param gps: The identifier to use for current stroke being processed.
+ * Choose a suitable value to avoid name clashes.
+ * \param gpc: The identifier to use for current editcurve being processed.
+ * Choose a suitable value to avoid name clashes.
+ */
+#define GP_EDITABLE_CURVES_BEGIN(gpstroke_iter, C, gpl, gps, gpc) \
+ { \
+ struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \
+ Depsgraph *depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C); \
+ Object *obact_ = CTX_data_active_object(C); \
+ bGPdata *gpd_ = CTX_data_gpencil_data(C); \
+ const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { \
+ bGPDframe *init_gpf_ = (is_multiedit_) ? gpl->frames.first : gpl->actframe; \
+ for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \
+ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \
+ BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \
+ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \
+ /* loop over strokes */ \
+ bGPDstroke *gpsn_; \
+ for (bGPDstroke *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->editcurve == NULL) \
+ continue; \
+ bGPDcurve *gpc = gps->editcurve; \
+ /* ... Do Stuff With Strokes ... */
+
+#define GP_EDITABLE_CURVES_END(gpstroke_iter) \
+ } \
+ } \
+ if (!is_multiedit_) { \
+ break; \
+ } \
+ } \
+ } \
+ CTX_DATA_END; \
+ } \
+ (void)0
+
+/**
* Iterate over all editable strokes using evaluated data in the current context,
* stopping on each usable layer + stroke pair (i.e. gpl and gps)
* to perform some operations on the stroke.
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 3a3a9bde38b..3617f20763e 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -184,7 +184,7 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
/* Add temp strokes. */
if (gpf) {
- bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true);
+ bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true, true);
gps_eval->flag |= GP_STROKE_TAG;
BLI_addtail(&gpf->strokes, gps_eval);
}
@@ -327,7 +327,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
}
/* create new stroke */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true);
+ new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
if (valid) {
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
@@ -353,7 +353,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
/* add to strokes */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
}
@@ -608,11 +608,11 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
}
/* make copy of source stroke, then adjust pointer to points too */
- gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
+ gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
gps_dst->flag &= ~GP_STROKE_TAG;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps_dst);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps_dst);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
@@ -1050,7 +1050,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* create new stroke */
- bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true);
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
@@ -1075,7 +1075,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
/* add to strokes */
BLI_addtail(&interFrame->strokes, new_stroke);
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 938f4ab26af..f795ed01bb8 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -172,6 +172,9 @@ static void gpencil_get_elements_len(bContext *C, int *totstrokes, int *totpoint
static void gpencil_dissolve_points(bContext *C)
{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *gpf = gpl->actframe;
if (gpf == NULL) {
@@ -179,7 +182,7 @@ static void gpencil_dissolve_points(bContext *C)
}
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
- gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
+ gpencil_stroke_delete_tagged_points(gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
CTX_DATA_END;
@@ -519,7 +522,7 @@ static int gpencil_stroke_merge_exec(bContext *C, wmOperator *op)
gpencil_dissolve_points(C);
}
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
/* free memory */
MEM_SAFE_FREE(original_array);
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 9f7725e01f5..33ac0c5bbbf 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -69,6 +69,13 @@ static bool gpencil_stroke_editmode_poll(bContext *C)
return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE));
}
+/* Poll callback for stroke curve editing mode */
+static bool gpencil_stroke_editmode_curve_poll(bContext *C)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ return (GPENCIL_EDIT_MODE(gpd) && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
+}
+
/* Poll callback for stroke painting mode */
static bool gpencil_stroke_paintmode_poll(bContext *C)
{
@@ -315,6 +322,15 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
keymap->poll = gpencil_stroke_editmode_poll;
}
+/* Stroke Curve Editing Keymap - Only when editmode is enabled and in curve edit mode */
+static void ed_keymap_gpencil_curve_editing(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Curve Edit Mode", 0, 0);
+
+ /* set poll callback - so that this keymap only gets enabled when curve editmode is enabled */
+ keymap->poll = gpencil_stroke_editmode_curve_poll;
+}
+
/* keys for draw with a drawing brush (no fill) */
static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf)
{
@@ -471,6 +487,7 @@ static void ed_keymap_gpencil_weightpainting_draw(wmKeyConfig *keyconf)
void ED_keymap_gpencil(wmKeyConfig *keyconf)
{
ed_keymap_gpencil_general(keyconf);
+ ed_keymap_gpencil_curve_editing(keyconf);
ed_keymap_gpencil_editing(keyconf);
ed_keymap_gpencil_painting(keyconf);
ed_keymap_gpencil_painting_draw(keyconf);
@@ -568,6 +585,11 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_sculpt_paint);
WM_operatortype_append(GPENCIL_OT_weight_paint);
+ /* Edit stroke editcurve */
+
+ WM_operatortype_append(GPENCIL_OT_stroke_enter_editcurve_mode);
+ WM_operatortype_append(GPENCIL_OT_stroke_editcurve_set_handle_type);
+
/* Editing (Buttons) ------------ */
WM_operatortype_append(GPENCIL_OT_annotation_add);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 82c30dea91d..e544093cd1d 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1195,7 +1195,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* subdivide and smooth the stroke */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
- gpencil_subdivide_stroke(gps, subdivide);
+ gpencil_subdivide_stroke(gpd, gps, subdivide);
}
/* Smooth stroke after subdiv - only if there's something to do for each iteration,
@@ -1226,7 +1226,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* Simplify adaptive */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->simplify_f > 0.0f)) {
- BKE_gpencil_stroke_simplify_adaptive(gps, brush->gpencil_settings->simplify_f);
+ BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
}
/* reproject to plane (only in 3d space) */
@@ -1279,11 +1279,11 @@ static void gpencil_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_stroke_trim(gps);
+ BKE_gpencil_stroke_trim(gpd, gps);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gpencil_stroke_added_enable(p);
}
@@ -1652,7 +1652,7 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
gpencil_stroke_soft_refine(gps);
}
- gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
+ gpencil_stroke_delete_tagged_points(p->gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
gpencil_update_cache(p->gpd);
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 55180885c5d..801dacb3e6b 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1082,7 +1082,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
/* Update evaluated data. */
ED_gpencil_sbuffer_update_eval(tgpi->gpd, tgpi->ob_eval);
@@ -1323,7 +1323,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
copy_v2_v2(gps->aspect_ratio, brush_settings->aspect_ratio);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps);
}
/* transfer stroke from temporary buffer to the actual frame */
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 1d500223ab8..ed18c2eed5d 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -303,7 +303,7 @@ static void gpencil_update_geometry(bGPdata *gpd)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_TAG) {
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
gps->flag &= ~GP_STROKE_TAG;
}
}
@@ -1021,7 +1021,7 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
/* Make a new stroke */
- new_stroke = BKE_gpencil_stroke_duplicate(gps, true);
+ new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
new_stroke->next = new_stroke->prev = NULL;
BLI_addtail(&gpf->strokes, new_stroke);
@@ -1574,6 +1574,7 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C,
bool changed = false;
bool redo_geom = false;
Object *ob = gso->object;
+ bGPdata *gpd = ob->data;
char tool = gso->brush->gpencil_sculpt_tool;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
@@ -1672,7 +1673,7 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C,
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
/* Update active frame now, only if material has fill. */
if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
- BKE_gpencil_stroke_geometry_update(gps_active);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_active);
}
else {
gpencil_recalc_geometry_tag(gps_active);
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index 82d5eedf576..ddd8a11d9b5 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -43,10 +43,13 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_material.h"
#include "BKE_report.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -58,6 +61,7 @@
#include "ED_gpencil.h"
#include "ED_select_utils.h"
+#include "ED_view3d.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -126,6 +130,86 @@ static bool gpencil_select_poll(bContext *C)
return false;
}
+static bool gpencil_3d_point_to_screen_space(ARegion *region,
+ const float diff_mat[4][4],
+ const float co[3],
+ int r_co[2])
+{
+ float parent_co[3];
+ mul_v3_m4v3(parent_co, diff_mat, co);
+ int screen_co[2];
+ if (ED_view3d_project_int_global(
+ region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
+ V3D_PROJ_RET_OK) {
+ if (!ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1])) {
+ copy_v2_v2_int(r_co, screen_co);
+ return true;
+ }
+ }
+ r_co[0] = V2D_IS_CLIPPED;
+ r_co[1] = V2D_IS_CLIPPED;
+ return false;
+}
+
+/* helper to deselect all selected strokes/points */
+static void deselect_all_selected(bContext *C)
+{
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ /* deselect stroke and its points if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+
+ /* deselect points */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ /* deselect stroke itself too */
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+
+ /* deselect curve and curve points */
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int j = 0; j < gpc->tot_curve_points; j++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[j];
+ BezTriple *bezt = &gpc_pt->bezt;
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ }
+ CTX_DATA_END;
+}
+
+static void select_all_curve_points(bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
+{
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (deselect == false) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ else {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ }
+
+ if (deselect == false) {
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -149,6 +233,7 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
int action = RNA_enum_get(op->ptr, "action");
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -168,7 +253,12 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
}
}
- ED_gpencil_select_toggle_all(C, action);
+ if (is_curve_edit) {
+ ED_gpencil_select_curve_toggle_all(C, action);
+ }
+ else {
+ ED_gpencil_select_toggle_all(C, action);
+ }
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
@@ -207,6 +297,7 @@ void GPENCIL_OT_select_all(wmOperatorType *ot)
static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -218,18 +309,34 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* select all points in selected strokes */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
+ if (is_curve_edit) {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (gpc->flag & GP_CURVE_SELECT) {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ }
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+ else {
+ /* select all points in selected strokes */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag |= GP_SPOINT_SELECT;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
}
}
+ CTX_DATA_END;
}
- CTX_DATA_END;
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
@@ -265,8 +372,9 @@ void GPENCIL_OT_select_linked(wmOperatorType *ot)
static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
{
- const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -278,47 +386,94 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* select all points in selected strokes */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
- bGPDspoint *pt;
- int row = 0;
- int start = 0;
- if (unselect_ends) {
- start = 1;
- }
+ bool changed = false;
+ if (is_curve_edit) {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
+ int idx = 0;
+ int start = 0;
+ if (unselect_ends) {
+ start = 1;
+ }
- for (int i = start; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- if ((row % 2) == 0) {
- pt->flag |= GP_SPOINT_SELECT;
+ for (int i = start; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ if ((idx % 2) == 0) {
+ gpc_pt->flag |= GP_SPOINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
+ }
+ else {
+ gpc_pt->flag &= ~GP_SPOINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+ }
+ idx++;
}
- else {
- pt->flag &= ~GP_SPOINT_SELECT;
+
+ if (unselect_ends) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[0];
+ gpc_pt->flag &= ~GP_SPOINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+
+ gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1];
+ gpc_pt->flag &= ~GP_SPOINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
}
- row++;
+
+ BKE_gpencil_curve_sync_selection(gps);
+ changed = true;
}
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+ else {
+ /* select all points in selected strokes */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
+ bGPDspoint *pt;
+ int row = 0;
+ int start = 0;
+ if (unselect_ends) {
+ start = 1;
+ }
- /* unselect start and end points */
- if (unselect_ends) {
- pt = &gps->points[0];
- pt->flag &= ~GP_SPOINT_SELECT;
+ for (int i = start; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ if ((row % 2) == 0) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ row++;
+ }
- pt = &gps->points[gps->totpoints - 1];
- pt->flag &= ~GP_SPOINT_SELECT;
+ /* unselect start and end points */
+ if (unselect_ends) {
+ pt = &gps->points[0];
+ pt->flag &= ~GP_SPOINT_SELECT;
+
+ pt = &gps->points[gps->totpoints - 1];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ changed = true;
}
}
+ CTX_DATA_END;
}
- CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
return OPERATOR_FINISHED;
}
@@ -365,10 +520,13 @@ typedef enum eGP_SelectGrouped {
/* ----------------------------------- */
/* On each visible layer, check for selected strokes - if found, select all others */
-static void gpencil_select_same_layer(bContext *C)
+static bool gpencil_select_same_layer(bContext *C)
{
Scene *scene = CTX_data_scene(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV);
bGPDstroke *gps;
@@ -390,29 +548,55 @@ static void gpencil_select_same_layer(bContext *C)
/* Select all if found */
if (found) {
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- bGPDspoint *pt;
- int i;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag |= GP_SPOINT_SELECT;
+ if (is_curve_edit) {
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (gps->editcurve != NULL && ED_gpencil_stroke_can_use(C, gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
+ }
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+
+ changed = true;
}
+ }
+ }
+ else {
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (ED_gpencil_stroke_can_use(C, gps)) {
+ bGPDspoint *pt;
+ int i;
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+
+ gps->flag |= GP_STROKE_SELECT;
- gps->flag |= GP_STROKE_SELECT;
+ changed = true;
+ }
}
}
}
}
CTX_DATA_END;
+
+ return changed;
}
/* Select all strokes with same colors as selected ones */
-static void gpencil_select_same_material(bContext *C)
+static bool gpencil_select_same_material(bContext *C)
{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* First, build set containing all the colors of selected strokes */
GSet *selected_colors = BLI_gset_str_new("GP Selected Colors");
+ bool changed = false;
+
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
if (gps->flag & GP_STROKE_SELECT) {
/* add instead of insert here, otherwise the uniqueness check gets skipped,
@@ -424,25 +608,48 @@ static void gpencil_select_same_material(bContext *C)
CTX_DATA_END;
/* Second, select any visible stroke that uses these colors */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
- /* select this stroke */
- bGPDspoint *pt;
- int i;
+ if (is_curve_edit) {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (gps->editcurve != NULL && BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
+ }
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag |= GP_SPOINT_SELECT;
+ changed = true;
}
+ }
+ CTX_DATA_END;
+ }
+ else {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ /* select this stroke */
+ bGPDspoint *pt;
+ int i;
- gps->flag |= GP_STROKE_SELECT;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+
+ gps->flag |= GP_STROKE_SELECT;
+
+ changed = true;
+ }
}
+ CTX_DATA_END;
}
- CTX_DATA_END;
/* free memomy */
if (selected_colors != NULL) {
BLI_gset_free(selected_colors, NULL);
}
+
+ return changed;
}
/* ----------------------------------- */
@@ -456,12 +663,14 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ bool changed = false;
+
switch (mode) {
case GP_SEL_SAME_LAYER:
- gpencil_select_same_layer(C);
+ changed = gpencil_select_same_layer(C);
break;
case GP_SEL_SAME_MATERIAL:
- gpencil_select_same_material(C);
+ changed = gpencil_select_same_material(C);
break;
default:
@@ -469,14 +678,16 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op)
break;
}
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
return OPERATOR_FINISHED;
}
@@ -515,6 +726,8 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot)
static int gpencil_select_first_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
/* if not edit/sculpt mode, the event is catched but not processed */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
return OPERATOR_CANCELLED;
@@ -523,6 +736,7 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes");
const bool extend = RNA_boolean_get(op->ptr, "extend");
+ bool changed = false;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* skip stroke if we're only manipulating selected strokes */
if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
@@ -532,30 +746,53 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
/* select first point */
BLI_assert(gps->totpoints >= 1);
- gps->points->flag |= GP_SPOINT_SELECT;
- gps->flag |= GP_STROKE_SELECT;
+ if (is_curve_edit) {
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ gpc->curve_points[0].flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ if ((extend == false) && (gps->totpoints > 1)) {
+ for (int i = 1; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+ }
+ }
+ changed = true;
+ }
+ }
+ else {
+ gps->points->flag |= GP_SPOINT_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
- /* deselect rest? */
- if ((extend == false) && (gps->totpoints > 1)) {
- /* start from index 1, to skip the first point that we'd just selected... */
- bGPDspoint *pt = &gps->points[1];
- int i = 1;
+ /* deselect rest? */
+ if ((extend == false) && (gps->totpoints > 1)) {
+ /* start from index 1, to skip the first point that we'd just selected... */
+ bGPDspoint *pt = &gps->points[1];
+ int i = 1;
- for (; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ for (; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
}
+ changed = true;
}
}
CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
return OPERATOR_FINISHED;
}
@@ -590,12 +827,14 @@ void GPENCIL_OT_select_first(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Select First
+/** \name Select Last
* \{ */
static int gpencil_select_last_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
/* if not edit/sculpt mode, the event is catched but not processed */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
return OPERATOR_CANCELLED;
@@ -604,6 +843,7 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes");
const bool extend = RNA_boolean_get(op->ptr, "extend");
+ bool changed = false;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* skip stroke if we're only manipulating selected strokes */
if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
@@ -613,30 +853,54 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
/* select last point */
BLI_assert(gps->totpoints >= 1);
- gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
- gps->flag |= GP_STROKE_SELECT;
+ if (is_curve_edit) {
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ gpc->curve_points[gpc->tot_curve_points - 1].flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc->curve_points[gpc->tot_curve_points - 1].bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ if ((extend == false) && (gps->totpoints > 1)) {
+ for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+ }
+ }
+ changed = true;
+ }
+ }
+ else {
+ gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
- /* deselect rest? */
- if ((extend == false) && (gps->totpoints > 1)) {
- /* don't include the last point... */
- bGPDspoint *pt = gps->points;
- int i = 1;
+ /* deselect rest? */
+ if ((extend == false) && (gps->totpoints > 1)) {
+ /* don't include the last point... */
+ bGPDspoint *pt = gps->points;
+ int i = 0;
- for (; i < gps->totpoints - 1; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ for (; i < gps->totpoints - 1; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
}
+
+ changed = true;
}
}
CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
return OPERATOR_FINISHED;
}
@@ -677,64 +941,118 @@ void GPENCIL_OT_select_last(wmOperatorType *ot)
static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* if not edit/sculpt mode, the event is catched but not processed */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
return OPERATOR_CANCELLED;
}
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
- bool prev_sel;
-
- /* First Pass: Go in forward order,
- * expanding selection if previous was selected (pre changes).
- * - This pass covers the "after" edges of selection islands
- */
- prev_sel = false;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* selected point - just set flag for next point */
- prev_sel = true;
+ bool changed = false;
+ if (is_curve_edit) {
+ GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
+ if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
+ bGPDcurve *editcurve = gps->editcurve;
+
+ bool prev_sel = false;
+ for (int i = 0; i < editcurve->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* selected point - just set flag for next point */
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ changed = true;
+ }
+ prev_sel = false;
+ }
}
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- pt->flag |= GP_SPOINT_SELECT;
+
+ prev_sel = false;
+ for (int i = editcurve->tot_curve_points - 1; i >= 0; i--) {
+ bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ changed = true;
+ }
+ prev_sel = false;
}
- prev_sel = false;
}
}
+ }
+ GP_EDITABLE_STROKES_END(gp_iter);
+ }
+ else {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+ bool prev_sel;
- /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
- * - This pass covers the "before" edges of selection islands
- */
- prev_sel = false;
- for (pt -= 1; i > 0; i--, pt--) {
- if (pt->flag & GP_SPOINT_SELECT) {
- prev_sel = true;
+ /* First Pass: Go in forward order,
+ * expanding selection if previous was selected (pre changes).
+ * - This pass covers the "after" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* selected point - just set flag for next point */
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ pt->flag |= GP_SPOINT_SELECT;
+ changed = true;
+ }
+ prev_sel = false;
+ }
}
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- pt->flag |= GP_SPOINT_SELECT;
+
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (pt -= 1; i > 0; i--, pt--) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ pt->flag |= GP_SPOINT_SELECT;
+ changed = true;
+ }
+ prev_sel = false;
}
- prev_sel = false;
}
}
}
+ CTX_DATA_END;
}
- CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
return OPERATOR_FINISHED;
}
@@ -762,65 +1080,125 @@ void GPENCIL_OT_select_more(wmOperatorType *ot)
static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
/* if not edit/sculpt mode, the event is catched but not processed */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
return OPERATOR_CANCELLED;
}
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
- bool prev_sel;
+ bool changed = false;
+ if (is_curve_edit) {
+ GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
+ if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
+ bGPDcurve *editcurve = gps->editcurve;
+ int i;
- /* First Pass: Go in forward order, shrinking selection
- * if previous was not selected (pre changes).
- * - This pass covers the "after" edges of selection islands
- */
- prev_sel = false;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* shrink if previous wasn't selected */
- if (prev_sel == false) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ bool prev_sel = false;
+ for (i = 0; i < editcurve->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ changed = true;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
- prev_sel = true;
}
- else {
- /* mark previous as being unselected - and hence, is trigger for shrinking */
- prev_sel = false;
+
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = editcurve->tot_curve_points - 1; i > 0; i--) {
+ bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ changed = true;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
}
}
+ }
+ GP_EDITABLE_STROKES_END(gp_iter);
+ }
+ else {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+ bool prev_sel;
- /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
- * - This pass covers the "before" edges of selection islands
- */
- prev_sel = false;
- for (pt -= 1; i > 0; i--, pt--) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* shrink if previous wasn't selected */
- if (prev_sel == false) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ /* First Pass: Go in forward order, shrinking selection
+ * if previous was not selected (pre changes).
+ * - This pass covers the "after" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ changed = true;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
- prev_sel = true;
}
- else {
- /* mark previous as being unselected - and hence, is trigger for shrinking */
- prev_sel = false;
+
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (pt -= 1; i > 0; i--, pt--) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ changed = true;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
}
}
}
+ CTX_DATA_END;
}
- CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
return OPERATOR_FINISHED;
}
@@ -852,7 +1230,7 @@ void GPENCIL_OT_select_less(wmOperatorType *ot)
* from gpencil_paint.c #gpencil_stroke_eraser_dostroke().
* It would be great to de-duplicate the logic here sometime, but that can wait.
*/
-static bool gpencil_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
+static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
bGPDlayer *gpl,
bGPDstroke *gps,
GP_SpaceConversion *gsc,
@@ -863,7 +1241,8 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
rcti *rect,
const float diff_mat[4][4],
const int selectmode,
- const float scale)
+ const float scale,
+ const bool is_curve_edit)
{
bGPDspoint *pt = NULL;
int x0 = 0, y0 = 0;
@@ -906,7 +1285,7 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
float r_hita[3], r_hitb[3];
bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
ED_gpencil_select_stroke_segment(
- gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
+ gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
}
}
}
@@ -927,17 +1306,119 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *UNUSED(gpd),
}
}
+ /* If curve edit mode, generate the curve. */
+ if (is_curve_edit && hit && gps_active->editcurve == NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
+ gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ /* Select all curve points. */
+ select_all_curve_points(gps_active, gps_active->editcurve, false);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_active);
+ changed = true;
+ }
+
/* Ensure that stroke selection is in sync with its points. */
BKE_gpencil_stroke_sync_selection(gps_active);
return changed;
}
+static bool gpencil_do_curve_circle_sel(bContext *C,
+ bGPDstroke *gps,
+ bGPDcurve *gpc,
+ const int mx,
+ const int my,
+ const int radius,
+ const bool select,
+ rcti *rect,
+ const float diff_mat[4][4],
+ const int selectmode)
+{
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
+
+ bool hit = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (bezt->hide == 1) {
+ continue;
+ }
+
+ const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
+ (!only_selected || BEZT_ISSEL_ANY(bezt));
+
+ /* if the handles are not visible only check ctrl point (vec[1])*/
+ int from = (!handles_visible) ? 1 : 0;
+ int to = (!handles_visible) ? 2 : 3;
+
+ for (int j = from; j < to; j++) {
+ float parent_co[3];
+ mul_v3_m4v3(parent_co, diff_mat, bezt->vec[j]);
+ int screen_co[2];
+ /* do 2d projection */
+ if (ED_view3d_project_int_global(
+ region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) !=
+ V3D_PROJ_RET_OK) {
+ continue;
+ }
+
+ /* view and bounding box test */
+ if (ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1]) &&
+ !BLI_rcti_isect_pt(rect, screen_co[0], screen_co[1])) {
+ continue;
+ }
+
+ /* test inside circle */
+ int dist_x = screen_co[0] - mx;
+ int dist_y = screen_co[1] - my;
+ int dist = dist_x * dist_x + dist_y * dist_y;
+ if (dist <= radius * radius) {
+ hit = true;
+ /* change selection */
+ if (select) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(bezt, j);
+ }
+ else {
+ BEZT_DESEL_IDX(bezt, j);
+ if (!BEZT_ISSEL_ANY(bezt)) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ }
+ }
+ }
+ }
+ }
+
+ /* select the entire curve */
+ if (hit && (selectmode == GP_SELECTMODE_STROKE)) {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (select) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ else {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ }
+ }
+
+ BKE_gpencil_curve_sync_selection(gps);
+
+ return hit;
+}
+
static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int selectmode;
if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
@@ -963,12 +1444,6 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
const int my = RNA_int_get(op->ptr, "y");
const int radius = RNA_int_get(op->ptr, "radius");
- GP_SpaceConversion gsc = {NULL};
- /* for bounding rect around circle (for quicky intersection testing) */
- rcti rect = {0};
-
- bool changed = false;
-
/* sanity checks */
if (area == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active area");
@@ -978,36 +1453,57 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
WM_gesture_is_modal_first(op->customdata));
const bool select = (sel_op != SEL_OP_SUB);
- if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_gpencil_select_toggle_all(C, SEL_DESELECT);
- changed = true;
- }
- /* init space conversion stuff */
- gpencil_point_conversion_init(C, &gsc);
-
- /* rect is rectangle of selection circle */
+ bool changed = false;
+ /* for bounding rect around circle (for quicky intersection testing) */
+ rcti rect = {0};
rect.xmin = mx - radius;
rect.ymin = my - radius;
rect.xmax = mx + radius;
rect.ymax = my + radius;
- /* find visible strokes, and select if hit */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- changed |= gpencil_stroke_do_circle_sel(gpd,
- gpl,
- gps,
- &gsc,
- mx,
- my,
- radius,
- select,
- &rect,
- gpstroke_iter.diff_mat,
- selectmode,
- scale);
+ if (is_curve_edit) {
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ ED_gpencil_select_curve_toggle_all(C, SEL_DESELECT);
+ changed = true;
+ }
+
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ changed |= gpencil_do_curve_circle_sel(
+ C, gps, gpc, mx, my, radius, select, &rect, gps_iter.diff_mat, selectmode);
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+
+ if (changed == false) {
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
+
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ ED_gpencil_select_toggle_all(C, SEL_DESELECT);
+ changed = true;
+ }
+
+ /* find visible strokes, and select if hit */
+ GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ changed |= gpencil_stroke_do_circle_sel(gpd,
+ gpl,
+ gps,
+ &gsc,
+ mx,
+ my,
+ radius,
+ select,
+ &rect,
+ gpstroke_iter.diff_mat,
+ selectmode,
+ scale,
+ is_curve_edit);
+ }
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
/* updates */
if (changed) {
@@ -1054,49 +1550,245 @@ void GPENCIL_OT_select_circle(wmOperatorType *ot)
*
* \{ */
-typedef bool (*GPencilTestFn)(bGPDstroke *gps,
- bGPDspoint *pt,
- const GP_SpaceConversion *gsc,
- const float diff_mat[4][4],
- void *user_data);
+typedef struct GP_SelectUserData {
+ int mx, my, radius;
+ /* Bounding box rect */
+ rcti rect;
+ const int (*lasso_coords)[2];
+ int lasso_coords_len;
+} GP_SelectUserData;
-static int gpencil_generic_select_exec(
- bContext *C, wmOperator *op, GPencilTestFn is_inside_fn, rcti box, void *user_data)
+typedef bool (*GPencilTestFn)(ARegion *region,
+ const float diff_mat[4][4],
+ const float pt[3],
+ GP_SelectUserData *user_data);
+
+#if 0
+static bool gpencil_stroke_fill_isect_rect(ARegion *region,
+ bGPDstroke *gps,
+ const float diff_mat[4][4],
+ rcti rect)
{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- ScrArea *area = CTX_wm_area(C);
+ int min[2] = {-INT_MAX, -INT_MAX};
+ int max[2] = {INT_MAX, INT_MAX};
- int selectmode;
- if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
- selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
- }
- else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
- selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
- }
- else {
- selectmode = ts->gpencil_selectmode_edit;
+ int(*points2d)[2] = MEM_callocN(sizeof(int[2]) * gps->totpoints, __func__);
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ int *pt2d = points2d[i];
+
+ int screen_co[2];
+ gpencil_3d_point_to_screen_space(region, diff_mat, &pt->x, screen_co);
+ DO_MINMAX2(screen_co, min, max);
+
+ copy_v2_v2_int(pt2d, screen_co);
}
- const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) &&
- ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
- const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) &&
- ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
+ bool hit = false;
+ /* check bounding box */
+ rcti bb = {min[0], max[0], min[1], max[1]};
+ if (BLI_rcti_isect(&rect, &bb, NULL)) {
+ for (int i = 0; i < gps->tot_triangles; i++) {
+ bGPDtriangle *tri = &gps->triangles[i];
+ int pt1[2], pt2[2], pt3[2];
+ int tri_min[2] = {-INT_MAX, -INT_MAX};
+ int tri_max[2] = {INT_MAX, INT_MAX};
+
+ copy_v2_v2_int(pt1, points2d[tri->verts[0]]);
+ copy_v2_v2_int(pt2, points2d[tri->verts[1]]);
+ copy_v2_v2_int(pt3, points2d[tri->verts[2]]);
+
+ DO_MINMAX2(pt1, tri_min, tri_max);
+ DO_MINMAX2(pt2, tri_min, tri_max);
+ DO_MINMAX2(pt3, tri_min, tri_max);
+
+ rcti tri_bb = {tri_min[0], tri_max[0], tri_min[1], tri_max[1]};
+ /* Case 1: triangle is entirely inside box selection */
+ /* (XXX: Can this even happen with no point inside the box?) */
+ if (BLI_rcti_inside_rcti(&tri_bb, &rect)) {
+ hit = true;
+ break;
+ }
- const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
- const float scale = ts->gp_sculpt.isect_threshold;
+ /* Case 2: rectangle intersects sides of triangle */
+ if (BLI_rcti_isect_segment(&rect, pt1, pt2) || BLI_rcti_isect_segment(&rect, pt2, pt3) ||
+ BLI_rcti_isect_segment(&rect, pt3, pt1)) {
+ hit = true;
+ break;
+ }
- GP_SpaceConversion gsc = {NULL};
+ /* TODO: Case 3: rectangle is inside the triangle */
+ }
+ }
+
+ MEM_freeN(points2d);
+ return hit;
+}
+#endif
+
+static bool gpencil_generic_curve_select(bContext *C,
+ Object *UNUSED(ob),
+ GPencilTestFn is_inside_fn,
+ rcti UNUSED(box),
+ GP_SelectUserData *user_data,
+ const bool strokemode,
+ const eSelectOp sel_op)
+{
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ const bool handle_only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
+ const bool handle_all = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
+ bool hit = false;
bool changed = false;
+ bool whole = false;
- /* sanity checks */
- if (area == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No active area");
- return OPERATOR_CANCELLED;
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ bool any_select = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (bezt->hide == 1) {
+ continue;
+ }
+
+ const bool handles_visible = (handle_all || (handle_only_selected &&
+ (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
+
+ if (handles_visible) {
+ for (int j = 0; j < 3; j++) {
+ const bool is_select = BEZT_ISSEL_IDX(bezt, j);
+ bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[j], user_data);
+ if (strokemode) {
+ if (is_inside) {
+ hit = true;
+ any_select = true;
+ break;
+ }
+ }
+ else {
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
+ if (sel_op_result != -1) {
+ if (sel_op_result) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(bezt, j);
+ any_select = true;
+ }
+ else {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt, j);
+ }
+ changed = true;
+ hit = true;
+ }
+ else {
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt, j);
+ }
+ }
+ }
+ }
+ }
+ /* if the handles are not visible only check ctrl point (vec[1])*/
+ else {
+ const bool is_select = bezt->f2;
+ bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[1], user_data);
+ if (strokemode) {
+ if (is_inside) {
+ hit = true;
+ any_select = true;
+ }
+ }
+ else {
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
+ if (sel_op_result != -1) {
+ if (sel_op_result) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ bezt->f2 |= SELECT;
+ any_select = true;
+ }
+ else {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ bezt->f2 &= ~SELECT;
+ }
+ changed = true;
+ hit = true;
+ }
+ else {
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ bezt->f2 &= ~SELECT;
+ }
+ }
+ }
+ }
+ }
+
+ /* TODO: Fix selection for filled in curves. */
+#if 0
+ if (!hit) {
+ /* check if we selected the inside of a filled curve */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
+ continue;
+ }
+
+ whole = gpencil_stroke_fill_isect_rect(region, gps, gps_iter.diff_mat, box);
+ }
+#endif
+ /* select the entire curve */
+ if (strokemode || whole) {
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, any_select, hit || whole);
+ if (sel_op_result != -1) {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (sel_op_result) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ else {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ }
+
+ if (sel_op_result) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ changed = true;
+ }
+ }
+
+ BKE_gpencil_curve_sync_selection(gps);
}
+ GP_EDITABLE_CURVES_END(gps_iter);
+ return changed;
+}
+
+static bool gpencil_generic_stroke_select(bContext *C,
+ Object *ob,
+ bGPdata *gpd,
+ GPencilTestFn is_inside_fn,
+ rcti box,
+ GP_SelectUserData *user_data,
+ const bool strokemode,
+ const bool segmentmode,
+ const eSelectOp sel_op,
+ const float scale,
+ const bool is_curve_edit)
+{
+ GP_SpaceConversion gsc = {NULL};
+ bool changed = false;
/* init space conversion stuff */
gpencil_point_conversion_init(C, &gsc);
@@ -1130,7 +1822,7 @@ static int gpencil_generic_select_exec(
bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
/* convert point coords to screenspace */
- const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
+ const bool is_inside = is_inside_fn(gsc.region, gpstroke_iter.diff_mat, &pt->x, user_data);
if (strokemode == false) {
const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0;
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
@@ -1144,7 +1836,7 @@ static int gpencil_generic_select_exec(
bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
float r_hita[3], r_hitb[3];
ED_gpencil_select_stroke_segment(
- gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
+ gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
}
}
}
@@ -1191,11 +1883,82 @@ static int gpencil_generic_select_exec(
}
}
+ /* If curve edit mode, generate the curve. */
+ if (is_curve_edit && (hit || whole) && gps_active->editcurve == NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
+ gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ /* Select all curve points. */
+ select_all_curve_points(gps_active, gps_active->editcurve, false);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_active);
+ changed = true;
+ }
+
/* Ensure that stroke selection is in sync with its points */
BKE_gpencil_stroke_sync_selection(gps_active);
}
GP_EVALUATED_STROKES_END(gpstroke_iter);
+ return changed;
+}
+
+static int gpencil_generic_select_exec(bContext *C,
+ wmOperator *op,
+ GPencilTestFn is_inside_fn,
+ rcti box,
+ GP_SelectUserData *user_data)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ ScrArea *area = CTX_wm_area(C);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ int selectmode;
+ if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
+ selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
+ }
+ else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
+ selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ }
+ else {
+ selectmode = ts->gpencil_selectmode_edit;
+ }
+
+ const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) &&
+ ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
+ const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) &&
+ ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
+
+ const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ const float scale = ts->gp_sculpt.isect_threshold;
+
+ bool changed = false;
+
+ /* sanity checks */
+ if (area == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active area");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (is_curve_edit) {
+ changed = gpencil_generic_curve_select(
+ C, ob, is_inside_fn, box, user_data, strokemode, sel_op);
+ }
+
+ if (changed == false) {
+ changed = gpencil_generic_stroke_select(C,
+ ob,
+ gpd,
+ is_inside_fn,
+ box,
+ user_data,
+ strokemode,
+ segmentmode,
+ sel_op,
+ scale,
+ is_curve_edit);
+ }
+
/* if paint mode,delete selected points */
if (GPENCIL_PAINT_MODE(gpd)) {
gpencil_delete_selected_point_wrap(C);
@@ -1222,27 +1985,21 @@ static int gpencil_generic_select_exec(
/** \name Box Select Operator
* \{ */
-struct GP_SelectBoxUserData {
- rcti rect;
-};
-
-static bool gpencil_test_box(bGPDstroke *gps,
- bGPDspoint *pt,
- const GP_SpaceConversion *gsc,
+static bool gpencil_test_box(ARegion *region,
const float diff_mat[4][4],
- void *user_data)
+ const float pt[3],
+ GP_SelectUserData *user_data)
{
- const struct GP_SelectBoxUserData *data = user_data;
- bGPDspoint pt2;
- int x0, y0;
- gpencil_point_to_parent_space(pt, diff_mat, &pt2);
- gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
- return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0));
+ int co[2] = {0};
+ if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
+ return BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]);
+ }
+ return false;
}
static int gpencil_box_select_exec(bContext *C, wmOperator *op)
{
- struct GP_SelectBoxUserData data = {0};
+ GP_SelectUserData data = {0};
WM_operator_properties_border_to_rcti(op, &data.rect);
rcti rect = data.rect;
return gpencil_generic_select_exec(C, op, gpencil_test_box, rect, &data);
@@ -1277,45 +2034,38 @@ void GPENCIL_OT_select_box(wmOperatorType *ot)
/** \name Lasso Select Operator
* \{ */
-struct GP_SelectLassoUserData {
- rcti rect;
- const int (*mcoords)[2];
- int mcoords_len;
-};
-
-static bool gpencil_test_lasso(bGPDstroke *gps,
- bGPDspoint *pt,
- const GP_SpaceConversion *gsc,
+static bool gpencil_test_lasso(ARegion *region,
const float diff_mat[4][4],
- void *user_data)
+ const float pt[3],
+ GP_SelectUserData *user_data)
{
- const struct GP_SelectLassoUserData *data = user_data;
- bGPDspoint pt2;
- int x0, y0;
- gpencil_point_to_parent_space(pt, diff_mat, &pt2);
- gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
- /* test if in lasso boundbox + within the lasso noose */
- return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) &&
- BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX));
+ int co[2] = {0};
+ if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
+ /* test if in lasso boundbox + within the lasso noose */
+ return (BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]) &&
+ BLI_lasso_is_point_inside(
+ user_data->lasso_coords, user_data->lasso_coords_len, co[0], co[1], INT_MAX));
+ }
+ return false;
}
static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
{
- struct GP_SelectLassoUserData data = {0};
- data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len);
+ struct GP_SelectUserData data = {0};
+ data.lasso_coords = WM_gesture_lasso_path_to_array(C, op, &data.lasso_coords_len);
/* Sanity check. */
- if (data.mcoords == NULL) {
+ if (data.lasso_coords == NULL) {
return OPERATOR_PASS_THROUGH;
}
/* Compute boundbox of lasso (for faster testing later). */
- BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len);
+ BLI_lasso_boundbox(&data.rect, data.lasso_coords, data.lasso_coords_len);
rcti rect = data.rect;
int ret = gpencil_generic_select_exec(C, op, gpencil_test_lasso, rect, &data);
- MEM_freeN((void *)data.mcoords);
+ MEM_freeN((void *)data.lasso_coords);
return ret;
}
@@ -1346,25 +2096,56 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot)
/** \name Mouse Pick Select Operator
* \{ */
-/* helper to deselect all selected strokes/points */
-static void deselect_all_selected(bContext *C)
+static void gpencil_select_curve_point(bContext *C,
+ const int mval[2],
+ const int radius_squared,
+ bGPDlayer **r_gpl,
+ bGPDstroke **r_gps,
+ bGPDcurve **r_gpc,
+ bGPDcurve_point **r_pt,
+ char *handle)
{
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- /* deselect stroke and its points if selected */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
- /* deselect points */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ int hit_distance = radius_squared;
+
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+
+ if (bezt->hide == 1) {
+ continue;
}
- /* deselect stroke itself too */
- gps->flag &= ~GP_STROKE_SELECT;
+ const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
+ (!only_selected || BEZT_ISSEL_ANY(bezt));
+
+ /* if the handles are not visible only check ctrl point (vec[1])*/
+ int from = (!handles_visible) ? 1 : 0;
+ int to = (!handles_visible) ? 2 : 3;
+
+ for (int j = from; j < to; j++) {
+ int screen_co[2];
+ if (gpencil_3d_point_to_screen_space(region, gps_iter.diff_mat, bezt->vec[j], screen_co)) {
+ const int pt_distance = len_manhattan_v2v2_int(mval, screen_co);
+
+ if (pt_distance <= radius_squared && pt_distance < hit_distance) {
+ *r_gpl = gpl;
+ *r_gps = gps;
+ *r_gpc = gpc;
+ *r_pt = gpc_pt;
+ *handle = j;
+ hit_distance = pt_distance;
+ }
+ }
+ }
}
}
- CTX_DATA_END;
+ GP_EDITABLE_CURVES_END(gps_iter);
}
static int gpencil_select_exec(bContext *C, wmOperator *op)
@@ -1374,6 +2155,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const float scale = ts->gp_sculpt.isect_threshold;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
const float radius = 0.4f * U.widget_unit;
@@ -1387,12 +2169,17 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all") && !use_shift_extend;
int mval[2] = {0};
+ /* get mouse location */
+ RNA_int_get_array(op->ptr, "location", mval);
GP_SpaceConversion gsc = {NULL};
bGPDlayer *hit_layer = NULL;
bGPDstroke *hit_stroke = NULL;
bGPDspoint *hit_point = NULL;
+ bGPDcurve *hit_curve = NULL;
+ bGPDcurve_point *hit_curve_point = NULL;
+ char hit_curve_handle = 0;
int hit_distance = radius_squared;
/* sanity checks */
@@ -1414,47 +2201,57 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE);
}
- /* init space conversion stuff */
- gpencil_point_conversion_init(C, &gsc);
+ if (is_curve_edit) {
+ gpencil_select_curve_point(C,
+ mval,
+ radius_squared,
+ &hit_layer,
+ &hit_stroke,
+ &hit_curve,
+ &hit_curve_point,
+ &hit_curve_handle);
+ }
- /* get mouse location */
- RNA_int_get_array(op->ptr, "location", mval);
+ if (hit_curve == NULL) {
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
- /* First Pass: Find stroke point which gets hit */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- bGPDspoint *pt;
- int i;
+ /* First Pass: Find stroke point which gets hit */
+ GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDspoint *pt;
+ int i;
- /* firstly, check for hit-point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- int xy[2];
-
- bGPDspoint pt2;
- gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
- gpencil_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]);
-
- /* do boundbox check first */
- if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
- const int pt_distance = len_manhattan_v2v2_int(mval, xy);
-
- /* check if point is inside */
- if (pt_distance <= radius_squared) {
- /* only use this point if it is a better match than the current hit - T44685 */
- if (pt_distance < hit_distance) {
- hit_layer = gpl;
- hit_stroke = gps_active;
- hit_point = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
- hit_distance = pt_distance;
+ /* firstly, check for hit-point */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ int xy[2];
+
+ bGPDspoint pt2;
+ gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
+ gpencil_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]);
+
+ /* do boundbox check first */
+ if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
+ const int pt_distance = len_manhattan_v2v2_int(mval, xy);
+
+ /* check if point is inside */
+ if (pt_distance <= radius_squared) {
+ /* only use this point if it is a better match than the current hit - T44685 */
+ if (pt_distance < hit_distance) {
+ hit_layer = gpl;
+ hit_stroke = gps_active;
+ hit_point = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ hit_distance = pt_distance;
+ }
}
}
}
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
/* Abort if nothing hit... */
- if (ELEM(NULL, hit_stroke, hit_point)) {
+ if (!hit_curve && !hit_curve_point && !hit_point && !hit_stroke) {
if (deselect_all) {
/* since left mouse select change, deselect all if click outside any hit */
@@ -1472,9 +2269,26 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ /* select all handles if the click was on the curve but not on a handle */
+ if (is_curve_edit && hit_point != NULL) {
+ whole = true;
+ hit_curve = hit_stroke->editcurve;
+ }
+
/* adjust selection behavior - for toggle option */
if (toggle) {
- deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0;
+ if (hit_curve_point != NULL) {
+ BezTriple *bezt = &hit_curve_point->bezt;
+ if (bezt->f1 & SELECT && hit_curve_handle == 0)
+ deselect = true;
+ if (bezt->f2 & SELECT && hit_curve_handle == 1)
+ deselect = true;
+ if (bezt->f3 & SELECT && hit_curve_handle == 2)
+ deselect = true;
+ }
+ else {
+ deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0;
+ }
}
/* If not extending selection, deselect everything else */
@@ -1484,64 +2298,94 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* Perform selection operations... */
if (whole) {
- bGPDspoint *pt;
- int i;
+ /* Generate editcurve if it does not exist */
+ if (is_curve_edit && hit_curve == NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, hit_layer, hit_stroke);
+ hit_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, hit_stroke);
+ hit_curve = hit_stroke->editcurve;
+ }
+ /* select all curve points */
+ if (hit_curve != NULL) {
+ select_all_curve_points(hit_stroke, hit_curve, deselect);
+ }
+ else {
+ bGPDspoint *pt;
+ int i;
- /* entire stroke's points */
- for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
+ /* entire stroke's points */
+ for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
+ if (deselect == false) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+
+ /* stroke too... */
if (deselect == false) {
- pt->flag |= GP_SPOINT_SELECT;
+ hit_stroke->flag |= GP_STROKE_SELECT;
}
else {
- pt->flag &= ~GP_SPOINT_SELECT;
+ hit_stroke->flag &= ~GP_STROKE_SELECT;
}
}
-
- /* stroke too... */
- if (deselect == false) {
- hit_stroke->flag |= GP_STROKE_SELECT;
- }
- else {
- hit_stroke->flag &= ~GP_STROKE_SELECT;
- }
}
else {
/* just the point (and the stroke) */
if (deselect == false) {
- /* we're adding selection, so selection must be true */
- hit_point->flag |= GP_SPOINT_SELECT;
- hit_stroke->flag |= GP_STROKE_SELECT;
-
- /* expand selection to segment */
- int selectmode;
- if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
- selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
- }
- else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
- selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ if (hit_curve_point != NULL) {
+ hit_curve_point->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
+ hit_curve->flag |= GP_CURVE_SELECT;
+ hit_stroke->flag |= GP_STROKE_SELECT;
}
else {
- selectmode = ts->gpencil_selectmode_edit;
- }
+ /* we're adding selection, so selection must be true */
+ hit_point->flag |= GP_SPOINT_SELECT;
+ hit_stroke->flag |= GP_STROKE_SELECT;
+
+ /* expand selection to segment */
+ int selectmode;
+ if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
+ selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
+ }
+ else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
+ selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ }
+ else {
+ selectmode = ts->gpencil_selectmode_edit;
+ }
- if (selectmode == GP_SELECTMODE_SEGMENT) {
- float r_hita[3], r_hitb[3];
- bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT);
- ED_gpencil_select_stroke_segment(
- hit_layer, hit_stroke, hit_point, hit_select, false, scale, r_hita, r_hitb);
+ if (selectmode == GP_SELECTMODE_SEGMENT) {
+ float r_hita[3], r_hitb[3];
+ bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT);
+ ED_gpencil_select_stroke_segment(
+ gpd, hit_layer, hit_stroke, hit_point, hit_select, false, scale, r_hita, r_hitb);
+ }
}
}
else {
- /* deselect point */
- hit_point->flag &= ~GP_SPOINT_SELECT;
+ if (hit_curve_point != NULL) {
+ BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
+ if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
+ hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
+ }
+ BKE_gpencil_curve_sync_selection(hit_stroke);
+ }
+ else {
+ /* deselect point */
+ hit_point->flag &= ~GP_SPOINT_SELECT;
- /* ensure that stroke is selected correctly */
- BKE_gpencil_stroke_sync_selection(hit_stroke);
+ /* ensure that stroke is selected correctly */
+ BKE_gpencil_stroke_sync_selection(hit_stroke);
+ }
}
}
/* updates */
- if (hit_point != NULL) {
+ if (hit_curve_point != NULL || hit_point != NULL) {
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
/* copy on write tag is needed, or else no refresh happens */
@@ -1679,13 +2523,13 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
const float threshold = RNA_int_get(op->ptr, "threshold");
const int selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
- bGPdata *gpd = (bGPdata *)ob->data;
const float range = pow(10, 5 - threshold);
- bool done = false;
+ bool changed = false;
/* Create a hash table with all selected colors. */
GHash *hue_table = BLI_ghash_int_new(__func__);
@@ -1722,7 +2566,6 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
if (gps_selected) {
gps->flag |= GP_STROKE_SELECT;
- done = true;
/* Extend stroke selection. */
if (selectmode == GP_SELECTMODE_STROKE) {
@@ -1736,7 +2579,7 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- if (done) {
+ if (changed) {
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index 544cb4fef1e..ada777d43f3 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -352,13 +352,14 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
* long stroke. Here the length is checked and removed if the length is too big. */
float length = BKE_gpencil_stroke_length(gps, true);
if (length <= MAX_LENGTH) {
+ bGPdata *gpd = ob->data;
if (sample > 0.0f) {
/* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because
* the sample function already call that. */
- BKE_gpencil_stroke_sample(gps, sample, false);
+ BKE_gpencil_stroke_sample(gpd, gps, sample, false);
}
else {
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
else {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 0fdd70c55bc..a1c2ff12866 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -55,6 +55,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -1144,7 +1145,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
bGPDstroke *gps_active = gps;
/* if duplicate, deselect all points. */
if (keep_original) {
- gps_active = BKE_gpencil_stroke_duplicate(gps, true);
+ gps_active = BKE_gpencil_stroke_duplicate(gps, true, true);
gps_active->flag &= ~GP_STROKE_SELECT;
for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) {
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1320,10 +1321,11 @@ void ED_gpencil_project_point_to_plane(const Scene *scene,
/**
* Subdivide a stroke once, by adding a point half way between each pair of existing points
+ * \param gpd: Datablock
* \param gps: Stroke data
* \param subdivide: Number of times to subdivide
*/
-void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide)
+void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, const int subdivide)
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@@ -1413,7 +1415,7 @@ void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide)
MEM_SAFE_FREE(temp_points);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* Reset parent matrix for all layers. */
@@ -2243,8 +2245,12 @@ static void gpencil_copy_points(
}
}
-static void gpencil_insert_point(
- bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], const float co_b[3])
+static void gpencil_insert_point(bGPdata *gpd,
+ bGPDstroke *gps,
+ bGPDspoint *a_pt,
+ bGPDspoint *b_pt,
+ const float co_a[3],
+ float co_b[3])
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@@ -2303,8 +2309,8 @@ static void gpencil_insert_point(
i2++;
}
- /* Calculate geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(temp_points);
}
@@ -2328,7 +2334,8 @@ static float gpencil_calc_factor(const float p2d_a1[2],
}
/* extend selection to stroke intersections */
-int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
+int ED_gpencil_select_stroke_segment(bGPdata *gpd,
+ bGPDlayer *gpl,
bGPDstroke *gps,
bGPDspoint *pt,
bool select,
@@ -2483,7 +2490,7 @@ int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
/* insert new point in the collision points */
if (insert) {
- gpencil_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb);
+ gpencil_insert_point(gpd, gps, hit_pointa, hit_pointb, r_hita, r_hitb);
}
/* free memory */
@@ -2611,6 +2618,82 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
}
}
+void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
+{
+ /* if toggle, check if we need to select or deselect */
+ if (action == SEL_TOGGLE) {
+ action = SEL_SELECT;
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (gpc->flag & GP_CURVE_SELECT) {
+ action = SEL_DESELECT;
+ }
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+
+ if (action == SEL_DESELECT) {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ gpc->flag &= ~GP_CURVE_SELECT;
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+ else {
+ GP_EDITABLE_STROKES_BEGIN(gps_iter, C, gpl, gps){
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ bool selected = false;
+
+ /* Make sure stroke has an editcurve */
+ if (gps->editcurve == NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ switch (action) {
+ case SEL_SELECT:
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ break;
+ case SEL_INVERT:
+ gpc_pt->flag ^= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_INVERT(bezt);
+ break;
+ default:
+ break;
+ }
+
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ selected = true;
+ }
+ }
+
+ if (selected) {
+ gpc->flag |= GP_CURVE_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gps_iter);
+ }
+}
+
/**
* Ensure the #tGPspoint buffer (while drawing stroke)
* size is enough to save all points of the stroke.
diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c
index 3bd2c3e6be6..1d1bc72aaec 100644
--- a/source/blender/editors/gpencil/gpencil_uv.c
+++ b/source/blender/editors/gpencil/gpencil_uv.c
@@ -273,7 +273,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
changed = true;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
i++;
}
}
@@ -291,7 +291,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
gps->uv_rotation = opdata->array_rot[i] - uv_rotation;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
i++;
}
}
@@ -316,7 +316,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
gps->uv_scale = opdata->array_scale[i] + scale;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
i++;
}
}
@@ -512,7 +512,7 @@ static int gpencil_reset_transform_fill_exec(bContext *C, wmOperator *op)
gps->uv_scale = 1.0f;
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
changed = true;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index c6ee30ad6e3..a4dc677f0dc 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -819,8 +819,9 @@ static void gpencil_save_selected_point(tGP_BrushVertexpaintData *gso,
gso->pbuffer_used++;
}
-/* Select points in this stroke and add to an array to be used later. */
-static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
+/* Select points in this stroke and add to an array to be used later.
+ * Returns true if any point was hit and got saved */
+static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
bGPDstroke *gps,
const char tool,
const float diff_mat[4][4])
@@ -841,9 +842,11 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
int index;
bool include_last = false;
+ bool saved = false;
+
/* Check if the stroke collide with brush. */
if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) {
- return;
+ return false;
}
if (gps->totpoints == 1) {
@@ -862,6 +865,7 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* apply operation to this point */
if (pt_active != NULL) {
gpencil_save_selected_point(gso, gps_active, 0, pc1);
+ saved = true;
}
}
}
@@ -913,6 +917,7 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
}
hit = true;
gpencil_save_selected_point(gso, gps_active, index, pc1);
+ saved = true;
}
/* Only do the second point if this is the last segment,
@@ -931,6 +936,7 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
hit = true;
gpencil_save_selected_point(gso, gps_active, index, pc2);
include_last = false;
+ saved = true;
}
}
else {
@@ -949,8 +955,8 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
if (pt_active != NULL) {
hit = true;
gpencil_save_selected_point(gso, gps_active, index, pc1);
-
include_last = false;
+ saved = true;
}
}
}
@@ -970,10 +976,13 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
for (int repeat = 0; repeat < 50; repeat++) {
gpencil_save_selected_point(gso, gps_active, -1, NULL);
}
+ saved = true;
}
}
}
}
+
+ return saved;
}
/* Apply vertex paint brushes to strokes in the given frame. */
@@ -1008,7 +1017,13 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
}
/* Check points below the brush. */
- gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat);
+ bool hit = gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat);
+
+ /* If stroke was hit and has an editcurve the curve needs an update. */
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ if (gps_active->editcurve != NULL && hit) {
+ gps_active->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
+ }
}
/* For Average tool, need calculate the average resulting color from all colors
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 48d323c5d57..17aa407bd76 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -314,7 +314,8 @@ void ED_gpencil_update_color_uv(struct Main *bmain, struct Material *mat);
* 2 - Hit in point B
* 3 - Hit in point A and B
*/
-int ED_gpencil_select_stroke_segment(struct bGPDlayer *gpl,
+int ED_gpencil_select_stroke_segment(struct bGPdata *gpd,
+ struct bGPDlayer *gpl,
struct bGPDstroke *gps,
struct bGPDspoint *pt,
bool select,
@@ -324,6 +325,7 @@ int ED_gpencil_select_stroke_segment(struct bGPDlayer *gpl,
float r_hitb[3]);
void ED_gpencil_select_toggle_all(struct bContext *C, int action);
+void ED_gpencil_select_curve_toggle_all(struct bContext *C, int action);
/* Ensure stroke sbuffer size is enough */
struct tGPspoint *ED_gpencil_sbuffer_ensure(struct tGPspoint *buffer_array,
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index ce049cfcaac..690d6c8055a 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1738,6 +1738,10 @@ static void ed_default_handlers(
wmKeyMap *keymap_general = WM_keymap_ensure(wm->defaultconf, "Grease Pencil", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_general);
+ wmKeyMap *keymap_curve_edit = WM_keymap_ensure(
+ wm->defaultconf, "Grease Pencil Stroke Curve Edit Mode", 0, 0);
+ WM_event_add_keymap_handler(handlers, keymap_curve_edit);
+
wmKeyMap *keymap_edit = WM_keymap_ensure(
wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_edit);
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index 84885dd8c49..0a742ec4470 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -31,7 +31,9 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
+#include "BKE_curve.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "ED_gpencil.h"
@@ -63,33 +65,351 @@ static void createTransGPencil_center_get(bGPDstroke *gps, float r_center[3])
}
}
-void createTransGPencil(bContext *C, TransInfo *t)
+static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visible)
{
- if (t->data_container_len == 0) {
+#define SEL_F1 (1 << 0)
+#define SEL_F2 (1 << 1)
+#define SEL_F3 (1 << 2)
+#define SEL_ALL ((1 << 0) | (1 << 1) | (1 << 2))
+
+ short flag = 0;
+
+ if (handles_visible) {
+ flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) |
+ ((bezt->f3 & SELECT) ? SEL_F3 : 0);
+ }
+ else {
+ if (bezt->f2 & SELECT) {
+ flag = SEL_ALL;
+ }
+ }
+
+ /* Special case for auto & aligned handles */
+ if (flag != SEL_ALL && flag & SEL_F2) {
+ if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
+ flag = SEL_ALL;
+ }
+ }
+
+#undef SEL_F1
+#undef SEL_F2
+#undef SEL_F3
+ return flag;
+}
+
+static void createTransGPencil_curves(bContext *C,
+ TransInfo *t,
+ Depsgraph *depsgraph,
+ ToolSettings *ts,
+ Object *obact,
+ bGPdata *gpd,
+ const int cfra_scene,
+ const bool is_multiedit,
+ const bool use_multiframe_falloff,
+ const bool is_prop_edit,
+ const bool is_prop_edit_connected,
+ const bool is_scale_thickness)
+{
+#define SEL_F1 (1 << 0)
+#define SEL_F2 (1 << 1)
+#define SEL_F3 (1 << 2)
+
+ View3D *v3d = t->view;
+ const bool handle_only_selected_visible = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
+ const bool handle_all_visible = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
+
+ TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
+ tc->data_len = 0;
+
+ /* Number of selected curve points */
+ uint32_t tot_curve_points = 0, tot_sel_curve_points = 0, tot_points = 0, tot_sel_points = 0;
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* Only editable and visible layers are considered. */
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
+ continue;
+ }
+ /* Check if stroke has an editcurve */
+ if (gps->editcurve == NULL) {
+ continue;
+ }
+
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (bezt->hide) {
+ continue;
+ }
+
+ const bool handles_visible = (handle_all_visible ||
+ (handle_only_selected_visible &&
+ (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
+
+ const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible);
+ if (sel_flag & (SEL_F1 | SEL_F2 | SEL_F3)) {
+ if (sel_flag & SEL_F1) {
+ tot_sel_points++;
+ }
+ if (sel_flag & SEL_F2) {
+ tot_sel_points++;
+ }
+ if (sel_flag & SEL_F3) {
+ tot_sel_points++;
+ }
+ tot_sel_curve_points++;
+ }
+
+ if (is_prop_edit) {
+ tot_points += 3;
+ tot_curve_points++;
+ }
+ }
+ }
+ }
+
+ /* If not multiedit out of loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (((is_prop_edit && !is_prop_edit_connected) ? tot_curve_points : tot_sel_points) == 0) {
+ tc->data_len = 0;
return;
}
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
+ int data_len_pt = 0;
- bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
+ if (is_prop_edit) {
+ tc->data_len = tot_points;
+ data_len_pt = tot_curve_points;
+ }
+ else {
+ tc->data_len = tot_sel_points;
+ data_len_pt = tot_sel_curve_points;
+ }
- Object *obact = CTX_data_active_object(C);
- TransData *td = NULL;
- float mtx[3][3], smtx[3][3];
+ if (tc->data_len == 0) {
+ return;
+ }
- const Scene *scene = CTX_data_scene(C);
- const int cfra_scene = CFRA;
+ transform_around_single_fallback_ex(t, data_len_pt);
- const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
- const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
- const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
- (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
+ tc->data = MEM_callocN(tc->data_len * sizeof(TransData), __func__);
+ TransData *td = tc->data;
- TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
+ const bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+ transform_mode_use_local_origins(t));
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* Only editable and visible layers are considered. */
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
+ bGPDframe *gpf = gpl->actframe;
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ float diff_mat[4][4], mtx[3][3];
+ float smtx[3][3];
+
+ /* Init multiframe falloff options. */
+ int f_init = 0;
+ int f_end = 0;
+
+ if (use_multiframe_falloff) {
+ BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
+ }
+
+ if ((gpf->framenum != cfra) && (!is_multiedit)) {
+ gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
+ /* in some weird situations (framelock enabled) return NULL */
+ if (gpf == NULL) {
+ continue;
+ }
+ if (!is_multiedit) {
+ init_gpf = gpf;
+ }
+ }
+
+ /* Calculate difference matrix. */
+ BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
+ copy_m3_m4(mtx, diff_mat);
+ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+
+ for (gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ /* If multi-frame and falloff, recalculate and save value. */
+ float falloff = 1.0f; /* by default no falloff */
+ if ((is_multiedit) && (use_multiframe_falloff)) {
+ /* Falloff depends on distance to active frame
+ * (relative to the overall frame range). */
+ falloff = BKE_gpencil_multiframe_falloff_calc(
+ gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
+ continue;
+ }
+ /* Check if stroke has an editcurve */
+ if (gps->editcurve == NULL) {
+ continue;
+ }
+ TransData *head, *tail;
+ head = tail = td;
+
+ gps->runtime.multi_frame_falloff = falloff;
+ bool need_handle_recalc = false;
+
+ bGPDcurve *gpc = gps->editcurve;
+ const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (bezt->hide) {
+ continue;
+ }
+
+ TransDataCurveHandleFlags *hdata = NULL;
+ bool bezt_use = false;
+ const bool handles_visible = (handle_all_visible ||
+ (handle_only_selected_visible &&
+ (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
+ const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible);
+ /* Iterate over bezier triple */
+ for (int j = 0; j < 3; j++) {
+ bool is_ctrl_point = (j == 1);
+ bool sel = sel_flag & (1 << j);
+
+ if (is_prop_edit || sel) {
+ copy_v3_v3(td->iloc, bezt->vec[j]);
+ td->loc = bezt->vec[j];
+ bool rotate_around_ctrl = !handles_visible ||
+ (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
+ (bezt->f2 & SELECT);
+ copy_v3_v3(td->center, bezt->vec[rotate_around_ctrl ? 1 : j]);
+
+ if (!handles_visible || is_ctrl_point) {
+ if (bezt->f2 & SELECT) {
+ td->flag = TD_SELECTED;
+ }
+ else {
+ td->flag = 0;
+ }
+ }
+ else if (handles_visible) {
+ if (BEZT_ISSEL_IDX(bezt, j)) {
+ td->flag = TD_SELECTED;
+ }
+ else {
+ td->flag = 0;
+ }
+ }
+
+ td->ext = NULL;
+ if (is_ctrl_point) {
+ if (t->mode != TFM_MIRROR) {
+ if (t->mode != TFM_GPENCIL_OPACITY) {
+ if (is_scale_thickness) {
+ td->val = &(gpc_pt->pressure);
+ td->ival = gpc_pt->pressure;
+ }
+ }
+ else {
+ td->val = &(gpc_pt->strength);
+ td->ival = gpc_pt->strength;
+ }
+ }
+ }
+ else {
+ td->val = NULL;
+ }
+
+ if (hdata == NULL) {
+ if (is_ctrl_point && ((sel_flag & SEL_F1 & SEL_F3) == 0)) {
+ hdata = initTransDataCurveHandles(td, bezt);
+ }
+ else if (!is_ctrl_point) {
+ hdata = initTransDataCurveHandles(td, bezt);
+ }
+ }
+
+ td->extra = gps;
+ td->ob = obact;
+
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+ copy_m3_m3(td->axismtx, mtx);
+
+ td++;
+ tail++;
+ }
+
+ bezt_use |= sel;
+ }
+
+ /* Update the handle types so transformation is possible */
+ if (bezt_use && !ELEM(t->mode, TFM_GPENCIL_OPACITY, TFM_GPENCIL_SHRINKFATTEN)) {
+ BKE_nurb_bezt_handle_test(
+ bezt, SELECT, handles_visible, use_around_origins_for_handles_test);
+ need_handle_recalc = true;
+ }
+ }
+ if (is_prop_edit && (head != tail)) {
+ calc_distanceCurveVerts(head, tail - 1, is_cyclic);
+ }
+
+ if (need_handle_recalc) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ }
+ }
+ }
+
+ /* If not multiedit out of loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+#undef SEL_F1
+#undef SEL_F2
+#undef SEL_F3
+}
+
+static void createTransGPencil_strokes(bContext *C,
+ TransInfo *t,
+ Depsgraph *depsgraph,
+ ToolSettings *ts,
+ Object *obact,
+ bGPdata *gpd,
+ const int cfra_scene,
+ const bool is_multiedit,
+ const bool use_multiframe_falloff,
+ const bool is_prop_edit,
+ const bool is_prop_edit_connected,
+ const bool is_scale_thickness)
+{
+ TransData *td = NULL;
+ float mtx[3][3], smtx[3][3];
+
+ TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* == Grease Pencil Strokes to Transform Data ==
* Grease Pencil stroke points can be a mixture of 2D (screen-space),
* or 3D coordinates. However, they're always saved as 3D points.
@@ -98,15 +418,6 @@ void createTransGPencil(bContext *C, TransInfo *t)
*/
tc->data_len = 0;
- if (gpd == NULL) {
- return;
- }
-
- /* initialize falloff curve */
- if (is_multiedit) {
- BKE_curvemapping_init(ts->gp_sculpt.cur_falloff);
- }
-
/* First Pass: Count the number of data-points required for the strokes,
* (and additional info about the configuration - e.g. 2D/3D?).
*/
@@ -368,6 +679,71 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
}
+void createTransGPencil(bContext *C, TransInfo *t)
+{
+ if (t->data_container_len == 0) {
+ return;
+ }
+
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ const Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ Object *obact = CTX_data_active_object(C);
+ bGPdata *gpd = obact->data;
+ BLI_assert(gpd != NULL);
+
+ const int cfra_scene = CFRA;
+
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) !=
+ 0;
+
+ const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+ const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
+ const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
+ (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
+
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ /* initialize falloff curve */
+ if (is_multiedit) {
+ BKE_curvemapping_init(ts->gp_sculpt.cur_falloff);
+ }
+
+ if (gpd == NULL) {
+ return;
+ }
+
+ if (is_curve_edit) {
+ createTransGPencil_curves(C,
+ t,
+ depsgraph,
+ ts,
+ obact,
+ gpd,
+ cfra_scene,
+ is_multiedit,
+ use_multiframe_falloff,
+ is_prop_edit,
+ is_prop_edit_connected,
+ is_scale_thickness);
+ }
+ else {
+ createTransGPencil_strokes(C,
+ t,
+ depsgraph,
+ ts,
+ obact,
+ gpd,
+ cfra_scene,
+ is_multiedit,
+ use_multiframe_falloff,
+ is_prop_edit,
+ is_prop_edit_connected,
+ is_scale_thickness);
+ }
+}
+
/* force recalculation of triangles during transformation */
void recalcData_gpencil_strokes(TransInfo *t)
{
@@ -375,13 +751,19 @@ void recalcData_gpencil_strokes(TransInfo *t)
GHash *strokes = BLI_ghash_ptr_new(__func__);
TransData *td = tc->data;
+ bGPdata *gpd = td->ob->data;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
for (int i = 0; i < tc->data_len; i++, td++) {
bGPDstroke *gps = td->extra;
if ((gps != NULL) && (!BLI_ghash_haskey(strokes, gps))) {
BLI_ghash_insert(strokes, gps, gps);
+ if (is_curve_edit && gps->editcurve != NULL) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ }
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
BLI_ghash_free(strokes, NULL, NULL);
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 60848eb5678..8a7ec7a99e9 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -799,7 +799,7 @@ void postTrans(bContext *C, TransInfo *t)
if (t->data_len_all != 0) {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
/* free data malloced per trans-data */
- if (ELEM(t->obedit_type, OB_CURVE, OB_SURF) || (t->spacetype == SPACE_GRAPH)) {
+ if (ELEM(t->obedit_type, OB_CURVE, OB_SURF, OB_GPENCIL) || (t->spacetype == SPACE_GRAPH)) {
TransData *td = tc->data;
for (int a = 0; a < tc->data_len; a++, td++) {
if (td->flag & TD_BEZTRIPLE) {
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index a71915d8122..d5c6ea2dfdd 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -653,6 +653,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
Object *ob = OBACT(view_layer);
bGPdata *gpd = CTX_data_gpencil_data(C);
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
+ const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int a, totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
@@ -711,16 +712,39 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
continue;
}
- /* we're only interested in selected points here... */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
+ if (is_curve_edit) {
+ if (gps->editcurve == NULL) {
+ continue;
+ }
- /* Change selection status of all points, then make the stroke match */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
- totsel++;
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ for (uint32_t j = 0; j < 3; j++) {
+ if (BEZT_ISSEL_IDX(bezt, j)) {
+ calc_tw_center_with_matrix(tbounds, bezt->vec[j], use_mat_local, diff_mat);
+ totsel++;
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* we're only interested in selected points here... */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+
+ /* Change selection status of all points, then make the stroke match */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
+ totsel++;
+ }
}
}
}
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index 103eb17c273..e67c6c03481 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -29,6 +29,8 @@
#include "BKE_context.h"
#include "BKE_unit.h"
+#include "DNA_gpencil_types.h"
+
#include "ED_screen.h"
#include "UI_interface.h"
@@ -68,8 +70,16 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
BLI_snprintf(str, sizeof(str), TIP_("Opacity: %3f"), ratio);
}
+ bool recalc = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
+ bGPdata *gpd = td->ob->data;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ /* Only recalculate data when in curve edit mode. */
+ if (is_curve_edit) {
+ recalc = true;
+ }
+
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
@@ -84,6 +94,10 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
}
}
+ if (recalc) {
+ recalcData(t);
+ }
+
ED_area_status_text(t->area, str);
}
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 14e7c1df4f4..95e3d89d2b7 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -29,6 +29,8 @@
#include "BKE_context.h"
#include "BKE_unit.h"
+#include "DNA_gpencil_types.h"
+
#include "ED_screen.h"
#include "UI_interface.h"
@@ -68,8 +70,16 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
BLI_snprintf(str, sizeof(str), TIP_("Shrink/Fatten: %3f"), ratio);
}
+ bool recalc = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
+ bGPdata *gpd = td->ob->data;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ /* Only recalculate data when in curve edit mode. */
+ if (is_curve_edit) {
+ recalc = true;
+ }
+
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
@@ -86,6 +96,10 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
}
}
+ if (recalc) {
+ recalcData(t);
+ }
+
ED_area_status_text(t->area, str);
}