GP: box/lasso support for select-operations
This adds support for more adbvanced select operations matching 3D view select operators. Also de-duplicate box/lasso select code.
This commit is contained in:
parent
ed4f204d86
commit
b323167600
|
@ -2727,18 +2727,18 @@ def _grease_pencil_selection(params):
|
|||
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "ctrl": True},
|
||||
{"properties": [("deselect", False)]}),
|
||||
{"properties": [("mode", 'ADD')]}),
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "shift": True, "ctrl": True},
|
||||
{"properties": [("deselect", True)]}),
|
||||
{"properties": [("mode", 'SUB')]}),
|
||||
# In the Node Editor, lasso select needs ALT modifier too
|
||||
# (as somehow CTRL+LMB drag gets taken for "cut" quite early).
|
||||
# There probably isn't too much harm adding this for other editors too
|
||||
# as part of standard GP editing keymap. This hotkey combo doesn't seem
|
||||
# to see much use under standard scenarios?
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "ctrl": True, "alt": True},
|
||||
{"properties": [("deselect", False)]}),
|
||||
{"properties": [("mode", 'ADD')]}),
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
|
||||
{"properties": [("deselect", True)]}),
|
||||
{"properties": [("mode", 'SUB')]}),
|
||||
("gpencil.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True},
|
||||
{"properties": [("extend", True), ("toggle", True)]}),
|
||||
# Whole stroke select
|
||||
|
@ -2918,8 +2918,7 @@ def km_grease_pencil_stroke_paint_draw_brush(_params):
|
|||
# Box select
|
||||
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "ctrl": True, "alt": True},
|
||||
{"properties": [("deselect", False)]}),
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "ctrl": True, "alt": True}, None),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -2942,8 +2941,7 @@ def km_grease_pencil_stroke_paint_erase(_params):
|
|||
# Box select (used by eraser)
|
||||
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "ctrl": True, "alt": True},
|
||||
{"properties": [("deselect", False)]}),
|
||||
("gpencil.select_lasso", {"type": 'EVT_TWEAK_A', "value": 'ANY', "ctrl": True, "alt": True}, None),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
|
|
@ -149,6 +149,8 @@ bool BKE_gpencil_data_minmax(
|
|||
bool BKE_gpencil_stroke_minmax(
|
||||
const struct bGPDstroke *gps, const bool use_select,
|
||||
float r_min[3], float r_max[3]);
|
||||
bool BKE_gpencil_stroke_select_check(
|
||||
const struct bGPDstroke *gps);
|
||||
|
||||
struct BoundBox *BKE_gpencil_boundbox_get(struct Object *ob);
|
||||
void BKE_gpencil_centroid_3D(struct bGPdata *gpd, float r_centroid[3]);
|
||||
|
|
|
@ -1133,6 +1133,19 @@ bool BKE_gpencil_data_minmax(Object *ob, const bGPdata *gpd, float r_min[3], flo
|
|||
return changed;
|
||||
}
|
||||
|
||||
bool BKE_gpencil_stroke_select_check(
|
||||
const bGPDstroke *gps)
|
||||
{
|
||||
const bGPDspoint *pt;
|
||||
int i;
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* compute center of bounding box */
|
||||
void BKE_gpencil_centroid_3D(bGPdata *gpd, float r_centroid[3])
|
||||
{
|
||||
|
|
|
@ -199,14 +199,14 @@ bool gp_stroke_inside_circle(
|
|||
void gp_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
|
||||
|
||||
void gp_point_to_xy(
|
||||
GP_SpaceConversion *settings, struct bGPDstroke *gps, struct bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const struct bGPDstroke *gps, const struct bGPDspoint *pt,
|
||||
int *r_x, int *r_y);
|
||||
|
||||
void gp_point_to_xy_fl(
|
||||
GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt,
|
||||
float *r_x, float *r_y);
|
||||
|
||||
void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt);
|
||||
void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt);
|
||||
/**
|
||||
* Change points position relative to parent object
|
||||
*/
|
||||
|
|
|
@ -1014,21 +1014,25 @@ void GPENCIL_OT_select_circle(wmOperatorType *ot)
|
|||
WM_operator_properties_gesture_circle_select(ot);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Box Selection */
|
||||
|
||||
static int gpencil_box_select_exec(bContext *C, wmOperator *op)
|
||||
typedef bool (*GPencilTestFn)(
|
||||
bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data);
|
||||
|
||||
static int gpencil_generic_select_exec(
|
||||
bContext *C, wmOperator *op,
|
||||
GPencilTestFn is_inside_fn, void *user_data)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
const bool strokemode = (
|
||||
(ts->gpencil_selectmode == GP_SELECTMODE_STROKE) &&
|
||||
((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
|
||||
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
|
||||
|
||||
const bool select = !RNA_boolean_get(op->ptr, "deselect");
|
||||
bool extend = RNA_boolean_get(op->ptr, "extend") && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0);
|
||||
const bool strokemode = (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0);
|
||||
|
||||
GP_SpaceConversion gsc = {NULL};
|
||||
rcti rect = {0};
|
||||
|
||||
bool changed = false;
|
||||
|
||||
|
@ -1038,15 +1042,12 @@ static int gpencil_box_select_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (strokemode) {
|
||||
extend = false;
|
||||
}
|
||||
|
||||
/* init space conversion stuff */
|
||||
gp_point_conversion_init(C, &gsc);
|
||||
|
||||
/* deselect all strokes first? */
|
||||
if (select && !extend) {
|
||||
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
|
@ -1061,9 +1062,6 @@ static int gpencil_box_select_exec(bContext *C, wmOperator *op)
|
|||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
/* get settings from operator */
|
||||
WM_operator_properties_border_to_rcti(op, &rect);
|
||||
|
||||
/* select/deselect points */
|
||||
GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
|
||||
{
|
||||
|
@ -1072,40 +1070,40 @@ static int gpencil_box_select_exec(bContext *C, wmOperator *op)
|
|||
int i;
|
||||
bool hit = false;
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
int x0, y0;
|
||||
|
||||
/* convert point coords to screenspace */
|
||||
bGPDspoint pt2;
|
||||
gp_point_to_parent_space(pt, diff_mat, &pt2);
|
||||
gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0);
|
||||
const bool is_inside = is_inside_fn(gps, pt, &gsc, diff_mat, user_data);
|
||||
|
||||
/* test if in selection rect */
|
||||
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) {
|
||||
hit = true;
|
||||
if (select) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
if (strokemode == false) {
|
||||
const bool is_select = (pt->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->flag, sel_op_result, GP_SPOINT_SELECT);
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
|
||||
/* if stroke mode, don't check more points */
|
||||
if ((hit) && (strokemode)) {
|
||||
}
|
||||
else {
|
||||
if (is_inside) {
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if stroke mode expand selection */
|
||||
if ((hit) && (strokemode)) {
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (select) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
if (strokemode) {
|
||||
const bool is_select = BKE_gpencil_stroke_select_check(gps);
|
||||
const bool is_inside = hit;
|
||||
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++) {
|
||||
if (sel_op_result) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1134,6 +1132,35 @@ static int gpencil_box_select_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Box Selection */
|
||||
|
||||
struct GP_SelectBoxUserData {
|
||||
rcti rect;
|
||||
};
|
||||
|
||||
static bool gpencil_test_box(
|
||||
bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data)
|
||||
{
|
||||
const struct GP_SelectBoxUserData *data = user_data;
|
||||
bGPDspoint pt2;
|
||||
int x0, y0;
|
||||
gp_point_to_parent_space(pt, diff_mat, &pt2);
|
||||
gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
|
||||
return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) &&
|
||||
BLI_rcti_isect_pt(&data->rect, x0, y0));
|
||||
}
|
||||
|
||||
static int gpencil_box_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
struct GP_SelectBoxUserData data = {0};
|
||||
WM_operator_properties_border_to_rcti(op, &data.rect);
|
||||
return gpencil_generic_select_exec(
|
||||
C, op,
|
||||
gpencil_test_box, &data);
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_box(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
|
@ -1153,129 +1180,55 @@ void GPENCIL_OT_select_box(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
|
||||
|
||||
/* rna */
|
||||
WM_operator_properties_gesture_box_select(ot);
|
||||
WM_operator_properties_select_operation(ot);
|
||||
WM_operator_properties_gesture_box(ot);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Lasso */
|
||||
|
||||
struct GP_SelectLassoUserData {
|
||||
rcti rect;
|
||||
const int (*mcords)[2];
|
||||
int mcords_len;
|
||||
};
|
||||
|
||||
static bool gpencil_test_lasso(
|
||||
bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const float diff_mat[4][4],
|
||||
void *user_data)
|
||||
{
|
||||
const struct GP_SelectLassoUserData *data = user_data;
|
||||
bGPDspoint pt2;
|
||||
int x0, y0;
|
||||
gp_point_to_parent_space(pt, diff_mat, &pt2);
|
||||
gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
|
||||
/* test if in lasso boundbox + within the lasso noose */
|
||||
return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) &&
|
||||
BLI_rcti_isect_pt(&data->rect, x0, y0) &&
|
||||
BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX));
|
||||
}
|
||||
|
||||
static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
GP_SpaceConversion gsc = {NULL};
|
||||
rcti rect = {0};
|
||||
struct GP_SelectLassoUserData data = {0};
|
||||
data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len);
|
||||
|
||||
bool extend = RNA_boolean_get(op->ptr, "extend") && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0);
|
||||
const bool select = !RNA_boolean_get(op->ptr, "deselect");
|
||||
const bool strokemode = (ts->gpencil_selectmode == GP_SELECTMODE_STROKE) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0);
|
||||
int mcords_tot;
|
||||
const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
/* sanity check */
|
||||
if (mcords == NULL)
|
||||
/* Sanity check. */
|
||||
if (data.mcords == NULL) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
|
||||
if (strokemode) {
|
||||
extend = false;
|
||||
}
|
||||
|
||||
/* compute boundbox of lasso (for faster testing later) */
|
||||
BLI_lasso_boundbox(&rect, mcords, mcords_tot);
|
||||
/* Compute boundbox of lasso (for faster testing later). */
|
||||
BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len);
|
||||
|
||||
/* init space conversion stuff */
|
||||
gp_point_conversion_init(C, &gsc);
|
||||
int ret = gpencil_generic_select_exec(
|
||||
C, op,
|
||||
gpencil_test_lasso, &data);
|
||||
|
||||
/* deselect all strokes first? */
|
||||
if (select && !extend) {
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
MEM_freeN((void *)data.mcords);
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
/* select/deselect points */
|
||||
GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
bool hit = false;
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
int x0, y0;
|
||||
|
||||
/* convert point coords to screenspace */
|
||||
bGPDspoint pt2;
|
||||
gp_point_to_parent_space(pt, diff_mat, &pt2);
|
||||
gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0);
|
||||
/* test if in lasso boundbox + within the lasso noose */
|
||||
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) &&
|
||||
BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX))
|
||||
{
|
||||
hit = true;
|
||||
if (select) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
|
||||
/* if stroke mode, don't check more points */
|
||||
if ((hit) && (strokemode)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if stroke mode expand selection */
|
||||
if ((hit) && (strokemode)) {
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (select) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure that stroke selection is in sync with its points */
|
||||
BKE_gpencil_stroke_sync_selection(gps);
|
||||
}
|
||||
GP_EDITABLE_STROKES_END;
|
||||
|
||||
/* cleanup */
|
||||
MEM_freeN((void *)mcords);
|
||||
|
||||
/* if paint mode,delete selected points */
|
||||
if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
|
||||
gp_delete_selected_point_wrap(C);
|
||||
changed = true;
|
||||
DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA);
|
||||
}
|
||||
|
||||
/* updates */
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&gpd->id, OB_RECALC_DATA);
|
||||
|
||||
/* copy on write tag is needed, or else no refresh happens */
|
||||
DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE);
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_lasso(wmOperatorType *ot)
|
||||
|
@ -1294,7 +1247,8 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
|
||||
|
||||
/* properties */
|
||||
WM_operator_properties_gesture_lasso_select(ot);
|
||||
WM_operator_properties_select_operation(ot);
|
||||
WM_operator_properties_gesture_lasso(ot);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
|
|
@ -555,7 +555,7 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
|
|||
* \param diff_mat: Matrix with the difference between original parent matrix
|
||||
* \param[out] r_pt: Pointer to new point after apply matrix
|
||||
*/
|
||||
void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt)
|
||||
void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt)
|
||||
{
|
||||
float fpt[3];
|
||||
|
||||
|
@ -612,12 +612,12 @@ void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bG
|
|||
* \warning This assumes that the caller has already checked whether the stroke in question can be drawn.
|
||||
*/
|
||||
void gp_point_to_xy(
|
||||
GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt,
|
||||
int *r_x, int *r_y)
|
||||
{
|
||||
ARegion *ar = gsc->ar;
|
||||
View2D *v2d = gsc->v2d;
|
||||
rctf *subrect = gsc->subrect;
|
||||
const ARegion *ar = gsc->ar;
|
||||
const View2D *v2d = gsc->v2d;
|
||||
const rctf *subrect = gsc->subrect;
|
||||
int xyval[2];
|
||||
|
||||
/* sanity checks */
|
||||
|
@ -666,12 +666,12 @@ void gp_point_to_xy(
|
|||
* \warning This assumes that the caller has already checked whether the stroke in question can be drawn
|
||||
*/
|
||||
void gp_point_to_xy_fl(
|
||||
GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt,
|
||||
float *r_x, float *r_y)
|
||||
{
|
||||
ARegion *ar = gsc->ar;
|
||||
View2D *v2d = gsc->v2d;
|
||||
rctf *subrect = gsc->subrect;
|
||||
const ARegion *ar = gsc->ar;
|
||||
const View2D *v2d = gsc->v2d;
|
||||
const rctf *subrect = gsc->subrect;
|
||||
float xyval[2];
|
||||
|
||||
/* sanity checks */
|
||||
|
|
Loading…
Reference in New Issue