GPencil: New Select Random operator

Select strokes or points randomly (similar to meshes).

Reviewed By: pepeland

Differential Revision: https://developer.blender.org/D12157
This commit is contained in:
Antonio Vazquez 2021-08-08 14:53:05 +02:00
parent a7bb537a61
commit a7aeec2655
4 changed files with 236 additions and 0 deletions

View File

@ -1854,6 +1854,7 @@ class VIEW3D_MT_select_gpencil(Menu):
layout.operator("gpencil.select_linked", text="Linked")
layout.operator("gpencil.select_alternate")
layout.operator("gpencil.select_random")
layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped")
if context.mode == 'VERTEX_GPENCIL':

View File

@ -375,6 +375,7 @@ void GPENCIL_OT_select_less(struct wmOperatorType *ot);
void GPENCIL_OT_select_first(struct wmOperatorType *ot);
void GPENCIL_OT_select_last(struct wmOperatorType *ot);
void GPENCIL_OT_select_alternate(struct wmOperatorType *ot);
void GPENCIL_OT_select_random(struct wmOperatorType *ot);
void GPENCIL_OT_select_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_duplicate(struct wmOperatorType *ot);

View File

@ -555,6 +555,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_select_first);
WM_operatortype_append(GPENCIL_OT_select_last);
WM_operatortype_append(GPENCIL_OT_select_alternate);
WM_operatortype_append(GPENCIL_OT_select_random);
WM_operatortype_append(GPENCIL_OT_select_vertex_color);
WM_operatortype_append(GPENCIL_OT_duplicate);

View File

@ -33,6 +33,7 @@
#include "BLI_ghash.h"
#include "BLI_lasso_2d.h"
#include "BLI_math_vector.h"
#include "BLI_rand.h"
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
@ -193,6 +194,28 @@ static void deselect_all_selected(bContext *C)
CTX_DATA_END;
}
static void select_all_stroke_points(bGPdata *gpd, bGPDstroke *gps, bool select)
{
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
if (select) {
pt->flag |= GP_SPOINT_SELECT;
}
else {
pt->flag &= ~GP_SPOINT_SELECT;
}
}
if (select) {
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);
}
}
static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
@ -512,6 +535,216 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Random Operator
* \{ */
static int gpencil_select_random_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
ToolSettings *ts = CTX_data_tool_settings(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
if ((gpd == NULL) || (GPENCIL_NONE_EDIT_MODE(gpd))) {
return OPERATOR_CANCELLED;
}
const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
const float randfac = RNA_float_get(op->ptr, "ratio");
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
const int start = (unselect_ends) ? 1 : 0;
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int selectmode;
if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
}
else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
}
else {
selectmode = ts->gpencil_selectmode_edit;
}
bool changed = false;
int seed_iter = seed;
int stroke_idx = 0;
if (is_curve_edit) {
GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
{
/* Only apply to unselected strokes (if select). */
if (select) {
if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) {
continue;
}
}
else {
if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) {
continue;
}
}
/* Different seed by stroke. */
seed_iter += gps->totpoints + stroke_idx;
stroke_idx++;
if (selectmode == GP_SELECTMODE_STROKE) {
RNG *rng = BLI_rng_new(seed_iter);
const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints;
bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false;
select_stroke ^= select;
/* Curve function has select paremter inverted. */
select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke);
changed = true;
}
else {
int elem_map_len = 0;
bGPDcurve_point **elem_map = MEM_mallocN(sizeof(*elem_map) * gpc->tot_curve_points,
__func__);
bGPDcurve_point *ptc;
for (int i = start; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
elem_map[elem_map_len++] = gpc_pt;
}
BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
const int count_select = elem_map_len * randfac;
for (int i = 0; i < count_select; i++) {
ptc = elem_map[i];
if (select) {
ptc->flag |= GP_SPOINT_SELECT;
BEZT_SEL_ALL(&ptc->bezt);
}
else {
ptc->flag &= ~GP_SPOINT_SELECT;
BEZT_DESEL_ALL(&ptc->bezt);
}
}
MEM_freeN(elem_map);
/* unselect start and end points */
if (unselect_ends) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[0];
gpc_pt->flag &= ~GP_SPOINT_SELECT;
BEZT_DESEL_ALL(&gpc_pt->bezt);
gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1];
gpc_pt->flag &= ~GP_SPOINT_SELECT;
BEZT_DESEL_ALL(&gpc_pt->bezt);
}
BKE_gpencil_curve_sync_selection(gpd, gps);
}
changed = true;
}
GP_EDITABLE_CURVES_END(gps_iter);
}
else {
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* Only apply to unselected strokes (if select). */
if (select) {
if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) {
continue;
}
}
else {
if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) {
continue;
}
}
/* Different seed by stroke. */
seed_iter += gps->totpoints + stroke_idx;
stroke_idx++;
if (selectmode == GP_SELECTMODE_STROKE) {
RNG *rng = BLI_rng_new(seed_iter);
const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints;
bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false;
select_stroke ^= select;
select_all_stroke_points(gpd, gps, select_stroke);
changed = true;
}
else {
int elem_map_len = 0;
bGPDspoint **elem_map = MEM_mallocN(sizeof(*elem_map) * gps->totpoints, __func__);
bGPDspoint *pt;
for (int i = start; i < gps->totpoints; i++) {
pt = &gps->points[i];
elem_map[elem_map_len++] = pt;
}
BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
const int count_select = elem_map_len * randfac;
for (int i = 0; i < count_select; i++) {
pt = elem_map[i];
if (select) {
pt->flag |= GP_SPOINT_SELECT;
}
else {
pt->flag &= ~GP_SPOINT_SELECT;
}
}
MEM_freeN(elem_map);
/* unselect start and end points */
if (unselect_ends) {
pt = &gps->points[0];
pt->flag &= ~GP_SPOINT_SELECT;
pt = &gps->points[gps->totpoints - 1];
pt->flag &= ~GP_SPOINT_SELECT;
}
BKE_gpencil_stroke_sync_selection(gpd, gps);
}
changed = true;
}
CTX_DATA_END;
}
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);
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
}
return OPERATOR_FINISHED;
}
void GPENCIL_OT_select_random(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Random";
ot->idname = "GPENCIL_OT_select_random";
ot->description = "Select random points for non selected strokes";
/* callbacks */
ot->exec = gpencil_select_random_exec;
ot->poll = gpencil_select_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_select_random(ot);
RNA_def_boolean(ot->srna,
"unselect_ends",
false,
"Unselect Ends",
"Do not select the first and last point of the stroke");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Grouped Operator
* \{ */