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:
Diffstat (limited to 'source/blender/editors/gpencil')
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-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_bake_animation.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_curve_draw.c845
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c44
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c2140
-rw-r--r--source/blender/editors/gpencil/gpencil_edit_curve.c241
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h15
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c43
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c53
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c28
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c1487
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c315
-rw-r--r--source/blender/editors/gpencil/gpencil_uv.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c33
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c130
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c90
24 files changed, 3620 insertions, 1934 deletions
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index bff7310e9f7..83ba519c4c7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -43,6 +43,7 @@ set(SRC
gpencil_armature.c
gpencil_bake_animation.c
gpencil_convert.c
+ gpencil_curve_draw.c
gpencil_data.c
gpencil_edit.c
gpencil_edit_curve.c
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index d8734c4ae6b..95d7648ace2 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -848,115 +848,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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* 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 e95496b51ee..291df3b678e 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -236,7 +236,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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c
index 1a5e2950e09..af9bd55fb44 100644
--- a/source/blender/editors/gpencil/gpencil_bake_animation.c
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.c
@@ -346,7 +346,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false);
}
else {
- BKE_gpencil_stroke_geometry_update(gpd_dst, gps);
+ BKE_gpencil_stroke_geometry_update(gpd_dst, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 2cb1e09d9a6..2d2d3cf95ab 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1378,6 +1378,7 @@ static void gpencil_layer_to_curve(bContext *C,
nu = NULL;
}
+ /* TODO: check if the strok is of type BEZIER. In that case the conversion can be less expensive. */
switch (mode) {
case GP_STROKECONVERT_PATH:
gpencil_stroke_to_path(C,
diff --git a/source/blender/editors/gpencil/gpencil_curve_draw.c b/source/blender/editors/gpencil/gpencil_curve_draw.c
new file mode 100644
index 00000000000..83df6bb63ad
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_curve_draw.c
@@ -0,0 +1,845 @@
+/*
+ * 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) 2017, Blender Foundation
+ * This is a new part of Blender
+ * Operators for creating new Grease Pencil primitives (boxes, circles, ...)
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+#include <stdio.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_paint.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_types.h"
+
+#include "ED_gpencil.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+
+#include "GPU_immediate.h"
+#include "GPU_matrix.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "gpencil_intern.h"
+
+/* ------------------------------------------------------------------------- */
+/* Structs & enums */
+
+typedef enum eGPDcurve_draw_state {
+ IN_MOVE = 0,
+ IN_SET_VECTOR = 1,
+ IN_DRAG_ALIGNED_HANDLE = 2,
+ IN_DRAG_FREE_HANDLE = 3,
+ IN_SET_THICKNESS = 4,
+} eGPDcurve_draw_state;
+
+typedef struct tGPDcurve_draw {
+ Scene *scene;
+ ARegion *region;
+ Object *ob;
+ bGPdata *gpd;
+ bGPDlayer *gpl;
+ bGPDframe *gpf;
+ bGPDstroke *gps;
+ bGPDcurve *gpc;
+ int cframe;
+
+ Brush *brush;
+
+ GP_SpaceConversion gsc;
+
+ /* imval of current event */
+ int imval[2];
+ /* imval of previous event */
+ int imval_prev[2];
+ /* imval when mouse was last pressed */
+ int imval_start[2];
+ /* imval when mouse was last released */
+ int imval_end[2];
+ bool is_mouse_down;
+
+ bool is_cyclic;
+ float prev_pressure;
+
+ /* Curve resolution */
+ int resolution;
+
+ /* Callback for viewport drawing. */
+ void *draw_handle;
+
+ eGPDcurve_draw_state state;
+} tGPDcurve_draw;
+
+enum {
+ CD_MODAL_CANCEL = 1,
+ CD_MODAL_CONFIRM,
+ CD_MODAL_FREE_HANDLE_ON,
+ CD_MODAL_FREE_HANDLE_OFF,
+ CD_MODAL_CYCLIC_TOGGLE,
+ CD_MODAL_DELETE_LAST,
+ CD_MODAL_SET_THICKNESS,
+};
+
+/* Forward declaration */
+static void gpencil_curve_draw_init(bContext *C, wmOperator *op, const wmEvent *event);
+static void gpencil_curve_draw_update(bContext *C, tGPDcurve_draw *tcd);
+static void gpencil_curve_draw_confirm(bContext *C, wmOperator *op, tGPDcurve_draw *tcd);
+static void gpencil_curve_draw_exit(bContext *C, wmOperator *op);
+
+/* ------------------------------------------------------------------------- */
+/* Helper functions */
+
+static void debug_print_state(tGPDcurve_draw *tcd)
+{
+ const char *state_str[] = {"MOVE", "VECTOR", "ALIGN", "FREE", "THICK", "ALPHA"};
+ printf("State: %s\tMouse x=%d\ty=%d\tpressed:%s\n",
+ state_str[tcd->state],
+ tcd->imval[0],
+ tcd->imval[1],
+ (tcd->is_mouse_down) ? "TRUE" : "FALSE");
+}
+
+static void gpencil_project_mval_to_v3(
+ Scene *scene, ARegion *region, Object *ob, const int mval_i[2], float r_out[3])
+{
+ ToolSettings *ts = scene->toolsettings;
+ float mval_f[2], mval_prj[2], rvec[3], dvec[3], zfac;
+ copy_v2fl_v2i(mval_f, mval_i);
+
+ ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, rvec);
+ zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL);
+
+ if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
+ V3D_PROJ_RET_OK) {
+ sub_v2_v2v2(mval_f, mval_prj, mval_f);
+ ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ sub_v3_v3v3(r_out, rvec, dvec);
+ }
+ else {
+ zero_v3(r_out);
+ }
+}
+
+/* Helper: Add a new curve point at the end (duplicating the previous last) */
+static void gpencil_push_curve_point(bContext *C, tGPDcurve_draw *tcd)
+{
+ bGPDcurve *gpc = tcd->gpc;
+ int old_num_points = gpc->tot_curve_points;
+ int new_num_points = old_num_points + 1;
+ gpc->tot_curve_points = new_num_points;
+
+ gpc->curve_points = MEM_recallocN(gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
+
+ 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));
+
+ new_last->bezt.h1 = new_last->bezt.h2 = HD_VECT;
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ tcd->gps, tcd->gpd->curve_edit_resolution, false, GP_GEO_UPDATE_DEFAULT);
+}
+
+/* Helper: Remove the last curve point */
+static void gpencil_pop_curve_point(bContext *C, tGPDcurve_draw *tcd)
+{
+ bGPdata *gpd = tcd->gpd;
+ bGPDstroke *gps = tcd->gps;
+ bGPDcurve *gpc = tcd->gpc;
+ const int old_num_points = gpc->tot_curve_points;
+ const int new_num_points = old_num_points - 1;
+ if (G.debug & G_DEBUG) {
+ printf("old: %d, new: %d\n", old_num_points, new_num_points);
+ }
+
+ /* Create new stroke and curve */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(tcd->gps, false, false);
+ new_stroke->points = NULL;
+
+ bGPDcurve *new_curve = BKE_gpencil_stroke_editcurve_new(new_num_points);
+ new_curve->flag = gpc->flag;
+ memcpy(new_curve->curve_points, gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
+ new_stroke->editcurve = new_curve;
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ new_stroke, gpd->curve_edit_resolution, false, GP_GEO_UPDATE_DEFAULT);
+
+ /* Remove and free old stroke and curve */
+ BLI_remlink(&tcd->gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+
+ tcd->gps = new_stroke;
+ tcd->gpc = new_curve;
+
+ BLI_addtail(&tcd->gpf->strokes, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static void gpencil_set_handle_type_last_point(tGPDcurve_draw *tcd, eBezTriple_Handle type)
+{
+ bGPDcurve *gpc = tcd->gpc;
+ bGPDcurve_point *cpt = &gpc->curve_points[gpc->tot_curve_points - 1];
+ cpt->bezt.h1 = cpt->bezt.h2 = type;
+}
+
+static void gpencil_set_alpha_last_segment(tGPDcurve_draw *tcd, float alpha)
+{
+ bGPDstroke *gps = tcd->gps;
+ bGPDcurve *gpc = tcd->gpc;
+
+ if (gpc->tot_curve_points < 2) {
+ return;
+ }
+
+ bGPDcurve_point *old_last = &gpc->curve_points[gpc->tot_curve_points - 2];
+ for (uint32_t i = old_last->point_index; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->strength = alpha;
+ }
+}
+
+static void gpencil_curve_draw_ui_callback(const struct bContext *UNUSED(C),
+ struct ARegion *UNUSED(region),
+ void *customdata)
+{
+ const tGPDcurve_draw *tcd = customdata;
+ GPU_depth_test(GPU_DEPTH_NONE);
+
+ GPU_matrix_push_projection();
+ GPU_polygon_offset(1.0f, 1.0f);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(tcd->ob->obmat);
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+
+ /* Draw overlays. */
+ if (ELEM(tcd->state, IN_DRAG_ALIGNED_HANDLE, IN_DRAG_FREE_HANDLE)) {
+ bGPDcurve *gpc = tcd->gpc;
+ bGPDcurve_point *cpt_last = &gpc->curve_points[gpc->tot_curve_points - 1];
+ BezTriple *bezt = &cpt_last->bezt;
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ float color[4] = {0, 0, 0, 1.0f};
+ UI_GetThemeColorType3fv(TH_GP_VERTEX_SELECT, SPACE_VIEW3D, color);
+
+ /* TODO: Use the GPU_SHADER_3D_POLYLINE_* shader instead. GPU_line_smooth will be deprecated.
+ */
+ GPU_line_smooth(true);
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ // immUniform1f("lineWidth", U.pixelsize * 2.0f);
+ // immUniform1i("lineSmooth", 1);
+
+ immUniformColor4fv(color);
+
+ /* Handle lines. */
+ immBegin(GPU_PRIM_LINES, 4);
+ immVertex3fv(pos, bezt->vec[0]);
+ immVertex3fv(pos, bezt->vec[1]);
+ immVertex3fv(pos, bezt->vec[1]);
+ immVertex3fv(pos, bezt->vec[2]);
+ immEnd();
+
+ // immUniform1f("size", U.pixelsize * UI_GetThemeValuef(TH_GP_VERTEX_SIZE) * 2.0f);
+
+ // immUniformColor4fv(color);
+
+ /* Handle points. */
+ immBegin(GPU_PRIM_POINTS, 3);
+ immVertex3fv(pos, bezt->vec[0]);
+ immVertex3fv(pos, bezt->vec[1]);
+ immVertex3fv(pos, bezt->vec[2]);
+ immEnd();
+
+ GPU_line_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+ }
+
+ immUnbindProgram();
+
+ GPU_matrix_pop();
+ GPU_matrix_pop_projection();
+
+ /* Reset default */
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main drawing functions */
+
+static void gpencil_curve_draw_update_header(bContext *C,
+ wmOperator *op,
+ const tGPDcurve_draw *tcd)
+{
+ char header[UI_MAX_DRAW_STR];
+ char buf[UI_MAX_DRAW_STR];
+
+ char *p = buf;
+ int available_len = sizeof(buf);
+
+#define WM_MODALKEY(_id) \
+ WM_modalkeymap_operator_items_to_string_buf( \
+ op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
+
+ switch (tcd->state) {
+ case IN_MOVE:
+ case IN_SET_VECTOR:
+ BLI_snprintf(header,
+ sizeof(header),
+ TIP_("%s: confirm, %s: cancel, "
+ "%s: toggle cyclic (%s), "
+ "%s: delete last, %s: set thickness"),
+ WM_MODALKEY(CD_MODAL_CONFIRM),
+ WM_MODALKEY(CD_MODAL_CANCEL),
+ WM_MODALKEY(CD_MODAL_CYCLIC_TOGGLE),
+ WM_bool_as_string(tcd->is_cyclic),
+ WM_MODALKEY(CD_MODAL_DELETE_LAST),
+ WM_MODALKEY(CD_MODAL_SET_THICKNESS));
+ break;
+ case IN_DRAG_FREE_HANDLE:
+ case IN_DRAG_ALIGNED_HANDLE:
+ BLI_snprintf(header,
+ sizeof(header),
+ TIP_("%s: confirm, %s: cancel, "
+ "%s: toggle cyclic (%s), "
+ "%s: free handle (%s), "
+ "%s: delete last, %s: set thickness"),
+ WM_MODALKEY(CD_MODAL_CONFIRM),
+ WM_MODALKEY(CD_MODAL_CANCEL),
+ WM_MODALKEY(CD_MODAL_CYCLIC_TOGGLE),
+ WM_bool_as_string(tcd->is_cyclic),
+ WM_MODALKEY(CD_MODAL_FREE_HANDLE_ON),
+ WM_bool_as_string(tcd->state == IN_DRAG_FREE_HANDLE),
+ WM_MODALKEY(CD_MODAL_DELETE_LAST),
+ WM_MODALKEY(CD_MODAL_SET_THICKNESS));
+ break;
+ case IN_SET_THICKNESS:
+ BLI_snprintf(header,
+ sizeof(header),
+ TIP_("%s: confirm, %s: cancel, "
+ "%s: toggle cyclic (%s), "
+ "%s: delete last"),
+ WM_MODALKEY(CD_MODAL_CONFIRM),
+ WM_MODALKEY(CD_MODAL_CANCEL),
+ WM_MODALKEY(CD_MODAL_CYCLIC_TOGGLE),
+ WM_bool_as_string(tcd->is_cyclic),
+ WM_MODALKEY(CD_MODAL_DELETE_LAST));
+ break;
+ }
+
+ ED_workspace_status_text(C, header);
+
+#undef WM_MODALKEY
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main drawing functions */
+
+static void gpencil_curve_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ARegion *region = CTX_wm_region(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+
+ ToolSettings *ts = scene->toolsettings;
+ Paint *paint = &ts->gp_paint->paint;
+ int cfra = CFRA;
+
+ /* Allocate temp curve draw data. */
+ tGPDcurve_draw *tcd = MEM_callocN(sizeof(tGPDcurve_draw), __func__);
+ tcd->scene = scene;
+ tcd->region = region;
+ tcd->gpd = gpd;
+ tcd->ob = ob;
+ /* Fixed resolution. */
+ tcd->resolution = 32;
+
+ /* Initialize mouse state */
+ copy_v2_v2_int(tcd->imval, event->mval);
+ copy_v2_v2_int(tcd->imval_prev, event->mval);
+ tcd->is_mouse_down = (event->val == KM_PRESS);
+ tcd->state = IN_SET_VECTOR;
+
+ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
+ BKE_brush_gpencil_paint_presets(bmain, ts, true);
+ }
+
+ Brush *brush = BKE_paint_toolslots_brush_get(paint, 0);
+ BKE_brush_tool_set(brush, paint, 0);
+ BKE_paint_brush_set(paint, brush);
+ BrushGpencilSettings *brush_settings = brush->gpencil_settings;
+ tcd->brush = brush;
+
+ /* Get active layer or create a new one. */
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ if (gpl == NULL) {
+ gpl = BKE_gpencil_layer_addnew(tcd->gpd, DATA_("Curve"), true, false);
+ }
+ tcd->gpl = gpl;
+
+ /* Recalculate layer transform matrix to avoid problems if props are animated. */
+ loc_eul_size_to_mat4(
+ tcd->gpl->layer_mat, tcd->gpl->location, tcd->gpl->rotation, tcd->gpl->scale);
+ invert_m4_m4(tcd->gpl->layer_invmat, tcd->gpl->layer_mat);
+
+ /* Get current frame or create new one. */
+ short add_frame_mode;
+ if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
+ add_frame_mode = GP_GETFRAME_ADD_COPY;
+ }
+ else {
+ add_frame_mode = GP_GETFRAME_ADD_NEW;
+ }
+
+ tcd->cframe = cfra;
+ bool need_tag = tcd->gpl->actframe == NULL;
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(tcd->gpl, tcd->cframe, add_frame_mode);
+ if (need_tag) {
+ DEG_id_tag_update(&tcd->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ }
+ tcd->gpf = gpf;
+
+ /* Create stroke. */
+ int mat_idx = BKE_gpencil_object_material_get_index_from_brush(ob, brush);
+ bGPDstroke *gps = BKE_gpencil_stroke_new(mat_idx, 1, brush->size);
+ gps->thickness = brush->size;
+ gps->hardeness = brush_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush_settings->aspect_ratio);
+
+ float first_pt[3];
+ gpencil_project_mval_to_v3(scene, region, ob, tcd->imval, first_pt);
+ gps->points[0].pressure = 1.0f;
+ gps->points[0].strength = 1.0f;
+ copy_v3_v3(&gps->points[0].x, first_pt);
+
+ BLI_addtail(&gpf->strokes, gps);
+ tcd->gps = gps;
+
+ /* Create editcurve. */
+ bGPDcurve *gpc = BKE_gpencil_stroke_editcurve_new(1);
+ bGPDcurve_point *cpt = &gpc->curve_points[0];
+ copy_v3_v3(cpt->bezt.vec[0], first_pt);
+ copy_v3_v3(cpt->bezt.vec[1], first_pt);
+ copy_v3_v3(cpt->bezt.vec[2], first_pt);
+ cpt->pressure = 1.0f;
+ cpt->strength = 1.0f;
+
+ gps->editcurve = gpc;
+ tcd->gpc = gpc;
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(tcd->gpd, gps, GP_GEO_UPDATE_DEFAULT);
+
+ /* Initialize space conversion. */
+ gpencil_point_conversion_init(C, &tcd->gsc);
+
+ tcd->draw_handle = ED_region_draw_cb_activate(
+ tcd->region->type, gpencil_curve_draw_ui_callback, tcd, REGION_DRAW_POST_VIEW);
+
+ gpencil_curve_draw_update(C, tcd);
+ op->customdata = tcd;
+}
+
+static void gpencil_curve_draw_update(bContext *C, tGPDcurve_draw *tcd)
+{
+ bGPdata *gpd = tcd->gpd;
+ bGPDstroke *gps = tcd->gps;
+ bGPDcurve *gpc = tcd->gpc;
+ int tot_points = gpc->tot_curve_points;
+ bGPDcurve_point *cpt = &gpc->curve_points[tot_points - 1];
+ BezTriple *bezt = &cpt->bezt;
+
+ float co[3];
+ switch (tcd->state) {
+ case IN_MOVE: {
+ gpencil_project_mval_to_v3(tcd->scene, tcd->region, tcd->ob, tcd->imval, co);
+ copy_v3_v3(bezt->vec[0], co);
+ copy_v3_v3(bezt->vec[1], co);
+ copy_v3_v3(bezt->vec[2], co);
+
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ gpencil_set_alpha_last_segment(tcd, 0.1f);
+ break;
+ }
+ case IN_DRAG_ALIGNED_HANDLE: {
+ float vec[3];
+ gpencil_project_mval_to_v3(tcd->scene, tcd->region, tcd->ob, tcd->imval, co);
+ sub_v3_v3v3(vec, bezt->vec[1], co);
+ add_v3_v3(vec, bezt->vec[1]);
+ copy_v3_v3(bezt->vec[0], vec);
+ copy_v3_v3(bezt->vec[2], co);
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ break;
+ }
+ case IN_DRAG_FREE_HANDLE: {
+ gpencil_project_mval_to_v3(tcd->scene, tcd->region, tcd->ob, tcd->imval, co);
+ copy_v3_v3(bezt->vec[2], co);
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ break;
+ }
+ case IN_SET_THICKNESS: {
+ int move[2];
+ sub_v2_v2v2_int(move, tcd->imval, tcd->imval_start);
+ int dir = move[0] > 0.0f ? 1 : -1;
+ int dist = len_manhattan_v2_int(move);
+ /* TODO: calculate correct radius. */
+ float dr = dir * ((float)dist / 10.0f);
+ cpt->pressure = tcd->prev_pressure + dr;
+ CLAMP_MIN(cpt->pressure, 0.0f);
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ break;
+ }
+ default:
+ break;
+ }
+
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static void gpencil_curve_draw_confirm(bContext *C, wmOperator *op, tGPDcurve_draw *tcd)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Confirm curve draw\n");
+ }
+ bGPDcurve *gpc = tcd->gpc;
+ int tot_points = gpc->tot_curve_points;
+ bGPDcurve_point *cpt = &gpc->curve_points[tot_points - 1];
+ cpt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&cpt->bezt);
+
+ BKE_gpencil_editcurve_recalculate_handles(tcd->gps);
+}
+
+static void gpencil_curve_draw_exit(bContext *C, wmOperator *op)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Exit curve draw\n");
+ }
+
+ wmWindow *win = CTX_wm_window(C);
+ tGPDcurve_draw *tcd = op->customdata;
+
+ ED_workspace_status_text(C, NULL);
+ WM_cursor_modal_restore(win);
+
+ ED_region_draw_cb_exit(tcd->region->type, tcd->draw_handle);
+
+ bGPdata *gpd = tcd->gpd;
+
+ MEM_SAFE_FREE(tcd);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
+
+ op->customdata = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Operator callbacks */
+
+static int gpencil_curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Invoke curve draw\n");
+ }
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Set cursor to dot. */
+ WM_cursor_modal_set(win, WM_CURSOR_DOT);
+
+ gpencil_curve_draw_init(C, op, event);
+ // tGPDcurve_draw *tcd = op->customdata;
+
+ /* Add modal handler. */
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int gpencil_curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGPDcurve_draw *tcd = op->customdata;
+ wmWindow *win = CTX_wm_window(C);
+ float drag_threshold = (float)WM_event_drag_threshold(event);
+
+ copy_v2_v2_int(tcd->imval, event->mval);
+
+ /* Modal keymap event. */
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case CD_MODAL_CONFIRM: {
+ /* Delete the 'preview' point. */
+ if (tcd->state == IN_MOVE) {
+ gpencil_pop_curve_point(C, tcd);
+ }
+ /* Create curve */
+ gpencil_curve_draw_confirm(C, op, tcd);
+ gpencil_curve_draw_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ case CD_MODAL_CANCEL: {
+ /* Delete the stroke. */
+ BLI_remlink(&tcd->gpf->strokes, tcd->gps);
+ BKE_gpencil_free_stroke(tcd->gps);
+ gpencil_curve_draw_exit(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ case CD_MODAL_FREE_HANDLE_ON: {
+ if (tcd->state == IN_DRAG_ALIGNED_HANDLE) {
+ tcd->state = IN_DRAG_FREE_HANDLE;
+ gpencil_set_handle_type_last_point(tcd, HD_FREE);
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ case CD_MODAL_FREE_HANDLE_OFF: {
+ if (tcd->state == IN_DRAG_FREE_HANDLE) {
+ tcd->state = IN_DRAG_ALIGNED_HANDLE;
+ gpencil_set_handle_type_last_point(tcd, HD_ALIGN);
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ case CD_MODAL_CYCLIC_TOGGLE: {
+ if (tcd->is_cyclic) {
+ tcd->gps->flag &= ~GP_STROKE_CYCLIC;
+ }
+ else {
+ tcd->gps->flag |= GP_STROKE_CYCLIC;
+ }
+ tcd->is_cyclic = !tcd->is_cyclic;
+ gpencil_curve_draw_update(C, tcd);
+ break;
+ }
+ case CD_MODAL_DELETE_LAST: {
+ if (tcd->state == IN_MOVE) {
+ gpencil_pop_curve_point(C, tcd);
+ }
+ else if (ELEM(tcd->state, IN_DRAG_ALIGNED_HANDLE, IN_DRAG_FREE_HANDLE)) {
+ tcd->state = IN_MOVE;
+ }
+ gpencil_curve_draw_update(C, tcd);
+ break;
+ }
+ case CD_MODAL_SET_THICKNESS: {
+ if (tcd->state != IN_SET_THICKNESS) {
+ tcd->state = IN_SET_THICKNESS;
+ WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
+
+ bGPDcurve_point *cpt_last = &tcd->gpc->curve_points[tcd->gpc->tot_curve_points - 1];
+ tcd->prev_pressure = cpt_last->pressure;
+ copy_v2_v2_int(tcd->imval_start, tcd->imval);
+
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ }
+ }
+ /* Event not in keymap. */
+ else {
+ switch (event->type) {
+ case LEFTMOUSE: {
+ if (event->val == KM_PRESS) {
+ copy_v2_v2_int(tcd->imval_start, tcd->imval);
+ tcd->is_mouse_down = true;
+ /* Set state to vector. */
+ if (tcd->state == IN_MOVE) {
+ tcd->state = IN_SET_VECTOR;
+ }
+ /* Reset state to move. */
+ else if (tcd->state == IN_SET_THICKNESS) {
+ tcd->state = IN_MOVE;
+ WM_cursor_modal_set(win, WM_CURSOR_DOT);
+ }
+ }
+ else if (event->val == KM_RELEASE) {
+ copy_v2_v2_int(tcd->imval_end, tcd->imval);
+ tcd->is_mouse_down = false;
+ /* Reset state to move. */
+ if (ELEM(tcd->state, IN_SET_VECTOR, IN_DRAG_ALIGNED_HANDLE, IN_DRAG_FREE_HANDLE)) {
+ tcd->state = IN_MOVE;
+ gpencil_push_curve_point(C, tcd);
+ }
+
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ case MOUSEMOVE: {
+ if (tcd->state == IN_SET_VECTOR &&
+ len_v2v2_int(tcd->imval, tcd->imval_start) > drag_threshold) {
+ tcd->state = IN_DRAG_ALIGNED_HANDLE;
+ gpencil_set_handle_type_last_point(tcd, HD_ALIGN);
+ }
+ gpencil_curve_draw_update(C, tcd);
+ break;
+ }
+ default: {
+ copy_v2_v2_int(tcd->imval_prev, tcd->imval);
+ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
+ }
+ }
+ }
+
+ gpencil_curve_draw_update_header(C, op, tcd);
+
+ if (G.debug & G_DEBUG) {
+ debug_print_state(tcd);
+ }
+ copy_v2_v2_int(tcd->imval_prev, tcd->imval);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void gpencil_curve_draw_cancel(bContext *C, wmOperator *op)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Cancel curve draw\n");
+ }
+ gpencil_curve_draw_exit(C, op);
+}
+
+static bool gpencil_curve_draw_poll(bContext *C)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Poll curve draw\n");
+ }
+ ScrArea *area = CTX_wm_area(C);
+ if (area && area->spacetype != SPACE_VIEW3D) {
+ return false;
+ }
+
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ if (gpd == NULL) {
+ return false;
+ }
+
+ if ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) {
+ return false;
+ }
+
+ return true;
+}
+
+wmKeyMap *gpencil_curve_draw_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {CD_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
+ {CD_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+ {CD_MODAL_FREE_HANDLE_ON, "FREE_HANDLE_ON", 0, "Free Handle On", ""},
+ {CD_MODAL_FREE_HANDLE_OFF, "FREE_HANDLE_OFF", 0, "Free Handle Off", ""},
+ {CD_MODAL_CYCLIC_TOGGLE, "CYCLIC_TOGGLE", 0, "Toggle Stroke Cyclic", ""},
+ {CD_MODAL_DELETE_LAST, "DELETE_LAST", 0, "Delete the Last Confirmed Point", ""},
+ {CD_MODAL_SET_THICKNESS, "SET_THICKNESS", 0, "Set the Thickness", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Curve Draw Tool Modal Map");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return NULL;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "Curve Draw Tool Modal Map", modal_items);
+
+ WM_modalkeymap_assign(keymap, "GPENCIL_OT_draw_curve");
+
+ return keymap;
+}
+
+void GPENCIL_OT_draw_curve(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Grease Pencil Draw Curve";
+ ot->idname = "GPENCIL_OT_draw_curve";
+ ot->description = "Draw a bézier stroke in the active grease pencil object";
+
+ /* api callbacks */
+ ot->invoke = gpencil_curve_draw_invoke;
+ ot->modal = gpencil_curve_draw_modal;
+ ot->cancel = gpencil_curve_draw_cancel;
+ ot->poll = gpencil_curve_draw_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+}
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index e272f46d13d..685cd54fc1b 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -1552,7 +1552,6 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
ListBase selected = {NULL};
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- bGPDstroke *gps = NULL;
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
@@ -1560,13 +1559,12 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
continue;
}
/* verify if any selected stroke is in the extreme of the stack and select to move */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
/* only if selected */
- if (gps->flag & GP_STROKE_SELECT) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ if (is_stroke_selected) {
/* check if the color is editable */
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
@@ -1597,6 +1595,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
int prev_index = target_index;
/* Now do the movement of the stroke */
switch (direction) {
+ bGPDstroke *gps = NULL;
/* Bring to Front */
case GP_STROKE_MOVE_TOP:
LISTBASE_FOREACH (LinkData *, link, &selected) {
@@ -1755,21 +1754,26 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* only if selected */
- if (gps->flag & GP_STROKE_SELECT) {
- /* 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_material_editable(ob, gpl, gps) == false) {
- continue;
- }
-
- /* assign new color */
- gps->mat_nr = idx;
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+ if (!is_stroke_selected) {
+ continue;
+ }
- changed = true;
+ /* 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_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* assign new color */
+ gps->mat_nr = idx;
+
+ changed = true;
}
}
/* If not multi-edit, exit loop. */
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 8d1f841da6c..b57b2fb3ace 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -207,22 +207,6 @@ 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(gpd, 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 */
@@ -846,24 +830,99 @@ static void gpencil_duplicate_points(bGPdata *gpd,
ListBase *new_strokes,
const char *layername)
{
- bGPDspoint *pt;
- int i;
-
int start_idx = -1;
- /* Step through the original stroke's points:
- * - We accumulate selected points (from start_idx to current index)
- * and then convert that to a new stroke
- */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* searching for start, are waiting for end? */
- if (start_idx == -1) {
- /* is this the first selected point for a new island? */
- if (pt->flag & GP_SPOINT_SELECT) {
- start_idx = i;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if (start_idx == -1) {
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ start_idx = i;
+ }
+ continue;
+ }
+
+ size_t len = 0;
+
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ len = i - start_idx;
}
+ else if (i == gpc->tot_curve_points - 1) {
+ len = i - start_idx + 1;
+ }
+
+ if (len < 1) {
+ continue;
+ }
+
+ /* make a stupid copy first of the entire stroke (to get the flags too) */
+ bGPDstroke *gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, false);
+
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
+
+ /* To avoid a curve update, we just copy the points. */
+ int start_idx_stroke = gpc->curve_points[start_idx].point_index;
+ int len_stroke = (gpc->curve_points[start_idx + len - 1].point_index - start_idx_stroke) + 1;
+
+ gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len_stroke, "gps stroke points copy");
+ memcpy(gpsd->points, gps->points + start_idx_stroke, sizeof(bGPDspoint) * len_stroke);
+ gpsd->totpoints = len_stroke;
+
+ gpsd->editcurve = BKE_gpencil_stroke_editcurve_new(len);
+ bGPDcurve *gpcd = gpsd->editcurve;
+ memcpy(gpcd->curve_points, gpc->curve_points + start_idx, sizeof(bGPDcurve_point) * len);
+
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
+ memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
+
+ /* Copy weights */
+ int e = start_idx;
+ for (int j = 0; j < gpsd->totpoints; j++) {
+ MDeformVert *dvert_dst = &gps->dvert[e];
+ MDeformVert *dvert_src = &gps->dvert[j];
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ e++;
+ }
+ }
+
+ /* TODO: Copy vertex weights*/
+ for (uint32_t j = 0; j < gpcd->tot_curve_points; j++) {
+ bGPDcurve_point *gpcd_pt = &gpcd->curve_points[j];
+ BezTriple *bezt = &gpcd_pt->bezt;
+ gpcd_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ gpcd->flag |= GP_CURVE_SELECT;
+
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+
+ BLI_addtail(new_strokes, gpsd);
+
+ start_idx = -1;
}
- if ((start_idx != -1) || (start_idx == gps->totpoints - 1)) {
+ }
+ else {
+ /* Step through the original stroke's points:
+ * - We accumulate selected points (from start_idx to current index)
+ * and then convert that to a new stroke
+ */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ /* searching for start, are waiting for end? */
+ if ((start_idx != -1) || (start_idx == gps->totpoints - 1)) {
+ /* is this the first selected point for a new island? */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ start_idx = i;
+ }
+ continue;
+ }
+
size_t len = 0;
/* is this the end of current island yet?
@@ -877,45 +936,47 @@ static void gpencil_duplicate_points(bGPdata *gpd,
len = i - start_idx + 1;
}
+ if (len < 1) {
+ continue;
+ }
+
/* make copies of the relevant data */
- if (len) {
- bGPDstroke *gpsd;
+ bGPDstroke *gpsd;
- /* make a stupid copy first of the entire stroke (to get the flags too) */
- gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, true);
+ /* make a stupid copy first of the entire stroke (to get the flags too) */
+ gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, false);
- /* saves original layer name */
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
- /* now, make a new points array, and copy of the relevant parts */
- gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
- memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
- gpsd->totpoints = len;
+ /* now, make a new points array, and copy of the relevant parts */
+ gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
+ memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
+ gpsd->totpoints = len;
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
- memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
-
- /* Copy weights */
- int e = start_idx;
- for (int j = 0; j < gpsd->totpoints; j++) {
- MDeformVert *dvert_dst = &gps->dvert[e];
- MDeformVert *dvert_src = &gps->dvert[j];
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
- e++;
- }
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
+ memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
+
+ /* Copy weights */
+ int e = start_idx;
+ for (int j = 0; j < gpsd->totpoints; j++) {
+ MDeformVert *dvert_dst = &gps->dvert[e];
+ MDeformVert *dvert_src = &gps->dvert[j];
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ e++;
}
+ }
- BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
- BLI_addtail(new_strokes, gpsd);
+ BLI_addtail(new_strokes, gpsd);
- /* cleanup + reset for next */
- start_idx = -1;
- }
+ /* cleanup + reset for next */
+ start_idx = -1;
}
}
}
@@ -923,7 +984,6 @@ static void gpencil_duplicate_points(bGPdata *gpd,
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");
@@ -936,74 +996,108 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
}
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;
+ /* 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;
+
+ if (gpf == NULL) {
+ continue;
+ }
- if (gpf == NULL) {
+ /* make copies of selected strokes, and deselect these once we're done */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- /* 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 (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
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;
+ if (gpc->tot_curve_points == 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, true, true);
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
- BLI_strncpy(
- gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- /* Initialize triangle information. */
- BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+ /* Initialize triangle information. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
- /* 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;
- BKE_gpencil_stroke_select_index_reset(gps);
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&new_strokes, gpsd);
+ }
+ else {
+ gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
+ }
- changed = true;
+ /* Deselect the points */
+ for (uint32_t 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;
+ BKE_gpencil_stroke_select_index_reset(gps);
+
+ changed = true;
}
+ else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
+
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
- /* 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);
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
+
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+
+ /* Initialize triangle information. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
+
+ /* 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)
+ */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+
+ changed = true;
+ }
}
- CTX_DATA_END;
+
+ /* 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;
if (changed) {
/* updates */
@@ -1109,8 +1203,8 @@ static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gp
gpencil_copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
/* Deselect original point. */
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1185,7 +1279,7 @@ static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gp
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
@@ -1235,8 +1329,7 @@ static void gpencil_curve_extrude_points(bGPdata *gpd,
BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
- gps_new->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
BEZT_DESEL_ALL(&gpc_pt->bezt);
@@ -1282,8 +1375,7 @@ static void gpencil_curve_extrude_points(bGPdata *gpd,
BEZT_DESEL_ALL(&old_last->bezt);
}
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
@@ -1291,9 +1383,7 @@ 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;
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -1310,16 +1400,13 @@ static int gpencil_extrude_exec(bContext *C, wmOperator *op)
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
gpencil_curve_extrude_points(gpd, gpf, gps, gpc);
@@ -1432,8 +1519,6 @@ static void gpencil_strokes_copypastebuf_colors_name_to_material_free(GHash *nam
/* Free copy/paste buffer data */
void ED_gpencil_strokes_copybuf_free(void)
{
- bGPDstroke *gps, *gpsn;
-
/* Free the colors buffer
* NOTE: This is done before the strokes so that the ptrs are still safe
*/
@@ -1443,23 +1528,12 @@ void ED_gpencil_strokes_copybuf_free(void)
}
/* Free the stroke buffer */
- for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- 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(&gpencil_strokes_copypastebuf, gps);
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpencil_strokes_copypastebuf) {
+ BLI_remlink(&gpencil_strokes_copypastebuf, gps);
+ BKE_gpencil_free_stroke(gps);
}
- gpencil_strokes_copypastebuf.first = gpencil_strokes_copypastebuf.last = NULL;
+ BLI_listbase_clear(&gpencil_strokes_copypastebuf);
}
/**
@@ -1505,7 +1579,6 @@ 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");
@@ -1520,62 +1593,54 @@ static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
/* clear the buffer first */
ED_gpencil_strokes_copybuf_free();
- 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;
+ /* 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;
- if (gpf == NULL) {
+ if (gpf == NULL) {
+ continue;
+ }
+
+ /* make copies of selected strokes, and deselect these once we're done */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- /* 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;
- }
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
+ if (is_stroke_selected) {
+ 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);
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, 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);
- }
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
- /* 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);
- }
+ /* 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) {
@@ -1653,7 +1718,6 @@ 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;
@@ -1718,54 +1782,50 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
/* Ensure that all the necessary colors exist */
new_colors = gpencil_copybuf_validate_colormap(C);
- 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, true);
- new_stroke->runtime.tmp_layerinfo[0] = '\0';
- new_stroke->next = new_stroke->prev = NULL;
+ /* 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)) {
+ continue;
+ }
+ /* 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);
+ }
+ }
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ /* 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 == NULL) {
+ continue;
+ }
+ /* 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;
- if (on_back) {
- BLI_addhead(&gpf->strokes, new_stroke);
- }
- else {
- BLI_addtail(&gpf->strokes, new_stroke);
- }
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
- /* 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);
- }
- }
+ 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);
}
/* free temp data */
@@ -1874,7 +1934,12 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ gps->editcurve->flag & GP_CURVE_SELECT :
+ gps->flag & GP_STROKE_SELECT;
+
+ /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
+ if (is_stroke_selected) {
BLI_remlink(&gpf_src->strokes, gps);
BLI_addtail(&strokes, gps);
}
@@ -2210,7 +2275,11 @@ static int gpencil_delete_selected_strokes(bContext *C)
}
/* free stroke if selected */
- if (gps->flag & GP_STROKE_SELECT) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ gps->editcurve->flag & GP_CURVE_SELECT :
+ gps->flag & GP_STROKE_SELECT;
+
+ if (is_stroke_selected) {
BLI_remlink(&gpf->strokes, gps);
/* free stroke memory arrays, then stroke itself */
BKE_gpencil_free_stroke(gps);
@@ -2233,243 +2302,166 @@ static int gpencil_delete_selected_strokes(bContext *C)
/* ----------------------------------- */
-static bool gpencil_dissolve_selected_curve_points(bContext *C,
- bGPdata *gpd,
- eGP_DissolveMode mode)
+static bool gpencil_dissolve_selected_curve_points(bGPdata *gpd,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ eGP_DissolveMode mode,
+ const bool do_segments_refit,
+ const float error_threshold)
{
- 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;
- }
- }
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ return false;
+ }
- 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--;
- }
+ int num_points_remaining = gpc->tot_curve_points;
+ int old_num_points = gpc->tot_curve_points;
+ switch (mode) {
+ case GP_DISSOLVE_POINTS: {
+ num_points_remaining = BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_SELECT, do_segments_refit, error_threshold);
+ break;
+ }
+ case GP_DISSOLVE_BETWEEN: {
+ int first = -1, last = 0;
+ 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;
}
- break;
- default:
- return false;
- break;
- }
-
- if (num_points_remaining < 1) {
- /* Delete stroke */
- BLI_remlink(&gpf_->strokes, gps);
- BKE_gpencil_free_stroke(gps);
+ last = i;
+ }
}
- 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;
+ for (int i = first + 1; i < last; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ cpt->flag |= GP_CURVE_POINT_TAG;
}
+ }
- if (gpc->curve_points != NULL) {
- MEM_freeN(gpc->curve_points);
+ num_points_remaining = BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_TAG, do_segments_refit, error_threshold);
+ 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) {
+ cpt->flag |= GP_CURVE_POINT_TAG;
}
-
- 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;
+ num_points_remaining = BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_TAG, do_segments_refit, error_threshold);
+
+ break;
}
+ default:
+ return false;
+ break;
}
- GP_EDITABLE_CURVES_END(gps_iter);
- return changed;
+ if (num_points_remaining < 1) {
+ /* Delete stroke */
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ return true;
+ }
+ else if (num_points_remaining == old_num_points) {
+ /* Nothing to do so return. */
+ return false;
+ }
+
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ return true;
}
-static bool gpencil_dissolve_selected_stroke_points(bContext *C,
- bGPdata *gpd,
+static bool gpencil_dissolve_selected_stroke_points(bGPdata *gpd,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
eGP_DissolveMode mode)
{
bool changed = false;
int first = 0;
int last = 0;
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- /* the stroke must have at least one point selected for any operator */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- MDeformVert *dvert = NULL;
- int i;
+ /* the stroke must have at least one point selected for any operator */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ MDeformVert *dvert = NULL;
+ int i;
- int tot = gps->totpoints; /* number of points in new buffer */
+ int tot = gps->totpoints; /* number of points in new buffer */
- /* first pass: count points to remove */
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- /* Count how many points are selected (i.e. how many to remove) */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* selected point - one of the points to remove */
- tot--;
- }
+ /* first pass: count points to remove */
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ /* Count how many points are selected (i.e. how many to remove) */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* selected point - one of the points to remove */
+ tot--;
}
- break;
- case GP_DISSOLVE_BETWEEN:
- /* need to find first and last point selected */
- first = -1;
- last = 0;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- if (first < 0) {
- first = i;
- }
- last = i;
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ /* need to find first and last point selected */
+ first = -1;
+ last = 0;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (first < 0) {
+ first = i;
}
+ last = i;
}
- /* count unselected points in the range */
- for (i = first, pt = gps->points + first; i < last; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- tot--;
- }
+ }
+ /* count unselected points in the range */
+ for (i = first, pt = gps->points + first; i < last; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ tot--;
}
- break;
- case GP_DISSOLVE_UNSELECT:
- /* count number of unselected points */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- tot--;
- }
+ }
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ /* count number of unselected points */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ tot--;
}
- break;
- default:
- return false;
- break;
- }
+ }
+ break;
+ default:
+ return false;
+ break;
+ }
- /* if no points are left, we simply delete the entire stroke */
- if (tot <= 0) {
- /* remove the entire stroke */
- BLI_remlink(&gpf_->strokes, gps);
- BKE_gpencil_free_stroke(gps);
- }
- else {
- /* just copy all points to keep into a smaller buffer */
- bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot,
- "new gp stroke points copy");
- bGPDspoint *npt = new_points;
+ /* if no points are left, we simply delete the entire stroke */
+ if (tot <= 0) {
+ /* remove the entire stroke */
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ else {
+ /* just copy all points to keep into a smaller buffer */
+ bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
+ bGPDspoint *npt = new_points;
- MDeformVert *new_dvert = NULL;
- MDeformVert *ndvert = NULL;
+ MDeformVert *new_dvert = NULL;
+ MDeformVert *ndvert = NULL;
- if (gps->dvert != NULL) {
- new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
- ndvert = new_dvert;
- }
+ if (gps->dvert != NULL) {
+ new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
+ ndvert = new_dvert;
+ }
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- }
- }
- if (gps->dvert != NULL) {
- dvert++;
- }
- }
- break;
- case GP_DISSOLVE_BETWEEN:
- /* copy first segment */
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < first; i++, pt++) {
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
*npt = *pt;
npt++;
@@ -2477,29 +2469,31 @@ static bool gpencil_dissolve_selected_stroke_points(bContext *C,
*ndvert = *dvert;
ndvert->dw = MEM_dupallocN(dvert->dw);
ndvert++;
- dvert++;
}
}
- /* copy segment (selected points) */
- (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
- for (i = first, pt = gps->points + first; i < last; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- *npt = *pt;
- npt++;
+ if (gps->dvert != NULL) {
+ dvert++;
+ }
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ /* copy first segment */
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < first; i++, pt++) {
+ *npt = *pt;
+ npt++;
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- }
- }
- if (gps->dvert != NULL) {
- dvert++;
- }
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
}
- /* copy last segment */
- (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
- for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
+ }
+ /* copy segment (selected points) */
+ (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
+ for (i = first, pt = gps->points + first; i < last; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
*npt = *pt;
npt++;
@@ -2507,85 +2501,125 @@ static bool gpencil_dissolve_selected_stroke_points(bContext *C,
*ndvert = *dvert;
ndvert->dw = MEM_dupallocN(dvert->dw);
ndvert++;
- dvert++;
}
}
+ if (gps->dvert != NULL) {
+ dvert++;
+ }
+ }
+ /* copy last segment */
+ (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
+ for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
+ *npt = *pt;
+ npt++;
- break;
- case GP_DISSOLVE_UNSELECT:
- /* copy any selected point */
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- *npt = *pt;
- npt++;
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ /* copy any selected point */
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ *npt = *pt;
+ npt++;
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- }
- }
if (gps->dvert != NULL) {
- dvert++;
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
}
}
- break;
- }
+ if (gps->dvert != NULL) {
+ dvert++;
+ }
+ }
+ break;
+ }
- /* free the old buffer */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
+ /* free the old buffer */
+ if (gps->points) {
+ MEM_freeN(gps->points);
+ }
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
- /* save the new buffer */
- gps->points = new_points;
- gps->dvert = new_dvert;
- gps->totpoints = tot;
+ /* save the new buffer */
+ gps->points = new_points;
+ gps->dvert = new_dvert;
+ gps->totpoints = tot;
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
- /* deselect the stroke, since none of its selected points will still be selected */
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
+ /* deselect the stroke, since none of its selected points will still be selected */
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
}
-
- changed = true;
}
+
+ changed = true;
}
- 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)
+static int gpencil_dissolve_selected_points(bContext *C,
+ eGP_DissolveMode mode,
+ const bool do_segments_refit,
+ const float error_threshold)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- 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;
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- if (is_curve_edit) {
- changed = gpencil_dissolve_selected_curve_points(C, gpd, mode);
- }
- else {
- changed = gpencil_dissolve_selected_stroke_points(C, gpd, mode);
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ /* Use LISTBASE_FOREACH_MUTABLE because strokes might be entirely deleted. */
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (gpencil_dissolve_selected_curve_points(
+ gpd, gpf, gps, mode, do_segments_refit, error_threshold)) {
+ changed = true;
+ }
+ }
+ else {
+ if (gpencil_dissolve_selected_stroke_points(gpd, gpf, gps, mode)) {
+ changed = true;
+ }
+ }
+ }
+ }
+ }
}
+ CTX_DATA_END;
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);
return OPERATOR_FINISHED;
}
+
return OPERATOR_CANCELLED;
}
@@ -2596,7 +2630,6 @@ 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;
@@ -2622,23 +2655,27 @@ static int gpencil_delete_selected_points(bContext *C)
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
- /* deselect old stroke, since it will be used as template for the new strokes */
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
- if (is_curve_edit) {
- bGPDcurve *gpc = gps->editcurve;
BKE_gpencil_curve_delete_tagged_points(
gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
+ changed = true;
}
- else {
+ }
+ else {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* deselect old stroke, since it will be used as template for the new strokes */
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
/* delete unwanted points by splitting stroke into several smaller ones */
BKE_gpencil_stroke_delete_tagged_points(
gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ changed = true;
}
-
- changed = true;
}
}
}
@@ -2732,8 +2769,10 @@ void GPENCIL_OT_delete(wmOperatorType *ot)
static int gpencil_dissolve_exec(bContext *C, wmOperator *op)
{
eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
+ const bool do_segments_refit = RNA_boolean_get(op->ptr, "do_segments_refit");
+ const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
- return gpencil_dissolve_selected_points(C, mode);
+ return gpencil_dissolve_selected_points(C, mode, do_segments_refit, error_threshold);
}
void GPENCIL_OT_dissolve(wmOperatorType *ot)
@@ -2769,6 +2808,22 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
0,
"Type",
"Method used for dissolving stroke points");
+
+ RNA_def_boolean(ot->srna,
+ "do_segments_refit",
+ false,
+ "Refit Segments",
+ "Try to match the previous shape of bézier stroke segment");
+
+ RNA_def_float(ot->srna,
+ "error_threshold",
+ GP_DEFAULT_CURVE_ERROR,
+ 0.0f,
+ 100.0f,
+ "Threshold",
+ "Bézier curve fitting error threshold",
+ 0.0f,
+ 3.0f);
}
/** \} */
@@ -2799,7 +2854,6 @@ 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) {
@@ -2821,39 +2875,60 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
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);
+ /* We move the entire handle if the control point is selected. */
+ if (bezt->f2 & SELECT) {
+ float tmp[3], offset[3];
+ mul_v3_m4v3(tmp, diff_mat, bezt->vec[1]);
+
+ /* calculate the offset vector */
+ offset[0] = gridf * floorf(0.5f + tmp[0] / gridf) - tmp[0];
+ offset[1] = gridf * floorf(0.5f + tmp[1] / gridf) - tmp[1];
+ offset[2] = gridf * floorf(0.5f + tmp[2] / gridf) - tmp[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(bezt->vec[0], inv_diff_mat, bezt->vec[0]);
+ mul_v3_m4v3(bezt->vec[1], inv_diff_mat, bezt->vec[1]);
+ mul_v3_m4v3(bezt->vec[2], inv_diff_mat, bezt->vec[2]);
+ }
+ else {
+ /* Move the handles to the grid individually. */
+ float tmp0[3], tmp1[3];
+ mul_v3_m4v3(tmp0, diff_mat, bezt->vec[0]);
+ mul_v3_m4v3(tmp1, diff_mat, bezt->vec[2]);
+
+ /* Calculate nearest point on the grid. */
+ tmp0[0] = gridf * floorf(0.5f + tmp0[0] / gridf);
+ tmp0[1] = gridf * floorf(0.5f + tmp0[1] / gridf);
+ tmp0[2] = gridf * floorf(0.5f + tmp0[2] / gridf);
+
+ tmp1[0] = gridf * floorf(0.5f + tmp1[0] / gridf);
+ tmp1[1] = gridf * floorf(0.5f + tmp1[1] / gridf);
+ tmp1[2] = gridf * floorf(0.5f + tmp1[2] / gridf);
+
+ /* Write to the selected handles. */
+ if (bezt->f1 & SELECT) {
+ mul_v3_m4v3(bezt->vec[0], inv_diff_mat, tmp0);
+ }
+ if (bezt->f3 & SELECT) {
+ mul_v3_m4v3(bezt->vec[2], inv_diff_mat, tmp1);
+ }
+ }
changed = true;
}
@@ -2861,11 +2936,13 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
if (changed) {
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
/* 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];
@@ -2924,7 +3001,6 @@ 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);
@@ -2933,53 +3009,117 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
const float *cursor_global = scene->cursor.location;
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];
+ 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_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
+ /* calculate difference matrix */
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *pt;
- int i;
+ 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_material_editable(obact, gpl, gps) == false) {
+ continue;
+ }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((is_stroke_selected) == 0) {
+ continue;
+ }
+
+ if (use_offset) {
+ /* TODO: Allow using midpoint instead? */
+ float offset[3];
+
+ /* To avoid recalculating the curve, we will offset the curve data first and then all the
+ * stroke points. */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ float tmp[3];
+ bGPDcurve *gpc = gps->editcurve;
+ /* Calculate offset. */
+ mul_v3_m4v3(tmp, diff_mat, gpc->curve_points->bezt.vec[1]);
+ sub_v3_v3v3(offset, cursor_global, tmp);
+
+ /* Offset points. */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ for (int j = 0; j < 3; j++) {
+ add_v3_v3(bezt->vec[j], offset);
+ }
+ }
}
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
- continue;
+ else {
+ /* compute offset from first point of stroke to cursor */
+ sub_v3_v3v3(offset, cursor_global, &gps->points->x);
}
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0) {
- continue;
+
+ /* apply offset to all points in the stroke */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ add_v3_v3(&pt->x, offset);
}
- if (use_offset) {
- float offset[3];
+ changed = true;
+ }
+ else {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ float inv_diff_mat[4][4];
+ invert_m4_m4_safe(inv_diff_mat, diff_mat);
- /* compute offset from first point of stroke to cursor */
- /* TODO: Allow using midpoint instead? */
- sub_v3_v3v3(offset, cursor_global, &gps->points->x);
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ continue;
+ }
+
+ float cur[3];
+ mul_v3_m4v3(cur, inv_diff_mat, cursor_global);
+
+ /* If the control point is selected, snap it to the cursor and offset the handles
+ * accordingly. */
+ if (bezt->f2 & SELECT) {
+ float offset[3];
+ sub_v3_v3v3(offset, cur, bezt->vec[1]);
+
+ for (int j = 0; j < 3; j++) {
+ add_v3_v3(bezt->vec[j], offset);
+ }
+ }
+ /* Snap the handles to the cursor. */
+ else {
+ if (bezt->f1 & SELECT) {
+ copy_v3_v3(bezt->vec[0], cur);
+ }
+ if (bezt->f3 & SELECT) {
+ copy_v3_v3(bezt->vec[2], cur);
+ }
+ }
- /* 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;
}
- changed = true;
+ if (changed) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ }
}
else {
/* affect each selected point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
if (pt->flag & GP_SPOINT_SELECT) {
copy_v3_v3(&pt->x, cursor_global);
gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
@@ -3051,9 +3191,6 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
BKE_gpencil_layer_transform_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;
@@ -3062,13 +3199,45 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
if (ED_gpencil_stroke_material_editable(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 (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
+ }
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ if ((cpt->flag * GP_CURVE_POINT_SELECT) == 0) {
+ continue;
+ }
+
+ float fpt[3];
+ for (int j = 0; j < 3; j++) {
+ if (BEZT_ISSEL_IDX(bezt, j)) {
+ mul_v3_m4v3(fpt, diff_mat, bezt->vec[j]);
+
+ add_v3_v3(r_centroid, fpt);
+ minmax_v3v3_v3(r_min, r_max, fpt);
+ (*count)++;
+ }
+ }
+ }
+
+ changed = true;
}
+ else {
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ continue;
+ }
/* apply parent transformations */
float fpt[3];
mul_v3_m4v3(fpt, diff_mat, &pt->x);
@@ -3078,9 +3247,8 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
(*count)++;
}
+ changed = true;
}
-
- changed = true;
}
}
}
@@ -3088,12 +3256,11 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
return changed;
}
-static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
+static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(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);
@@ -3105,12 +3272,7 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
INIT_MINMAX(min, max);
bool changed = false;
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
- }
+ changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
if (changed) {
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
@@ -3218,8 +3380,6 @@ 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 */
if (ELEM(NULL, gpd)) {
@@ -3237,11 +3397,18 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
/* skip strokes that are not selected or invalid for current view */
- if (((gps->flag & GP_STROKE_SELECT) == 0) ||
- ED_gpencil_stroke_can_use(C, gps) == false) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
+ continue;
+ }
+
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
/* skip hidden or locked colors */
@@ -3271,14 +3438,13 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
if (before != (gps->flag & GP_STROKE_CYCLIC)) {
/* Create new geometry. */
- if (is_curve_edit) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
else if ((gps->flag & GP_STROKE_CYCLIC) && geometry) {
BKE_gpencil_stroke_close(gps);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
changed = true;
@@ -3303,22 +3469,6 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
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.
@@ -3342,15 +3492,17 @@ 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;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
- prop = RNA_def_boolean(
- ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke");
+ prop = RNA_def_boolean(ot->srna,
+ "geometry",
+ false,
+ "Create Geometry",
+ "Create new geometry for closing stroke (only applies for poly strokes)");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -3371,6 +3523,7 @@ 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 bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const int type = RNA_enum_get(op->ptr, "type");
/* sanity checks */
@@ -3381,45 +3534,55 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
bool changed = false;
/* loop all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- if (gpl->actframe == NULL) {
- continue;
- }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
- /* skip strokes that are not selected or invalid for current view */
- if (((gps->flag & GP_STROKE_SELECT) == 0) || (ED_gpencil_stroke_can_use(C, gps) == false)) {
- continue;
- }
- /* skip hidden or locked colors */
- if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
- (gp_style->flag & GP_MATERIAL_LOCKED)) {
- continue;
- }
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- short prev_first = gps->caps[0];
- short prev_last = gps->caps[1];
+ /* skip strokes that are not selected or invalid for current view */
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
- if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) {
- ++gps->caps[0];
- if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- }
- }
- if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) {
- ++gps->caps[1];
- if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
- }
- if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
+ if (!is_stroke_selected) {
+ continue;
+ }
+ /* skip hidden or locked colors */
+ if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
+ (gp_style->flag & GP_MATERIAL_LOCKED)) {
+ continue;
+ }
- if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
- changed = true;
+ 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) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) {
+ ++gps->caps[1];
+ if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
+ 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;
+ }
+ }
}
}
}
@@ -3543,11 +3706,6 @@ 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;
}
@@ -3567,29 +3725,34 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
/* Add all stroke selected of the frame. */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* 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_material_editable(ob, gpl, gps) == false) {
- continue;
- }
- elem = &strokes_list[tot_strokes];
- elem->gpf = gpf;
- elem->gps = gps;
- elem->used = false;
-
- tot_strokes++;
- /* Limit the number of strokes. */
- if (tot_strokes == max_join_strokes) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Too many strokes selected, only joined first %d strokes",
- max_join_strokes);
- break;
- }
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
+ 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_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
+ elem = &strokes_list[tot_strokes];
+ elem->gpf = gpf;
+ elem->gps = gps;
+ elem->used = false;
+
+ tot_strokes++;
+ /* Limit the number of strokes. */
+ if (tot_strokes == max_join_strokes) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Too many strokes selected, only joined first %d strokes",
+ max_join_strokes);
+ break;
}
}
}
@@ -3623,12 +3786,16 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
}
/* Calc geometry data for new stroke. */
- BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
/* If join only, delete old strokes. */
if (type == GP_STROKE_JOIN) {
for (int i = 0; i < tot_strokes; i++) {
elem = &strokes_list[i];
+ if (GPENCIL_STROKE_TYPE_BEZIER(elem->gps) != GPENCIL_STROKE_TYPE_BEZIER(gps_new)) {
+ continue;
+ }
+
BLI_remlink(&elem->gpf->strokes, elem->gps);
BKE_gpencil_free_stroke(elem->gps);
}
@@ -3679,7 +3846,7 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot)
/** \name Stroke Flip Operator
* \{ */
-static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -3689,7 +3856,6 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *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) {
@@ -3699,26 +3865,27 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
}
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* 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_material_editable(ob, gpl, gps) == false) {
- continue;
- }
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Flip stroke. */
- BKE_gpencil_stroke_flip(gps);
- }
+ if (!is_stroke_selected) {
+ continue;
+ }
- changed = true;
+ /* 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_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* Flip stroke. */
+ BKE_gpencil_stroke_flip(gps);
+
+ changed = true;
}
}
CTX_DATA_END;
@@ -3762,7 +3929,6 @@ 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);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Init snap context for geometry projection. */
@@ -3785,37 +3951,28 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
continue;
}
for (bGPDstroke *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) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
continue;
}
- bool curve_select = false;
- if (is_curve_edit && gps->editcurve != NULL) {
- curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
+ cfra_prv = gpf->framenum;
+ CFRA = gpf->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
- 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)) {
- cfra_prv = gpf->framenum;
- CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph);
- }
-
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
+ 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(gpd, gps, gps->editcurve);
+ /* TODO: Reproject curve data and regenerate stroke.
+ * Right now we are using the projected points to regenerate the curve. This will most
+ * likely change the handles which is usually not wanted.*/
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
-
- changed = true;
- }
+ changed = true;
}
}
/* If not multi-edit, exit loop. */
@@ -3909,7 +4066,8 @@ 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(gpd, gps);
+ /* TODO: maybe add an option to only include selected strokes? */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -3957,7 +4115,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->flag & GP_STROKE_SELECT && GPENCIL_STROKE_TYPE_POLY(gps)) {
for (int r = 0; r < repeat; r++) {
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
@@ -3984,6 +4142,22 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
}
}
+ else if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_smooth(gps,
+ factor,
+ 2,
+ repeat,
+ only_selected,
+ false,
+ smooth_position,
+ smooth_thickness,
+ smooth_strength);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd_, gps, GP_GEO_UPDATE_DEFAULT);
+ }
+ }
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
}
@@ -4164,38 +4338,33 @@ static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
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)
- {
+
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
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);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
- }
- else {
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ else {
if (gps->flag & GP_STROKE_SELECT) {
gpencil_stroke_subdivide(gps, cuts);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
- if (changed) {
- /* smooth stroke */
- gpencil_smooth_stroke(C, op);
- }
+ if (changed) {
+ /* smooth stroke */
+ gpencil_smooth_stroke(C, op);
}
if (changed) {
@@ -4207,22 +4376,6 @@ static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
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;
@@ -4238,7 +4391,6 @@ 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;
@@ -4274,23 +4426,24 @@ static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- 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);
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (gps->editcurve->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_simplify_adaptive(gps, factor);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else 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);
if (changed) {
/* notifiers */
@@ -4334,24 +4487,26 @@ static int gpencil_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- 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);
- }
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_simplify_fixed(gps, steps);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ changed = true;
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else if (gps->flag & GP_STROKE_SELECT) {
+ for (int i = 0; i < steps; i++) {
+ BKE_gpencil_stroke_simplify_fixed(gpd, gps);
+ }
+ changed = true;
+ }
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
if (changed) {
/* notifiers */
@@ -4538,14 +4693,13 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
bGPdata *gpd_dst = NULL;
bGPDlayer *gpl_dst = NULL;
bGPDframe *gpf_dst = NULL;
- bGPDspoint *pt;
Material *ma = NULL;
- int i, idx;
+ int idx;
eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
+ const bool keep_ends = RNA_boolean_get(op->ptr, "keep_ends");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
/* sanity checks */
if (ELEM(NULL, gpd_src)) {
@@ -4604,6 +4758,13 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
gpf_dst = NULL;
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
+ continue;
+ }
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -4613,70 +4774,202 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+
/* Separate selected strokes. */
- if (gps->flag & GP_STROKE_SELECT) {
- /* add layer if not created before */
- if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
- BKE_gpencil_layer_copy_settings(gpl, gpl_dst);
- /* Copy masks. */
- BKE_gpencil_layer_mask_copy(gpl, gpl_dst);
- }
+ if (gpl_dst == NULL) {
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
+ BKE_gpencil_layer_copy_settings(gpl, gpl_dst);
+ /* Copy masks. */
+ BKE_gpencil_layer_mask_copy(gpl, gpl_dst);
+ }
- /* add frame if not created before */
- if (gpf_dst == NULL) {
- gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
- }
+ /* add frame if not created before */
+ if (gpf_dst == NULL) {
+ gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
+ }
- /* add duplicate materials */
+ /* add duplicate materials */
- /* XXX same material can be in multiple slots. */
- ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
+ /* XXX same material can be in multiple slots. */
+ ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
- idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
+ idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
- /* selected points mode */
- if (mode == GP_SEPARATE_POINT) {
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ /* selected points mode */
+ if (mode == GP_SEPARATE_POINT) {
+ /* make copy of source stroke */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
+
+ /* Reassign material. */
+ gps_dst->mat_nr = idx;
+
+ /* link to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc_src = gps->editcurve;
+ bGPDcurve *gpc_dst = gps_dst->editcurve;
+
+ /* Flip the selection */
+ for (int i = 0; i < gpc_dst->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc_dst->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ cpt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ else {
+ cpt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ }
+
+ if (keep_ends) {
+ /* Shrink the selection in the original stroke to keep the connecting points. */
+ int tot_selected = 0, num_deselected = 0;
+
+ bool prev_sel = false;
+ int i;
+ for (i = 0; i < gpc_src->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc_src->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);
+ num_deselected++;
+ }
+ prev_sel = true;
+ tot_selected++;
+ }
+ 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 = gpc_src->tot_curve_points - 1; i > 0; i--) {
+ bGPDcurve_point *gpc_pt = &gpc_src->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);
+ num_deselected++;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
+ }
+
+ /* Deselect curve if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gpc_src->flag &= ~GP_CURVE_SELECT;
+ }
}
- else {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* Reassign material. */
- gps_dst->mat_nr = idx;
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd_dst, gpf_dst, gps_dst, NULL, gpc_dst, GP_CURVE_POINT_SELECT);
+
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd_src, gpf, gps, gps->next, gpc_src, GP_CURVE_POINT_SELECT);
+ }
+ else {
- /* link to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* Invert selection status of all points in destination stroke */
+ for (int i = 0; i < gps_dst->totpoints; i++) {
+ bGPDspoint *pt = &gps_dst->points[i];
+ 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;
+ if (keep_ends) {
+ bGPDspoint *pt;
+ int i, tot_selected = 0, num_deselected = 0;
+ bool prev_sel;
+
+ /* 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;
+ num_deselected++;
+ }
+ prev_sel = true;
+ tot_selected++;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
}
- /* delete selected points from destination stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+ /* 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;
+ num_deselected++;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
+ }
- /* delete selected points from origin stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd_src, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ /* Deselect stroke if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
}
+
+ /* delete selected points from destination stroke */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+
+ /* delete selected points from origin stroke */
+ BKE_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) {
+ /* deselect old stroke */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ gps->editcurve->flag &= ~GP_CURVE_SELECT;
}
- /* selected strokes mode */
- else if (mode == GP_SEPARATE_STROKE) {
- /* deselect old stroke */
+ else {
gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
- /* unlink from source frame */
- BLI_remlink(&gpf->strokes, gps);
- gps->prev = gps->next = NULL;
- /* relink to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps);
- /* Reassign material. */
- gps->mat_nr = idx;
}
+ BKE_gpencil_stroke_select_index_reset(gps);
+ /* unlink from source frame */
+ BLI_remlink(&gpf->strokes, gps);
+ gps->prev = gps->next = NULL;
+ /* relink to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps);
+ /* Reassign material. */
+ gps->mat_nr = idx;
}
}
}
@@ -4754,6 +5047,24 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static bool gpencil_stroke_separate_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+
+ /* Only show connect keep_ends in GP_SEPARATE_POINT mode. */
+ if (STREQ(prop_id, "keep_ends")) {
+ const int type = RNA_enum_get(op->ptr, "mode");
+ if (type == GP_SEPARATE_POINT) {
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
{
static const EnumPropertyItem separate_type[] = {
@@ -4772,12 +5083,19 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
ot->invoke = WM_menu_invoke;
ot->exec = gpencil_stroke_separate_exec;
ot->poll = gpencil_strokes_edit3d_poll;
+ ot->poll_property = gpencil_stroke_separate_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", "");
+
+ RNA_def_boolean(ot->srna,
+ "keep_ends",
+ false,
+ "Keep Ends",
+ "Seperate the selected points, but keep the ends in both");
}
/** \} */
@@ -4786,19 +5104,17 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
/** \name Stroke Split Operator
* \{ */
-static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDspoint *pt;
- int i;
/* sanity checks */
if (ELEM(NULL, gpd)) {
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);
+ bool changed = false;
/* loop strokes and split parts */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -4811,8 +5127,9 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
continue;
}
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
-
+ /* We are deleting the strokes we are iterating over and adding new strokes to the end of
+ * the frame. So we need to use backwards mutable iteration here. */
+ LISTBASE_FOREACH_BACKWARD_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -4821,42 +5138,66 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+
/* Split selected strokes. */
- if (gps->flag & GP_STROKE_SELECT) {
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
}
- 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);
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
+ 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;
+ bGPDcurve *gpc_dst = gps_dst->editcurve;
+ for (int i = 0; i < gpc_dst->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc_dst->curve_points[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ cpt->flag &= ~GP_CURVE_POINT_TAG;
+ }
+ else {
+ cpt->flag |= GP_CURVE_POINT_TAG;
}
+ }
- /* delete selected points from destination stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd, gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd, gpf, gps_dst, NULL, gpc_dst, GP_CURVE_POINT_TAG);
- /* delete selected points from origin stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
- }
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
}
- }
- /* select again tagged points */
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *ptn = gps->points;
- for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
- if (ptn->flag & GP_SPOINT_TAG) {
- ptn->flag |= GP_SPOINT_SELECT;
- ptn->flag &= ~GP_SPOINT_TAG;
+ else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
}
+
+ /* Make copy of source stroke. */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, false);
+
+ /* Link to same frame. */
+ BLI_addtail(&gpf->strokes, gps_dst);
+
+ /* Tag the unselected points. */
+ for (int i = 0; i < gps_dst->totpoints; i++) {
+ bGPDspoint *pt = &gps_dst->points[i];
+ if (pt->flag & GP_SPOINT_SELECT) {
+ pt->flag &= ~GP_SPOINT_TAG;
+ }
+ else {
+ pt->flag |= GP_SPOINT_TAG;
+ }
+ }
+
+ /* Delete tagged points from destination stroke. */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps_dst, NULL, GP_SPOINT_TAG, false, 0);
+
+ /* Delete selected points from origin stroke. */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
+
+ changed = true;
}
}
@@ -4868,9 +5209,10 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- 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) {
+ 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;
}
@@ -4934,7 +5276,7 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
+ RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 1.0f);
RNA_def_boolean(ot->srna,
"only_selected",
true,
@@ -5305,24 +5647,31 @@ static int gpencil_merge_by_distance_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- 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);
+ bool changed = false;
+ /* Go through each editable selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ /* TODO: don't hardcode the refit and threshold. Figure out how to set these. */
+ if (BKE_gpencil_editcurve_merge_distance(gps, threshold, unselected, false, 0.0f)) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ changed = true;
+ }
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_merge_distance(gpd, gpf_, gps, threshold, unselected);
+ changed = true;
+ }
}
+ 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;
}
@@ -5474,8 +5823,7 @@ static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op)
}
}
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
/* If not multi-edit, exit loop. */
diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c
index e766a410889..925375d7103 100644
--- a/source/blender/editors/gpencil/gpencil_edit_curve.c
+++ b/source/blender/editors/gpencil/gpencil_edit_curve.c
@@ -35,6 +35,7 @@
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_report.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -49,91 +50,14 @@
#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;
+#include "UI_interface.h"
+#include "UI_resources.h"
- 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(gpd, 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;
+#include "gpencil_intern.h"
- /* 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.0f);
- RNA_def_property_ui_range(prop, FLT_MIN, 10.0f, 0.1f, 5);
-}
+/* -------------------------------------------------------------------- */
+/** \name Set handle type operator
+ * \{ */
static int gpencil_editcurve_set_handle_type_exec(bContext *C, wmOperator *op)
{
@@ -169,8 +93,7 @@ static int gpencil_editcurve_set_handle_type_exec(bContext *C, wmOperator *op)
}
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
GP_EDITABLE_CURVES_END(gps_iter);
@@ -199,7 +122,7 @@ void GPENCIL_OT_stroke_editcurve_set_handle_type(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = gpencil_editcurve_set_handle_type_exec;
- ot->poll = gpencil_curve_edit_mode_poll;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -207,5 +130,151 @@ void GPENCIL_OT_stroke_editcurve_set_handle_type(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set stroke type operator
+ * \{ */
+
+static int gpencil_stroke_set_type_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ eGPStrokeType type = RNA_enum_get(op->ptr, "type");
+ const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
+
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bool changed = false;
+ switch (type) {
+ case STROKE_POLY: {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (only_selected && (gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
+ }
+ BKE_gpencil_stroke_editcurve_sync_selection(gpd, gps, gpc);
+ BKE_gpencil_free_stroke_editcurve(gps);
+ changed = true;
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ } break;
+
+ case STROKE_BEZIER: {
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
+
+ GP_EDITABLE_STROKES_BEGIN (gps_iter, C, gpl, gps) {
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (only_selected && (gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
+
+ BKE_gpencil_stroke_refit_curve(gps, threshold, corner_angle, GP_GEO_UPDATE_DEFAULT);
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL);
+
+ /* Select all curve points. */
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&pt->bezt);
+ }
+ gpc->flag |= GP_CURVE_SELECT;
+
+ /* Deselect stroke points. */
+ for (uint32_t i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
+ changed = true;
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gps_iter);
+ } break;
+ default: {
+ BKE_report(op->reports, RPT_ERROR, "Unknown stroke type");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ 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 void gpencil_stroke_set_type_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+ uiLayoutSetPropSep(layout, true);
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ uiItemR(layout, &ptr, "only_selected", 0, NULL, ICON_NONE);
+ eGPStrokeType type = RNA_enum_get(&ptr, "type");
+ if (type == STROKE_BEZIER) {
+ uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "corner_angle", 0, NULL, ICON_NONE);
+ }
+}
+
+void GPENCIL_OT_stroke_set_type(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+ static const EnumPropertyItem stroke_types[] = {
+ {STROKE_POLY, "POLY", 0, "Poly", ""},
+ {STROKE_BEZIER, "BEZIER", 0, "Bezier", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Set Stroke Type";
+ ot->idname = "GPENCIL_OT_stroke_set_type";
+ ot->description = "Set the type of the stroke";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_set_type_exec;
+ ot->poll = gpencil_active_layer_poll;
+ ot->ui = gpencil_stroke_set_type_ui;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_enum(ot->srna, "type", stroke_types, STROKE_POLY, "Type", "Stroke type");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(
+ ot->srna, "only_selected", true, "Selected Only", "Only set the type for selected strokes");
+
+ prop = RNA_def_float(ot->srna,
+ "threshold",
+ GP_DEFAULT_CURVE_ERROR,
+ 0.0f,
+ 100.0f,
+ "Threshold",
+ "Bézier curve fitting error threshold",
+ 0.0f,
+ 3.0f);
+
+ prop = RNA_def_float_distance(ot->srna,
+ "corner_angle",
+ GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE,
+ 0.0f,
+ M_PI,
+ "Corner Angle",
+ "Angle threshold to be treated as corners",
+ 0.0f,
+ M_PI);
+ RNA_def_property_subtype(prop, PROP_ANGLE);
+}
/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 091ff2c16b0..b1c83a2634e 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1599,7 +1599,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps);
+ BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* ----------------------- */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index d1a1e417d9e..49ca83c511c 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -227,6 +227,12 @@ typedef struct tGPDprimitive {
} tGPDprimitive;
+/* Stroke types enum definition. */
+typedef enum eGPStrokeType {
+ STROKE_POLY = 0,
+ STROKE_BEZIER = 1,
+} eGPStrokeType;
+
bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1);
void gpencil_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
@@ -397,8 +403,8 @@ 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);
+void GPENCIL_OT_stroke_set_type(struct wmOperatorType *ot);
/* stroke sculpting -- */
@@ -519,6 +525,9 @@ void GPENCIL_OT_primitive_polyline(struct wmOperatorType *ot);
void GPENCIL_OT_primitive_circle(struct wmOperatorType *ot);
void GPENCIL_OT_primitive_curve(struct wmOperatorType *ot);
+wmKeyMap *gpencil_curve_draw_modal_keymap(wmKeyConfig *keyconf);
+void GPENCIL_OT_draw_curve(struct wmOperatorType *ot);
+
/* vertex groups ------------ */
void GPENCIL_OT_vertex_group_assign(struct wmOperatorType *ot);
void GPENCIL_OT_vertex_group_remove_from(struct wmOperatorType *ot);
@@ -639,10 +648,10 @@ struct GP_EditableStrokes_Iter {
/* skip strokes that are invalid for current view */ \
if (ED_gpencil_stroke_can_use(C, gps) == false) \
continue; \
- if (gps->editcurve == NULL) \
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) \
continue; \
bGPDcurve *gpc = gps->editcurve; \
- /* ... Do Stuff With Strokes ... */
+ /* ... Do Stuff With Curves ... */
#define GP_EDITABLE_CURVES_END(gpstroke_iter) \
} \
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 0062e363cdf..a5fc76ed041 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -424,7 +424,7 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
/* Add to strokes. */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
@@ -569,7 +569,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
gpencil_interpolate_smooth_stroke(new_stroke, tgpi->smooth_factor, tgpi->smooth_steps);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
/* add to strokes */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
}
@@ -844,7 +844,7 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
gps_dst->flag &= ~GP_STROKE_TAG;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps_dst);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps_dst, GP_GEO_UPDATE_DEFAULT);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
@@ -1398,7 +1398,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
gpencil_interpolate_smooth_stroke(new_stroke, smooth_factor, smooth_steps);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
/* Add strokes to frame. */
bGPDframe *interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 259b2882589..ef31ed6c3da 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -523,7 +523,7 @@ static int gpencil_stroke_merge_exec(bContext *C, wmOperator *op)
gpencil_dissolve_points(C);
}
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* 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 35640cf3b66..08543cce5bd 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -64,13 +64,6 @@ 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)
{
@@ -175,6 +168,13 @@ static bool gpencil_stroke_paintmode_tint_poll(bContext *C)
return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_TINT);
}
+#if 0
+static bool gpencil_stroke_paintmode_curve_poll(bContext *C)
+{
+ return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_CURVE);
+}
+#endif
+
/* Poll callback for stroke sculpting mode */
static bool gpencil_stroke_sculptmode_poll(bContext *C)
{
@@ -317,15 +317,6 @@ 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)
{
@@ -354,6 +345,15 @@ static void ed_keymap_gpencil_painting_tint(wmKeyConfig *keyconf)
keymap->poll = gpencil_stroke_paintmode_tint_poll;
}
+#if 0
+/* keys for draw with a curve brush */
+static void ed_keymap_gpencil_painting_curve(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Curve)", 0, 0);
+ keymap->poll = gpencil_stroke_paintmode_curve_poll;
+}
+#endif
+
/* Stroke Painting Keymap - Only when paintmode is enabled */
static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf)
{
@@ -482,13 +482,16 @@ 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);
ed_keymap_gpencil_painting_erase(keyconf);
ed_keymap_gpencil_painting_fill(keyconf);
ed_keymap_gpencil_painting_tint(keyconf);
+#if 0
+ ed_keymap_gpencil_painting_curve(keyconf);
+#endif
+ gpencil_curve_draw_modal_keymap(keyconf);
ed_keymap_gpencil_sculpting(keyconf);
ed_keymap_gpencil_sculptpainting_smooth(keyconf);
ed_keymap_gpencil_sculptpainting_thickness(keyconf);
@@ -580,10 +583,10 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_sculpt_paint);
WM_operatortype_append(GPENCIL_OT_weight_paint);
- /* Edit stroke editcurve */
+ /* Editcurve */
- WM_operatortype_append(GPENCIL_OT_stroke_enter_editcurve_mode);
WM_operatortype_append(GPENCIL_OT_stroke_editcurve_set_handle_type);
+ WM_operatortype_append(GPENCIL_OT_stroke_set_type);
/* Editing (Buttons) ------------ */
@@ -695,6 +698,8 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_primitive_circle);
WM_operatortype_append(GPENCIL_OT_primitive_curve);
+ WM_operatortype_append(GPENCIL_OT_draw_curve);
+
/* convert old 2.7 files to 2.8 */
WM_operatortype_append(GPENCIL_OT_convert_old_files);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 67d4b7726b5..fa867bcd4c2 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -54,6 +54,7 @@
#include "BKE_deform.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_main.h"
@@ -944,6 +945,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
const bool is_depth = (bool)(align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
const bool is_lock_axis_view = (bool)(ts->gp_sculpt.lock_axis == 0);
const bool is_camera = is_lock_axis_view && (rv3d->persp == RV3D_CAMOB) && (!is_depth);
+ const bool is_bezier_mode = ts->gpencil_flags & GP_TOOL_FLAG_BEZIER_MODE;
int totelem;
/* For very low pressure at the end, truncate stroke. */
@@ -951,7 +953,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
int last_i = gpd->runtime.sbuffer_used - 1;
while (last_i > 0) {
ptc = (tGPspoint *)gpd->runtime.sbuffer + last_i;
- if (ptc->pressure > 0.001f) {
+ if (ptc->pressure > 0.0f) {
break;
}
gpd->runtime.sbuffer_used = last_i - 1;
@@ -1197,8 +1199,21 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
}
}
+ /* Convert to bezier stroke when we are in bezier mode. */
+ if (is_bezier_mode) {
+ /* The refitting algorithm assumes that we have a bounding box calculated. */
+ BKE_gpencil_stroke_boundingbox_calc(gps);
+ BKE_gpencil_stroke_refit_curve(gps,
+ ts->gpencil_curve_fit_threshold,
+ ts->gpencil_curve_fit_corner_angle,
+ GP_GEO_UPDATE_CURVE_REFIT_ALL);
+ }
+
/* subdivide and smooth the stroke */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
+ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
+ (subdivide > 0)
+ /* XXX: For now, don't subdivide in bezier mode. */
+ && !(is_bezier_mode)) {
gpencil_subdivide_stroke(gpd, gps, subdivide);
}
@@ -1207,6 +1222,17 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
* without changing too much the original stroke. */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->draw_smoothfac > 0.0f)) {
+ if (is_bezier_mode) {
+ BKE_gpencil_editcurve_smooth(gps,
+ brush->gpencil_settings->draw_smoothfac,
+ 2,
+ brush->gpencil_settings->draw_smoothlvl,
+ false,
+ true,
+ true,
+ false,
+ true);
+ }
float reduce = 0.0f;
for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) {
for (i = 0; i < gps->totpoints - 1; i++) {
@@ -1230,15 +1256,24 @@ 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(gpd, gps, brush->gpencil_settings->simplify_f);
+ if (is_bezier_mode) {
+ BKE_gpencil_editcurve_simplify_adaptive(gps, brush->gpencil_settings->simplify_f);
+ }
+ else {
+ BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
+ }
+ }
+
+ if (!is_bezier_mode) {
+ /* reproject to plane (only in 3d space) */
+ gpencil_reproject_toplane(p, gps);
}
- /* reproject to plane (only in 3d space) */
- gpencil_reproject_toplane(p, gps);
/* change position relative to parent object */
gpencil_apply_parent(depsgraph, obact, gpl, gps);
/* If camera view or view projection, reproject flat to view to avoid perspective effect. */
- if ((!is_depth) && (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || is_camera)) {
+ if ((!is_bezier_mode) && (!is_depth) &&
+ (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || is_camera)) {
ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps);
}
@@ -1282,12 +1317,12 @@ 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) {
+ (p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) && (!is_bezier_mode)) {
BKE_gpencil_stroke_trim(gpd, gps);
}
/* Join with existing strokes. */
- if (ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) {
+ if ((ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) && (!is_bezier_mode)) {
if (gps->prev != NULL) {
int pt_index = 0;
bool doit = true;
@@ -1306,7 +1341,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* In Multiframe mode, duplicate the stroke in other frames. */
if (GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd)) {
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index a2b4e5dee64..d7d0cd2c05b 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1093,7 +1093,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* Update evaluated data. */
ED_gpencil_sbuffer_update_eval(tgpi->gpd, tgpi->ob_eval);
@@ -1351,7 +1351,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(tgpi->gpd, gps);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* transfer stroke from temporary buffer to the actual frame */
@@ -1394,7 +1394,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
}
ED_gpencil_stroke_close_by_distance(gps, 0.02f);
}
- BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* In Multiframe mode, duplicate the stroke in other frames. */
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index efd0f86df03..9ea4f176585 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -54,6 +54,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_gpencil_modifier.h"
#include "BKE_main.h"
@@ -96,6 +97,8 @@ typedef struct tGP_BrushEditData {
ScrArea *area;
ARegion *region;
+ ToolSettings *ts;
+
/* Current GPencil datablock */
bGPdata *gpd;
@@ -291,7 +294,7 @@ static void gpencil_recalc_geometry_tag(bGPDstroke *gps)
}
/* Recalc any stroke tagged. */
-static void gpencil_update_geometry(bGPdata *gpd)
+static void gpencil_update_geometry(bGPdata *gpd, ToolSettings *ts)
{
if (gpd == NULL) {
return;
@@ -305,7 +308,11 @@ static void gpencil_update_geometry(bGPdata *gpd)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_TAG) {
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_refit_curve(gps,
+ ts->gpencil_curve_fit_threshold,
+ ts->gpencil_curve_fit_corner_angle,
+ GP_GEO_UPDATE_CURVE_REFIT_ALL);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL);
gps->flag &= ~GP_STROKE_TAG;
}
}
@@ -1161,6 +1168,7 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op)
gso->bmain = CTX_data_main(C);
/* store state */
gso->settings = gpencil_sculpt_get_settings(scene);
+ gso->ts = ts;
/* Random generator, only init once. */
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
@@ -1304,7 +1312,7 @@ static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op)
gso->brush->gpencil_settings->sculpt_flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
/* Update geometry data for tagged strokes. */
- gpencil_update_geometry(gso->gpd);
+ gpencil_update_geometry(gso->gpd, gso->ts);
/* free operator data */
MEM_freeN(gso);
@@ -1449,6 +1457,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
bool include_last = false;
bool changed = false;
float rot_eval = 0.0f;
+ const bool is_curve = GPENCIL_STROKE_TYPE_BEZIER(gps_active);
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
@@ -1465,6 +1474,9 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* apply operation to this point */
if (pt_active != NULL) {
+ if (is_curve) {
+ pt_active->flag |= GP_SPOINT_TAG;
+ }
rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, 0);
changed = apply(gso, gps_active, rot_eval, 0, radius, pc1);
}
@@ -1508,7 +1520,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* To each point individually... */
pt = &gps->points[i];
- if ((pt->runtime.pt_orig == NULL) && (tool != GPSCULPT_TOOL_GRAB)) {
+ if (!is_curve && (pt->runtime.pt_orig == NULL) && (tool != GPSCULPT_TOOL_GRAB)) {
continue;
}
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
@@ -1517,8 +1529,14 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
continue;
}
+
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if ((pt_active != NULL) && (index < gps_active->totpoints)) {
+ if (is_curve) {
+ /* Tag points that will be transformed for curve update. */
+ pt_active->flag |= GP_SPOINT_TAG;
+ }
+
rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i);
ok = apply(gso, gps_active, rot_eval, index, radius, pc1);
}
@@ -1690,7 +1708,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(gpd, gps_active);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_active, GP_GEO_UPDATE_DEFAULT);
}
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 c33b43247fd..e4f397be20e 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -132,27 +132,6 @@ 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)
{
@@ -162,33 +141,32 @@ static void deselect_all_selected(bContext *C)
gpd->select_last_index = 0;
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;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
- /* deselect points */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ /* Deselect the curve points. */
+ for (uint32_t 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;
}
-
- /* deselect stroke itself too */
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
}
-
- /* 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);
+ else {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* Deselect the points. */
+ for (uint32_t i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
}
-
- gpc->flag &= ~GP_CURVE_SELECT;
}
+
+ BKE_gpencil_stroke_select_index_reset(gps);
}
CTX_DATA_END;
}
@@ -210,12 +188,10 @@ static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gp
if (deselect == false) {
gpc->flag |= GP_CURVE_SELECT;
- gps->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
- gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
}
}
@@ -244,7 +220,6 @@ 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");
@@ -264,12 +239,7 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
}
}
- if (is_curve_edit) {
- ED_gpencil_select_curve_toggle_all(C, action);
- }
- else {
- ED_gpencil_select_toggle_all(C, action);
- }
+ ED_gpencil_select_toggle_all(C, action);
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
@@ -308,7 +278,6 @@ 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");
@@ -320,9 +289,11 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (is_curve_edit) {
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
+ bool changed = false;
+ /* select all points in selected strokes */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -330,13 +301,10 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
gpc_pt->flag |= GP_CURVE_POINT_SELECT;
BEZT_SEL_ALL(bezt);
}
+ changed = true;
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
- }
- else {
- /* select all points in selected strokes */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ else {
if (gps->flag & GP_STROKE_SELECT) {
bGPDspoint *pt;
int i;
@@ -344,19 +312,24 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
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;
}
@@ -385,7 +358,6 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
{
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");
@@ -398,10 +370,10 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
}
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)) {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) && (gpc->tot_curve_points > 1)) {
int idx = 0;
int start = 0;
if (unselect_ends) {
@@ -435,11 +407,7 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
changed = true;
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
- }
- else {
- /* select all points in selected strokes */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ else {
if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
bGPDspoint *pt;
int row = 0;
@@ -471,8 +439,8 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
changed = true;
}
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -535,7 +503,6 @@ 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) {
@@ -549,49 +516,46 @@ static bool gpencil_select_same_layer(bContext *C)
/* Search for a selected stroke */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- if (gps->flag & GP_STROKE_SELECT) {
- found = true;
- break;
- }
+ if (!ED_gpencil_stroke_can_use(C, gps)) {
+ continue;
+ }
+
+ if (gps->flag & GP_STROKE_SELECT ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT)) {
+ found = true;
+ break;
}
}
/* Select all if found */
if (found) {
- 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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (!ED_gpencil_stroke_can_use(C, gps)) {
+ continue;
+ }
- changed = true;
+ if (GPENCIL_STROKE_TYPE_BEZIER(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;
}
- }
- 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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ else {
+ bGPDspoint *pt;
+ int i;
- changed = true;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
}
+
+ gps->flag |= GP_STROKE_SELECT;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
}
}
@@ -604,14 +568,12 @@ static bool gpencil_select_same_layer(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) {
+ if (gps->flag & GP_STROKE_SELECT ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT)) {
/* add instead of insert here, otherwise the uniqueness check gets skipped,
* and we get many duplicate entries...
*/
@@ -620,28 +582,22 @@ static bool gpencil_select_same_material(bContext *C)
}
CTX_DATA_END;
+ bool changed = false;
+
/* Second, select any visible stroke that uses these colors */
- 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)) {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
- changed = true;
+ gpc->flag |= GP_CURVE_SELECT;
}
- }
- CTX_DATA_END;
- }
- else {
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ else {
/* select this stroke */
bGPDspoint *pt;
int i;
@@ -651,13 +607,13 @@ static bool gpencil_select_same_material(bContext *C)
}
gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
-
- changed = true;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
/* Free memory. */
if (selected_colors != NULL) {
@@ -741,7 +697,6 @@ 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 has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
@@ -754,36 +709,33 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
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)) {
+ if (only_selected &&
+ !((gps->flag & GP_STROKE_SELECT) ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT))) {
continue;
}
/* select first point */
BLI_assert(gps->totpoints >= 1);
- 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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
- 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);
- }
+ gpc->curve_points[0].flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+
+ if ((extend == false) && (gpc->tot_curve_points > 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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -795,8 +747,10 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
pt->flag &= ~GP_SPOINT_SELECT;
}
}
- changed = true;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
CTX_DATA_END;
@@ -851,7 +805,6 @@ void GPENCIL_OT_select_first(wmOperatorType *ot)
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 has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
@@ -864,35 +817,33 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
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)) {
+ if (only_selected &&
+ !((gps->flag & GP_STROKE_SELECT) ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT))) {
continue;
}
/* select last point */
BLI_assert(gps->totpoints >= 1);
- 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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
- 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);
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ 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;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ 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;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -904,9 +855,10 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
pt->flag &= ~GP_SPOINT_SELECT;
}
}
-
- changed = true;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
CTX_DATA_END;
@@ -961,106 +913,98 @@ 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 has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
return OPERATOR_CANCELLED;
}
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;
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ bGPDcurve *gpc = gps->editcurve;
+
+ bool prev_sel = 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 (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;
}
+ }
- 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;
+ for (int i = gpc->tot_curve_points - 1; i >= 0; i--) {
+ bGPDcurve_point *gpc_pt = &gpc->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;
}
}
}
- 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;
+ else 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;
- }
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- pt->flag |= GP_SPOINT_SELECT;
- changed = true;
- }
- prev_sel = false;
+ /* 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;
}
+ }
- /* 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;
+ /* 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;
}
}
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -1100,7 +1044,6 @@ 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 has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
@@ -1108,105 +1051,115 @@ static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op))
}
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;
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ bGPDcurve *gpc = gps->editcurve;
+ int tot_selected = 0, num_deselected = 0;
- 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;
+ bool prev_sel = false;
+ int i;
+ for (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) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ changed = true;
+ num_deselected++;
}
+ prev_sel = true;
+ tot_selected++;
+ }
+ 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;
+ /* 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 = gpc->tot_curve_points - 1; i > 0; i--) {
+ bGPDcurve_point *gpc_pt = &gpc->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;
+ num_deselected++;
}
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
}
+
+ /* Deselect curve if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
}
- 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;
+ else if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i, tot_selected = 0, num_deselected = 0;
+ bool prev_sel;
- /* 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;
+ /* 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;
+ num_deselected++;
}
+ prev_sel = true;
+ tot_selected++;
+ }
+ 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;
+ /* 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;
+ num_deselected++;
}
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
}
+
+ /* Deselect curve if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -1261,8 +1214,7 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
rcti *rect,
const float diff_mat[4][4],
const int selectmode,
- const float scale,
- const bool is_curve_edit)
+ const float scale)
{
bGPDspoint *pt = NULL;
int x0 = 0, y0 = 0;
@@ -1328,23 +1280,13 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *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(gpd, 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(gpd, gps_active);
return changed;
}
-static bool gpencil_do_curve_circle_sel(bContext *C,
+static bool gpencil_curve_do_circle_sel(bContext *C,
bGPDstroke *gps,
bGPDcurve *gpc,
const int mx,
@@ -1366,6 +1308,9 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
+ bGPDcurve_point *gpc_active_pt = (gpc_pt->runtime.gpc_pt_orig) ? gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_active_pt->bezt;
if (bezt->hide == 1) {
continue;
@@ -1403,13 +1348,13 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
hit = true;
/* change selection */
if (select) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_IDX(bezt, j);
+ gpc_active_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(bezt_active, j);
}
else {
- BEZT_DESEL_IDX(bezt, j);
- if (!BEZT_ISSEL_ANY(bezt)) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt_active, j);
+ if (!BEZT_ISSEL_ANY(bezt_active)) {
+ gpc_active_pt->flag &= ~GP_CURVE_POINT_SELECT;
}
}
}
@@ -1420,20 +1365,31 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
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;
+ bGPDcurve_point *gpc_active_pt = (gpc_pt->runtime.gpc_pt_orig) ?
+ gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_active_pt->bezt;
if (select) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_ALL(bezt);
+ gpc_active_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt_active);
}
else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
+ gpc_active_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt_active);
}
}
- }
- BKE_gpencil_curve_sync_selection(gpd, gps);
+ if (select) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ }
+ else {
+ BKE_gpencil_curve_sync_selection(gpd, gps);
+ }
return hit;
}
@@ -1443,7 +1399,6 @@ 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) {
@@ -1487,32 +1442,24 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
rect.xmax = mx + radius;
rect.ymax = my + radius;
- if (is_curve_edit) {
- if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_gpencil_select_curve_toggle_all(C, SEL_DESELECT);
- changed = true;
- }
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
- 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 (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ // ED_gpencil_select_curve_toggle_all(C, SEL_DESELECT);
+ ED_gpencil_select_toggle_all(C, SEL_DESELECT);
+ changed = true;
}
- 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) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ changed |= gpencil_curve_do_circle_sel(
+ C, gps, gpc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode);
}
-
- /* find visible strokes, and select if hit */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ else {
changed |= gpencil_stroke_do_circle_sel(gpd,
gpl,
gps,
@@ -1524,11 +1471,10 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
&rect,
gpstroke_iter.diff_mat,
selectmode,
- scale,
- is_curve_edit);
+ scale);
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* updates */
if (changed) {
@@ -1604,7 +1550,7 @@ static bool gpencil_stroke_fill_isect_rect(ARegion *region,
int *pt2d = points2d[i];
int screen_co[2];
- gpencil_3d_point_to_screen_space(region, diff_mat, &pt->x, screen_co);
+ ED_gpencil_3d_point_to_screen_space(region, NULL, diff_mat, &pt->x, screen_co);
DO_MINMAX2(screen_co, min, max);
copy_v2_v2_int(pt2d, screen_co);
@@ -1654,278 +1600,251 @@ static bool gpencil_stroke_fill_isect_rect(ARegion *region,
static bool gpencil_generic_curve_select(bContext *C,
Object *ob,
+ bGPdata *gpd,
+ bGPDstroke *gps,
+ bGPDcurve *gpc,
+ struct GP_EditableStrokes_Iter *gpstroke_iter,
+ GP_SpaceConversion *gsc,
GPencilTestFn is_inside_fn,
- rcti UNUSED(box),
+ rcti box,
GP_SelectUserData *user_data,
const bool strokemode,
const eSelectOp sel_op)
{
- ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
- bGPdata *gpd = ob->data;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+
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;
+ 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;
- 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;
+ bGPDcurve_point *gpc_pt_active = (gpc_pt->runtime.gpc_pt_orig) ? gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_pt_active->bezt;
- if (bezt->hide == 1) {
- continue;
- }
+ if (bezt_active->hide == 1) {
+ continue;
+ }
- const bool handles_visible = (handle_all || (handle_only_selected &&
- (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
+ 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 (handles_visible) {
+ for (int j = 0; j < 3; j++) {
+ const bool is_select = BEZT_ISSEL_IDX(bezt_active, j);
+ bool is_inside = is_inside_fn(
+ gsc->region, gpstroke_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->f2 |= SELECT;
+ gpc_pt_active->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(bezt_active, j);
any_select = true;
}
else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- bezt->f2 &= ~SELECT;
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt_active, j);
}
changed = true;
hit = true;
}
else {
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- bezt->f2 &= ~SELECT;
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt_active, j);
}
}
}
}
}
-
- /* 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;
+ /* if the handles are not visible only check ctrl point (vec[1])*/
+ else {
+ const bool is_select = bezt_active->f2;
+ bool is_inside = is_inside_fn(gsc->region, gpstroke_iter->diff_mat, bezt->vec[1], user_data);
+ if (strokemode) {
+ if (is_inside) {
+ hit = true;
+ any_select = true;
+ }
}
-
- 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;
-
+ 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_ALL(bezt);
+ gpc_pt_active->flag |= GP_CURVE_POINT_SELECT;
+ bezt_active->f2 |= SELECT;
+ any_select = true;
}
else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ bezt_active->f2 &= ~SELECT;
+ }
+ changed = true;
+ hit = true;
+ }
+ else {
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ bezt_active->f2 &= ~SELECT;
}
}
+ }
+ }
+ }
+
+ if (!hit) {
+ /* check if we selected the inside of a filled curve */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps_active->mat_nr + 1);
+ if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
+ return changed;
+ }
+ int mval[2];
+ mval[0] = (box.xmax + box.xmin) / 2;
+ mval[1] = (box.ymax + box.ymin) / 2;
+
+ whole = ED_gpencil_stroke_point_is_inside(gps_active, gsc, mval, gpstroke_iter->diff_mat);
+ }
+
+ /* 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];
+ bGPDcurve_point *gpc_pt_active = (gpc_pt->runtime.gpc_pt_orig) ?
+ gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_pt_active->bezt;
if (sel_op_result) {
- gpc->flag |= GP_CURVE_SELECT;
+ gpc_pt_active->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt_active);
}
else {
- gpc->flag &= ~GP_CURVE_SELECT;
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt_active);
}
- changed = true;
}
- }
- BKE_gpencil_curve_sync_selection(gpd, gps);
+ if (sel_op_result) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ changed = true;
+ }
+ }
+ else {
+ BKE_gpencil_curve_sync_selection(gpd, gps_active);
}
- GP_EDITABLE_CURVES_END(gps_iter);
return changed;
}
-static bool gpencil_generic_stroke_select(bContext *C,
- Object *ob,
+static bool gpencil_generic_stroke_select(Object *ob,
bGPdata *gpd,
+ bGPDlayer *gpl,
+ bGPDstroke *gps,
+ struct GP_EditableStrokes_Iter *gpstroke_iter,
+ GP_SpaceConversion *gsc,
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)
+ const float scale)
{
- GP_SpaceConversion gsc = {NULL};
bool changed = false;
- /* init space conversion stuff */
- gpencil_point_conversion_init(C, &gsc);
-
- /* deselect all strokes first? */
- if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
- /* Set selection index to 0. */
- gpd->select_last_index = 0;
-
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- 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;
- BKE_gpencil_stroke_select_index_reset(gps);
- }
- CTX_DATA_END;
-
- changed = true;
- }
-
- /* select/deselect points */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- bool whole = false;
+ bool whole = false;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- bGPDspoint *pt;
- int i;
- bool hit = false;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ bGPDspoint *pt;
+ int i;
+ bool hit = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
- /* convert point coords to screenspace */
- 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);
- if (sel_op_result != -1) {
- SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT);
- changed = true;
- hit = true;
+ /* convert point coords to screenspace */
+ 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);
+ if (sel_op_result != -1) {
+ SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT);
+ changed = true;
+ hit = true;
- /* Expand selection to segment. */
- if (segmentmode) {
- bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
- float r_hita[3], r_hitb[3];
- ED_gpencil_select_stroke_segment(
- gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
- }
- }
- }
- else {
- if (is_inside) {
- hit = true;
- break;
+ /* Expand selection to segment. */
+ if (segmentmode) {
+ bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
+ float r_hita[3], r_hitb[3];
+ ED_gpencil_select_stroke_segment(
+ gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
}
}
}
-
- /* If nothing hit, check if the mouse is inside a filled stroke using the center or
- * Box or lasso area. */
- if (!hit) {
- /* Only check filled strokes. */
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
- continue;
+ else {
+ if (is_inside) {
+ hit = true;
+ break;
}
- int mval[2];
- mval[0] = (box.xmax + box.xmin) / 2;
- mval[1] = (box.ymax + box.ymin) / 2;
+ }
+ }
- whole = ED_gpencil_stroke_point_is_inside(gps, &gsc, mval, gpstroke_iter.diff_mat);
+ /* If nothing hit, check if the mouse is inside a filled stroke using the center or
+ * Box or lasso area. */
+ if (!hit) {
+ /* Only check filled strokes. */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps_active->mat_nr + 1);
+ if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
+ return changed;
}
+ int mval[2];
+ mval[0] = (box.xmax + box.xmin) / 2;
+ mval[1] = (box.ymax + box.ymin) / 2;
- /* if stroke mode expand selection. */
- if ((strokemode) || (whole)) {
- const bool is_select = BKE_gpencil_stroke_select_check(gps_active) || whole;
- const bool is_inside = hit || whole;
- const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
- if (sel_op_result != -1) {
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ whole = ED_gpencil_stroke_point_is_inside(gps_active, gsc, mval, gpstroke_iter->diff_mat);
+ }
- if (sel_op_result) {
- pt_active->flag |= GP_SPOINT_SELECT;
- }
- else {
- pt_active->flag &= ~GP_SPOINT_SELECT;
- }
+ /* if stroke mode expand selection. */
+ if ((strokemode) || (whole)) {
+ const bool is_select = BKE_gpencil_stroke_select_check(gps_active) || whole;
+ const bool is_inside = hit || whole;
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
+ if (sel_op_result != -1) {
+ for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) {
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+
+ if (sel_op_result) {
+ pt_active->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt_active->flag &= ~GP_SPOINT_SELECT;
}
- changed = true;
}
- }
-
- /* 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(gpd, 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(gpd, gps_active);
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
+
+ /* Ensure that stroke selection is in sync with its points */
+ BKE_gpencil_stroke_sync_selection(gpd, gps_active);
return changed;
}
@@ -1940,7 +1859,6 @@ static int gpencil_generic_select_exec(bContext *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) {
@@ -1961,38 +1879,69 @@ static int gpencil_generic_select_exec(bContext *C,
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);
+ bool changed = false;
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
+
+ /* deselect all strokes first? */
+ if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
+ /* Set selection index to 0. */
+ gpd->select_last_index = 0;
+
+ deselect_all_selected(C);
+ changed = true;
}
- 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);
+ GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpencil_generic_curve_select(C,
+ ob,
+ gpd,
+ gps,
+ gpc,
+ &gpstroke_iter,
+ &gsc,
+ is_inside_fn,
+ box,
+ user_data,
+ strokemode,
+ sel_op)) {
+ changed = true;
+ }
+ }
+ else {
+ if (gpencil_generic_stroke_select(ob,
+ gpd,
+ gpl,
+ gps,
+ &gpstroke_iter,
+ &gsc,
+ is_inside_fn,
+ box,
+ user_data,
+ strokemode,
+ segmentmode,
+ sel_op,
+ scale)) {
+ changed = true;
+ }
+ }
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* if paint mode,delete selected points */
if (GPENCIL_PAINT_MODE(gpd)) {
gpencil_delete_selected_point_wrap(C);
- changed = true;
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ changed = true;
}
/* updates */
@@ -2020,7 +1969,7 @@ static bool gpencil_test_box(ARegion *region,
GP_SelectUserData *user_data)
{
int co[2] = {0};
- if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
+ if (ED_gpencil_3d_point_to_screen_space(region, NULL, diff_mat, pt, co)) {
return BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]);
}
return false;
@@ -2069,7 +2018,7 @@ static bool gpencil_test_lasso(ARegion *region,
GP_SelectUserData *user_data)
{
int co[2] = {0};
- if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
+ if (ED_gpencil_3d_point_to_screen_space(region, NULL, 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(
@@ -2125,56 +2074,93 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot)
/** \name Mouse Pick Select Operator
* \{ */
-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)
+static bool gpencil_select_curve_point_closest(bContext *C,
+ bGPDcurve *gpc,
+ struct GP_EditableStrokes_Iter *gps_iter,
+ const int mval[2],
+ const int radius_squared,
+ bGPDcurve_point **r_pt,
+ int *handle_idx,
+ int *hit_distance)
{
+ bool hit = false;
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
- int hit_distance = radius_squared;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
- 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;
+ bGPDcurve_point *gpc_pt_active = (gpc_pt->runtime.gpc_pt_orig) ? gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_pt_active->bezt;
- if (bezt->hide == 1) {
- continue;
+ if (bezt_active->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++) {
+ int screen_co[2];
+ if (ED_gpencil_3d_point_to_screen_space(
+ region, NULL, 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_pt = gpc_pt_active;
+ *handle_idx = j;
+ *hit_distance = pt_distance;
+ hit = true;
+ }
}
+ }
+ }
- const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
- (!only_selected || BEZT_ISSEL_ANY(bezt));
+ return hit;
+}
+
+static bool gpencil_select_stroke_point_closest(bGPDstroke *gps,
+ struct GP_EditableStrokes_Iter *gps_iter,
+ GP_SpaceConversion *gsc,
+ const int mval[2],
+ const int radius_squared,
+ bGPDspoint **r_pt,
+ int *hit_distance)
+{
+ bool hit = false;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- /* If the handles are not visible only check control point (vec[1]). */
- int from = (!handles_visible) ? 1 : 0;
- int to = (!handles_visible) ? 2 : 3;
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ int xy[2];
- 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);
+ bGPDspoint pt2;
+ gpencil_point_to_parent_space(pt, gps_iter->diff_mat, &pt2);
+ gpencil_point_to_xy(gsc, gps_active, &pt2, &xy[0], &xy[1]);
- 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;
- }
- }
+ /* 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 && pt_distance < *hit_distance) {
+ /* only use this point if it is a better match than the current hit - T44685 */
+ *r_pt = pt_active;
+ *hit_distance = pt_distance;
+ hit = true;
}
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
+
+ return hit;
}
static int gpencil_select_exec(bContext *C, wmOperator *op)
@@ -2184,7 +2170,6 @@ 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;
@@ -2201,14 +2186,12 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* 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_curve_handle_idx = 0;
int hit_distance = radius_squared;
/* sanity checks */
@@ -2230,62 +2213,45 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE);
}
- if (is_curve_edit) {
- gpencil_select_curve_point(C,
- mval,
- radius_squared,
- &hit_layer,
- &hit_stroke,
- &hit_curve,
- &hit_curve_point,
- &hit_curve_handle);
- }
-
- 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;
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
- /* 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_active, &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;
- }
- }
- }
+ /* First Pass: Find 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;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpencil_select_curve_point_closest(C,
+ gpc,
+ &gpstroke_iter,
+ mval,
+ radius_squared,
+ &hit_curve_point,
+ &hit_curve_handle_idx,
+ &hit_distance)) {
+ hit_layer = gpl;
+ hit_stroke = gps_active;
+ hit_point = &gps_active->points[hit_curve_point->point_index];
+ hit_curve = gps_active->editcurve;
+ }
+ }
+ else {
+ if (gpencil_select_stroke_point_closest(
+ gps, &gpstroke_iter, &gsc, mval, radius_squared, &hit_point, &hit_distance)) {
+ hit_layer = gpl;
+ hit_stroke = gps_active;
}
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* Abort if nothing hit... */
- if (!hit_curve && !hit_curve_point && !hit_point && !hit_stroke) {
-
+ if ((hit_curve == NULL && hit_curve_point == NULL) &&
+ (hit_point == NULL && hit_stroke == NULL)) {
if (deselect_all) {
/* since left mouse select change, deselect all if click outside any hit */
deselect_all_selected(C);
-
/* copy on write tag is needed, or else no refresh happens */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
@@ -2298,23 +2264,17 @@ 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) {
if (hit_curve_point != NULL) {
BezTriple *bezt = &hit_curve_point->bezt;
- if ((bezt->f1 & SELECT) && (hit_curve_handle == 0)) {
+ if ((bezt->f1 & SELECT) && (hit_curve_handle_idx == 0)) {
deselect = true;
}
- if ((bezt->f2 & SELECT) && (hit_curve_handle == 1)) {
+ if ((bezt->f2 & SELECT) && (hit_curve_handle_idx == 1)) {
deselect = true;
}
- if ((bezt->f3 & SELECT) && (hit_curve_handle == 2)) {
+ if ((bezt->f3 & SELECT) && (hit_curve_handle_idx == 2)) {
deselect = true;
}
}
@@ -2330,13 +2290,6 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* Perform selection operations... */
if (whole) {
- /* 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(gpd, hit_stroke, hit_curve, deselect);
@@ -2371,9 +2324,8 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
if (deselect == false) {
if (hit_curve_point != NULL) {
hit_curve_point->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
+ BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle_idx);
hit_curve->flag |= GP_CURVE_SELECT;
- hit_stroke->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
}
else {
@@ -2404,7 +2356,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
else {
if (hit_curve_point != NULL) {
- BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
+ BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle_idx);
if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
}
@@ -2487,8 +2439,12 @@ void GPENCIL_OT_select(wmOperatorType *ot)
prop = RNA_def_boolean(ot->srna, "use_shift_extend", false, "Extend", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select by Vertex Color
+ * \{ */
-/* Select by Vertex Color. */
/* Helper to create a hash of colors. */
static void gpencil_selected_hue_table(bContext *C,
Object *ob,
@@ -2508,22 +2464,43 @@ static void gpencil_selected_hue_table(bContext *C,
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
- if ((gps->flag & GP_STROKE_SELECT) == 0) {
- continue;
- }
/* Read all points to get all colors selected. */
- bGPDspoint *pt;
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (((pt->flag & GP_SPOINT_SELECT) == 0) || (pt->vert_color[3] == 0.0f)) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
+ }
+
+ 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) == 0) || (gpc_pt->vert_color[3] == 0.0f)) {
+ continue;
+ }
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(gpc_pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
+ if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+ else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
continue;
}
- /* Round Hue value. */
- rgb_to_hsv_compat_v(pt->vert_color, hsv);
- uint key = truncf(hsv[0] * range);
- if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
- BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (((pt->flag & GP_SPOINT_SELECT) == 0) || (pt->vert_color[3] == 0.0f)) {
+ continue;
+ }
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
+ if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
}
}
}
@@ -2577,39 +2554,77 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ deselect_all_selected(C);
+
/* Select any visible stroke that uses any of these colors. */
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- bGPDspoint *pt;
- int i;
- bool gps_selected = false;
- /* Check all stroke points. */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->vert_color[3] == 0.0f) {
- continue;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ bool gpc_selected = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ if (gpc_pt->vert_color[3] == 0.0f) {
+ continue;
+ }
+
+ /* Only check Hue to get value and saturation full ranges. */
+ float hsv[3];
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(gpc_pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
+
+ if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
+ gpc_selected = true;
+ changed = true;
+ }
}
- /* Only check Hue to get value and saturation full ranges. */
- float hsv[3];
- /* Round Hue value. */
- rgb_to_hsv_compat_v(pt->vert_color, hsv);
- uint key = truncf(hsv[0] * range);
+ if (gpc_selected) {
+ gpc->flag |= GP_CURVE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
- if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
- pt->flag |= GP_SPOINT_SELECT;
- gps_selected = true;
+ /* Extend selection. */
+ if (selectmode == GP_SELECTMODE_STROKE) {
+ select_all_curve_points(gpd, gps, gpc, false);
+ }
}
}
+ else {
+ bGPDspoint *pt;
+ int i;
+ bool gps_selected = false;
+ /* Check all stroke points. */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->vert_color[3] == 0.0f) {
+ continue;
+ }
- if (gps_selected) {
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ /* Only check Hue to get value and saturation full ranges. */
+ float hsv[3];
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
- /* Extend stroke selection. */
- if (selectmode == GP_SELECTMODE_STROKE) {
- bGPDspoint *pt1 = NULL;
+ if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ pt->flag |= GP_SPOINT_SELECT;
+ gps_selected = true;
+ changed = true;
+ }
+ }
- for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
- pt1->flag |= GP_SPOINT_SELECT;
+ if (gps_selected) {
+ gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+
+ /* Extend stroke selection. */
+ if (selectmode == GP_SELECTMODE_STROKE) {
+ bGPDspoint *pt1 = NULL;
+
+ for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
+ pt1->flag |= GP_SPOINT_SELECT;
+ }
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index 970afc3ff6b..35d1c69d709 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -365,7 +365,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
BKE_gpencil_stroke_sample(gpd, gps, sample, false);
}
else {
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
else {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index e6488cfe454..f9659a4061b 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -713,9 +713,6 @@ void gpencil_point_to_parent_space(const bGPDspoint *pt,
*/
void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps)
{
- bGPDspoint *pt;
- int i;
-
/* undo matrix */
float diff_mat[4][4];
float inverse_diff_mat[4][4];
@@ -724,8 +721,20 @@ void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, b
BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
- for (i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ for (int j = 0; j < 3; j++) {
+ mul_v3_m4v3(fpt, inverse_diff_mat, bezt->vec[j]);
+ copy_v3_v3(bezt->vec[j], fpt);
+ }
+ }
+ }
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
copy_v3_v3(&pt->x, fpt);
}
@@ -1497,7 +1506,7 @@ void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, const int subdivide
MEM_SAFE_FREE(temp_points);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* Reset parent matrix for all layers. */
@@ -1622,7 +1631,21 @@ void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight)
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ BKE_gpencil_dvert_ensure(gps);
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
+ if (dw != NULL) {
+ dw->weight = weight;
+ }
+ }
+ }
+ }
+ else if (gps->flag & GP_STROKE_SELECT) {
/* verify the weight array is created */
BKE_gpencil_dvert_ensure(gps);
@@ -1676,17 +1699,36 @@ void ED_gpencil_vgroup_remove(bContext *C, Object *ob)
continue;
}
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->dvert == NULL) {
+ continue;
+ }
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) && (dvert->totweight > 0)) {
+ MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
+ if (dw != NULL) {
+ BKE_defvert_remove_group(dvert, dw);
+ }
+ }
+ }
+ }
+ else if ((gps->flag & GP_STROKE_SELECT)) {
if (gps->dvert == NULL) {
continue;
}
- MDeformVert *dvert = &gps->dvert[i];
- if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) {
- MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
- if (dw != NULL) {
- BKE_defvert_remove_group(dvert, dw);
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ MDeformVert *dvert = &gps->dvert[i];
+ if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) {
+ MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
+ if (dw != NULL) {
+ BKE_defvert_remove_group(dvert, dw);
+ }
}
}
}
@@ -1729,20 +1771,39 @@ void ED_gpencil_vgroup_select(bContext *C, Object *ob)
continue;
}
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- if (gps->dvert == NULL) {
+ bool selected = false;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->dvert == NULL) {
continue;
}
- MDeformVert *dvert = &gps->dvert[i];
- if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
- pt->flag |= GP_SPOINT_SELECT;
- gps->flag |= GP_STROKE_SELECT;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ cpt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&cpt->bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+ selected = true;
+ }
+ }
+ }
+ else if (gps->dvert != NULL) {
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ MDeformVert *dvert = &gps->dvert[i];
+
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ pt->flag |= GP_SPOINT_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ selected = true;
+ }
}
}
- if (gps->flag & GP_STROKE_SELECT) {
+ if (selected) {
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
}
@@ -1783,17 +1844,57 @@ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
+ bool deselected = false;
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- if (gps->dvert == NULL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->dvert == NULL) {
continue;
}
- MDeformVert *dvert = &gps->dvert[i];
+ int desel_count = 0;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ cpt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&cpt->bezt);
+ desel_count++;
+ }
- if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ desel_count++;
+ }
+ }
+
+ if (desel_count == gpc->tot_curve_points) {
+ deselected = true;
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ }
+ else if (gps->dvert != NULL) {
+ int desel_count = 0;
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ MDeformVert *dvert = &gps->dvert[i];
+
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ desel_count++;
+ }
+
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ desel_count++;
+ }
}
+
+ if (desel_count == gps->totpoints) {
+ deselected = true;
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ }
+
+ if (deselected) {
+ BKE_gpencil_stroke_select_index_reset(gps);
}
}
}
@@ -2396,7 +2497,7 @@ static void gpencil_insert_point(bGPdata *gpd,
i2++;
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
MEM_SAFE_FREE(temp_points);
}
@@ -2628,9 +2729,18 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
action = SEL_SELECT;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- action = SEL_DESELECT;
- break; /* XXX: this only gets out of the inner loop. */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ action = SEL_DESELECT;
+ break;
+ }
+ }
+ else {
+ if (gps->flag & GP_STROKE_SELECT) {
+ action = SEL_DESELECT;
+ break; /* XXX: this only gets out of the inner loop. */
+ }
}
}
CTX_DATA_END;
@@ -2654,18 +2764,25 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
/* deselect all strokes on all frames */
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- bGPDstroke *gps;
-
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- bGPDspoint *pt;
- int i;
-
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
/* only edit strokes that are valid in this view... */
- if (ED_gpencil_stroke_can_use(C, gps)) {
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (!ED_gpencil_stroke_can_use(C, gps))
+ continue;
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&pt->bezt);
+ }
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ else {
+ for (uint32_t i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_SELECT;
}
-
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
}
@@ -2677,39 +2794,68 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
else {
/* select or deselect all strokes */
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- bGPDspoint *pt;
- int i;
bool selected = false;
- /* Change selection status of all points, then make the stroke match */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- switch (action) {
- case SEL_SELECT:
- pt->flag |= GP_SPOINT_SELECT;
- break;
-#if 0
- case SEL_DESELECT:
- pt->flag &= ~GP_SPOINT_SELECT;
- break;
-#endif
- case SEL_INVERT:
- pt->flag ^= GP_SPOINT_SELECT;
- break;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+
+ for (uint32_t 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 (pt->flag & GP_SPOINT_SELECT) {
- selected = true;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ selected = true;
+ }
}
- }
- /* Change status of stroke */
- if (selected) {
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ if (selected) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
}
else {
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ 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++) {
+ switch (action) {
+ case SEL_SELECT:
+ pt->flag |= GP_SPOINT_SELECT;
+ break;
+ case SEL_INVERT:
+ pt->flag ^= GP_SPOINT_SELECT;
+ break;
+ }
+
+ if (pt->flag & GP_SPOINT_SELECT) {
+ selected = true;
+ }
+ }
+
+ /* Change status of stroke */
+ if (selected) {
+ gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ }
+ else {
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
}
}
CTX_DATA_END;
@@ -2758,9 +2904,7 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
/* 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);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
bGPDcurve *gpc = gps->editcurve;
@@ -3388,3 +3532,34 @@ void ED_gpencil_stroke_close_by_distance(bGPDstroke *gps, const float threshold)
BKE_gpencil_stroke_close(gps);
}
}
+
+/* Convert 3D point to 2D point in screen space. */
+bool ED_gpencil_3d_point_to_screen_space(struct ARegion *region,
+ const struct rcti *rect,
+ 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])) {
+ if (rect == NULL) {
+ copy_v2_v2_int(r_co, screen_co);
+ return true;
+ }
+ else {
+ if (BLI_rcti_isect_pt(rect, 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;
+}
diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c
index 6bd0540a9d4..64182115b20 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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
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(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index 402bccce2f7..c68d81f8a6b 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -59,7 +59,7 @@ static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[] = {
};
/* Helper: Check if any stroke is selected. */
-static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const bool is_curve_edit)
+static bool is_any_stroke_selected(bContext *C, const bool is_multiedit)
{
bool is_selected = false;
@@ -82,10 +82,7 @@ static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const b
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
is_selected = true;
@@ -136,7 +133,7 @@ static int gpencil_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float gain, offset;
{
@@ -254,7 +251,7 @@ static int gpencil_vertexpaint_hsv_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float hue = RNA_float_get(op->ptr, "h");
float sat = RNA_float_get(op->ptr, "s");
float val = RNA_float_get(op->ptr, "v");
@@ -373,7 +370,7 @@ static int gpencil_vertexpaint_invert_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -463,7 +460,7 @@ static int gpencil_vertexpaint_levels_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float gain = RNA_float_get(op->ptr, "gain");
float offset = RNA_float_get(op->ptr, "offset");
@@ -563,7 +560,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float factor = RNA_float_get(op->ptr, "factor");
bool changed = false;
@@ -1060,6 +1057,14 @@ static void gpencil_reset_vertex(bGPDstroke *gps, eGp_Vertex_Mode mode)
}
if (mode != GPPAINT_MODE_FILL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ zero_v4(gpc_pt->vert_color);
+ }
+ }
+
bGPDspoint *pt;
for (int i = 0; i < gps->totpoints; i++) {
pt = &gps->points[i];
@@ -1072,12 +1077,11 @@ static int gpencil_stroke_reset_vertex_color_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);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
/* First need to check if there are something selected. If not, apply to all strokes. */
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, is_curve_edit);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
/* Reset Vertex colors. */
bool changed = false;
@@ -1096,10 +1100,7 @@ static int gpencil_stroke_reset_vertex_color_exec(bContext *C, wmOperator *op)
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if ((!any_selected) || (gpc->flag & GP_CURVE_SELECT)) {
gpencil_reset_vertex(gps, mode);
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index 16605b6c634..7694dc1ce91 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -37,6 +37,7 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_material.h"
#include "BKE_report.h"
@@ -435,20 +436,40 @@ static bool brush_tint_apply(tGP_BrushVertexpaintData *gso,
/* Apply color to Stroke point. */
if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush) && (pt_index > -1)) {
- bGPDspoint *pt = &gps->points[pt_index];
- if (brush_invert_check(gso)) {
- pt->vert_color[3] -= inf;
- CLAMP_MIN(pt->vert_color[3], 0.0f);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve_point *pt = &gps->editcurve->curve_points[pt_index];
+ if (brush_invert_check(gso)) {
+ pt->vert_color[3] -= inf;
+ CLAMP_MIN(pt->vert_color[3], 0.0f);
+ }
+ else {
+ /* Premult. */
+ mul_v3_fl(pt->vert_color, pt->vert_color[3]);
+ /* "Alpha over" blending. */
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, gso->linear_color, inf);
+ pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
+ /* Un-premult. */
+ if (pt->vert_color[3] > 0.0f) {
+ mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ }
+ }
}
else {
- /* Premult. */
- mul_v3_fl(pt->vert_color, pt->vert_color[3]);
- /* "Alpha over" blending. */
- interp_v3_v3v3(pt->vert_color, pt->vert_color, gso->linear_color, inf);
- pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
- /* Un-premult. */
- if (pt->vert_color[3] > 0.0f) {
- mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ bGPDspoint *pt = &gps->points[pt_index];
+ if (brush_invert_check(gso)) {
+ pt->vert_color[3] -= inf;
+ CLAMP_MIN(pt->vert_color[3], 0.0f);
+ }
+ else {
+ /* Premult. */
+ mul_v3_fl(pt->vert_color, pt->vert_color[3]);
+ /* "Alpha over" blending. */
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, gso->linear_color, inf);
+ pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
+ /* Un-premult. */
+ if (pt->vert_color[3] > 0.0f) {
+ mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ }
}
}
}
@@ -820,9 +841,71 @@ static void gpencil_save_selected_point(tGP_BrushVertexpaintData *gso,
gso->pbuffer_used++;
}
+static void gpencil_vertexpaint_select_curve(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ const char tool,
+ const float diff_mat[4][4],
+ const float bound_mat[4][4])
+{
+ ARegion *region = gso->region;
+ GP_SpaceConversion *gsc = &gso->gsc;
+ rcti *rect = &gso->brush_rect;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDcurve *gpc = gps->editcurve;
+
+ /* Check stroke masking. */
+ if (GPENCIL_ANY_VERTEX_MASK(gso->mask)) {
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ return;
+ }
+ }
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
+ return;
+ }
+
+ if (gpc->tot_curve_points == 1) {
+ return;
+ }
+
+ /* If the curve has more than one control point... */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+
+ int screen_co[2];
+ /* Test if points can be projected. */
+ if (!ED_gpencil_3d_point_to_screen_space(region, rect, diff_mat, bezt->vec[1], screen_co)) {
+ continue;
+ }
+
+ float co[2] = {(float)screen_co[0], (float)screen_co[1]};
+ /* Test if the point is in the circle. */
+ if (len_v2v2(gso->mval, co) > radius) {
+ continue;
+ }
+
+ bGPDcurve_point *cpt_active = NULL;
+ int index = -1;
+ if (cpt->runtime.gpc_pt_orig) {
+ cpt_active = cpt->runtime.gpc_pt_orig;
+ index = cpt->runtime.idx_orig;
+ }
+ else {
+ cpt_active = cpt;
+ index = i;
+ }
+ gpencil_save_selected_point(gso, gps_active, index, screen_co);
+ }
+}
+
/* 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,
+static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
bGPDstroke *gps,
const char tool,
const float diff_mat[4][4],
@@ -849,13 +932,13 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* Check stroke masking. */
if (GPENCIL_ANY_VERTEX_MASK(gso->mask)) {
if ((gps->flag & GP_STROKE_SELECT) == 0) {
- return false;
+ return;
}
}
/* Check if the stroke collide with brush. */
if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
- return false;
+ return;
}
if (gps->totpoints == 1) {
@@ -990,8 +1073,6 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
}
}
}
-
- return saved;
}
/* Apply vertex paint brushes to strokes in the given frame. */
@@ -1027,12 +1108,11 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
}
/* Check points below the brush. */
- bool hit = gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat, bound_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;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ gpencil_vertexpaint_select_curve(gso, gps, tool, diff_mat, bound_mat);
+ }
+ else {
+ gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat, bound_mat);
}
}
@@ -1106,6 +1186,10 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
printf("ERROR: Unknown type of GPencil Vertex Paint brush\n");
break;
}
+
+ if (changed && GPENCIL_STROKE_TYPE_BEZIER(selected->gps)) {
+ BKE_gpencil_stroke_geometry_update(gso->gpd, selected->gps, GP_GEO_UPDATE_POLYLINE_COLOR);
+ }
}
/* Clear the selected array, but keep the memory allocation. */
gso->pbuffer = gpencil_select_buffer_ensure(
diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c
index 6d953a4d8cf..8ba23f7678f 100644
--- a/source/blender/editors/gpencil/gpencil_weight_paint.c
+++ b/source/blender/editors/gpencil/gpencil_weight_paint.c
@@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_object_deform.h"
#include "BKE_report.h"
@@ -237,8 +238,15 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso,
/* create dvert */
BKE_gpencil_dvert_ensure(gps);
- MDeformVert *dvert = gps->dvert + pt_index;
+ MDeformVert *dvert;
float inf;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ dvert = gpc->dvert + pt_index;
+ }
+ else {
+ dvert = gps->dvert + pt_index;
+ }
/* Compute strength of effect */
inf = brush_influence_calc(gso, radius, co);
@@ -365,7 +373,14 @@ static void gpencil_save_selected_point(tGP_BrushWeightpaintData *gso,
int pc[2])
{
tGP_Selected *selected;
- bGPDspoint *pt = &gps->points[index];
+ bGPDspoint *pt = NULL;
+ bGPDcurve_point *cpt = NULL;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ cpt = &gps->editcurve->curve_points[index];
+ }
+ else {
+ pt = &gps->points[index];
+ }
/* Ensure the array to save the list of selected points is big enough. */
gso->pbuffer = gpencil_select_buffer_ensure(
@@ -375,11 +390,65 @@ static void gpencil_save_selected_point(tGP_BrushWeightpaintData *gso,
selected->gps = gps;
selected->pt_index = index;
copy_v2_v2_int(selected->pc, pc);
- copy_v4_v4(selected->color, pt->vert_color);
+ copy_v4_v4(selected->color, (cpt != NULL) ? cpt->vert_color : pt->vert_color);
gso->pbuffer_used++;
}
+static void gpencil_weightpaint_select_curve(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4],
+ const float bound_mat[4][4])
+{
+ ARegion *region = gso->region;
+ GP_SpaceConversion *gsc = &gso->gsc;
+ rcti *rect = &gso->brush_rect;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDcurve *gpc = gps->editcurve;
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
+ return;
+ }
+
+ if (gpc->tot_curve_points == 1) {
+ return;
+ }
+
+ /* If the curve has more than one control point... */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+
+ int screen_co[2];
+ /* Test if points can be projected. */
+ if (!ED_gpencil_3d_point_to_screen_space(region, rect, diff_mat, bezt->vec[1], screen_co)) {
+ continue;
+ }
+
+ float co[2] = {(float)screen_co[0], (float)screen_co[1]};
+ /* Test if the point is in the circle. */
+ if (len_v2v2(gso->mval, co) > radius) {
+ continue;
+ }
+
+ bGPDcurve_point *cpt_active = NULL;
+ int index = -1;
+ if (cpt->runtime.gpc_pt_orig) {
+ cpt_active = cpt->runtime.gpc_pt_orig;
+ index = cpt->runtime.idx_orig;
+ }
+ else {
+ cpt_active = cpt;
+ index = i;
+ }
+ gpencil_save_selected_point(gso, gps_active, index, screen_co);
+ }
+}
+
/* Select points in this stroke and add to an array to be used later. */
static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
bGPDstroke *gps,
@@ -531,9 +600,13 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C,
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
-
- /* Check points below the brush. */
- gpencil_weightpaint_select_stroke(gso, gps, diff_mat, bound_mat);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ gpencil_weightpaint_select_curve(gso, gps, diff_mat, bound_mat);
+ }
+ else {
+ /* Check points below the brush. */
+ gpencil_weightpaint_select_stroke(gso, gps, diff_mat, bound_mat);
+ }
}
/*---------------------------------------------------------------------
@@ -541,7 +614,6 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C,
*--------------------------------------------------------------------- */
bool changed = false;
for (i = 0; i < gso->pbuffer_used; i++) {
- changed = true;
selected = &gso->pbuffer[i];
switch (tool) {
@@ -554,6 +626,10 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C,
printf("ERROR: Unknown type of GPencil Weight Paint brush\n");
break;
}
+
+ if (changed && GPENCIL_STROKE_TYPE_BEZIER(selected->gps)) {
+ BKE_gpencil_stroke_geometry_update(gso->gpd, selected->gps, GP_GEO_UPDATE_POLYLINE_WEIGHT);
+ }
}
/* Clear the selected array, but keep the memory allocation. */
gso->pbuffer = gpencil_select_buffer_ensure(