diff options
Diffstat (limited to 'source/blender/editors/gpencil')
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( |