WIP: Box Lasso and Circle Select Keymap Options for Faces and Edges #105527

Closed
Lukas Sneyd wants to merge 12 commits from lcas:mesh-box-lasso-circle-keymap into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
10 changed files with 871 additions and 139 deletions

View File

@ -25,7 +25,8 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2],
int y0,
int x1,
int y1,
int error_value);
int error_value,
bool fully_inside);
#ifdef __cplusplus
}

View File

@ -55,7 +55,8 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2],
int y0,
int x1,
int y1,
const int error_value)
const int error_value,
bool fully_inside)
{
if (x0 == error_value || x1 == error_value || mcoords_len == 0) {
@ -64,23 +65,31 @@ bool BLI_lasso_is_edge_inside(const int mcoords[][2],
const int v1[2] = {x0, y0}, v2[2] = {x1, y1};
/* check points in lasso */
if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) {
return true;
}
if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) {
/* enclosed faces only checks for both points inside selection area */
if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value) &&
BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) {
return true;
}
/* no points in lasso, so we have to intersect with lasso edge */
if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) {
return true;
}
for (uint a = 0; a < mcoords_len - 1; a++) {
if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) {
if (!fully_inside) {
/* check points in lasso */
if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) {
return true;
}
if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) {
return true;
}
/* no points in lasso, so we have to intersect with lasso edge */
if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) {
return true;
}
for (unsigned int a = 0; a < mcoords_len - 1; a++) {
if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) {
return true;
}
}
}
return false;

View File

@ -705,7 +705,7 @@ bool select_lasso(const ViewContext &vc,
int(pos1_proj.y),
int(pos2_proj.x),
int(pos2_proj.y),
IS_CLIPPED)) {
IS_CLIPPED, false)) {
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
changed = true;
break;

View File

@ -390,11 +390,20 @@ void mesh_foreachScreenEdge_clip_bb_segment(struct ViewContext *vc,
void *userData,
eV3DProjTest clip_flag);
void mesh_foreachScreenFace(
void mesh_foreachScreenFaceCenter(
struct ViewContext *vc,
void (*func)(void *userData, struct BMFace *efa, const float screen_co[2], int index),
void *userData,
eV3DProjTest clip_flag);
void mesh_foreachScreenFaceVerts(struct ViewContext *vc,
void (*func)(void *userData,
struct BMFace *efa,
const float screen_co[][2],
int total_count,
struct rctf *poly_rect,
bool *face_hit),
void *userData,
const eV3DProjTest clip_flag);
void nurbs_foreachScreenVert(struct ViewContext *vc,
void (*func)(void *userData,
struct Nurb *nu,

View File

@ -752,7 +752,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenFace(
mesh_foreachScreenFaceCenter(
vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
*r_dist_center = data.dist_px_manhattan;
@ -804,7 +804,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
*dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag);
mesh_foreachScreenFaceCenter(vc, findnearestface__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;

View File

@ -13,6 +13,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_bitmap.h"
#include "BLI_math_geom.h"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
@ -38,6 +39,8 @@
#include "ED_screen.h"
#include "ED_view3d.h"
#include "MEM_guardedalloc.h"
/* -------------------------------------------------------------------- */
/** \name Internal Clipping Utilities
* \{ */
@ -238,7 +241,7 @@ struct foreachScreenEdge_userData {
int content_planes_len;
};
struct foreachScreenFace_userData {
struct foreachScreenFaceCenter_userData {
void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index);
void *userData;
ViewContext vc;
@ -536,12 +539,12 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc,
/** \name Edit-Mesh: For Each Screen Face Center
* \{ */
static void mesh_foreachScreenFace__mapFunc(void *userData,
static void mesh_foreachScreenFaceCenter__mapFunc(void *userData,
int index,
const float cent[3],
const float /*no*/[3])
{
foreachScreenFace_userData *data = static_cast<foreachScreenFace_userData *>(userData);
foreachScreenFaceCenter_userData *data = static_cast<foreachScreenFaceCenter_userData *>(userData);
BMFace *efa = BM_face_at_index(data->vc.em->bm, index);
if (UNLIKELY(BM_elem_flag_test(efa, BM_ELEM_HIDDEN))) {
return;
@ -556,14 +559,14 @@ static void mesh_foreachScreenFace__mapFunc(void *userData,
data->func(data->userData, efa, screen_co, index);
}
void mesh_foreachScreenFace(
void mesh_foreachScreenFaceCenter(
ViewContext *vc,
void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index),
void *userData,
const eV3DProjTest clip_flag)
{
BLI_assert((clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) == 0);
foreachScreenFace_userData data;
foreachScreenFaceCenter_userData data;
Mesh *me = editbmesh_get_eval_cage_from_orig(
vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
@ -580,16 +583,150 @@ void mesh_foreachScreenFace(
const int face_dot_tags_num = me->runtime->subsurf_face_dot_tags.size();
if (face_dot_tags_num && (face_dot_tags_num != me->totvert)) {
BKE_mesh_foreach_mapped_subdiv_face_center(
me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP);
me, mesh_foreachScreenFaceCenter__mapFunc, &data, MESH_FOREACH_NOP);
}
else {
BKE_mesh_foreach_mapped_face_center(
me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP);
me, mesh_foreachScreenFaceCenter__mapFunc, &data, MESH_FOREACH_NOP);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit-Mesh: For Each Screen Face Verts
* \{ */
void mesh_foreachScreenFaceVerts(ViewContext *vc,
void (*func)(void *userData,
struct BMFace *efa,
const float screen_co[][2],
int total_count,
rctf *screen_rect,
bool *face_hit),
void *userData,
const eV3DProjTest clip_flag)
{
ED_view3d_check_mats_rv3d(vc->rv3d);
BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE);
BMFace *efa;
const BMesh *bm = vc->em->bm;
float temp_screen_co[2];
int total_length = 0;
float(*screen_coords)[2] = static_cast<float(*)[2]>(
MEM_mallocN(sizeof(float) * 2 * bm->totvert, __func__));
int face_screen_verts_size = 4;
float(*face_screen_verts)[2] = static_cast<float(*)[2]>(
MEM_mallocN(sizeof(int) * 2 * face_screen_verts_size, __func__));
/* This makes only sense on subdivided meshes.*/
BLI_bitmap *faces_visited;
int cage_index = BKE_modifiers_get_cage_index(vc->scene, vc->obedit, NULL, 1);
const bool cage_display = cage_index != -1;
if (cage_display) {
faces_visited = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
}
/* Transform and store all visible verts into screen coords. */
for (int i = 0; i < bm->totvert; i++) {
BMVert *bmvert = BM_vert_at_index(vc->em->bm, i);
if (BM_elem_flag_test_bool(bmvert, BM_ELEM_HIDDEN)) {
continue;
}
if (ED_view3d_project_float_object(vc->region, bmvert->co, temp_screen_co, clip_flag) ==
V3D_PROJ_RET_OK) {
screen_coords[i][0] = temp_screen_co[0];
screen_coords[i][1] = temp_screen_co[1];
}
else {
screen_coords[i][0] = 0.0f;
screen_coords[i][1] = 0.0f;
}
}
const int *poly_index = static_cast<const int *>(CustomData_get_layer(&bm->pdata, CD_ORIGINDEX));
const bool use_original_index = poly_index != 0;
rctf poly_rect_data;
rctf *poly_rect = &poly_rect_data;
bool face_hit = false;
/* Collect polygon verts and send off per poly callback. */
for (int i = 0; i < bm->totface; i++) {
int original_index = i;
if (use_original_index) {
original_index = *poly_index++;
if (original_index == ORIGINDEX_NONE) {
continue;
}
}
if (cage_display && BLI_BITMAP_TEST(faces_visited, original_index)) {
continue;
}
efa = BM_face_at_index(vc->em->bm, original_index);
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
if (bm->totvert > face_screen_verts_size) {
face_screen_verts_size = bm->totvert;
MEM_freeN(face_screen_verts);
face_screen_verts = static_cast<float(*)[2]>(
MEM_mallocN(sizeof(float) * 2 * face_screen_verts_size, __func__));
}
total_length = 0;
BLI_rctf_init_minmax(poly_rect);
bool skip = false;
BMLoop *l_first, *l_iter;
int j = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
do {
const int k = BM_elem_index_get(l_iter->v);
face_screen_verts[j][0] = screen_coords[k][0];
face_screen_verts[j][1] = screen_coords[k][1];
/* Ignore polygons with invalid screen coords.*/
if (face_screen_verts[j][0] == 0.0f && face_screen_verts[j][1] == 0.0f) {
skip = true;
break;
}
total_length++, j++;
BLI_rctf_do_minmax_v(poly_rect, screen_coords[k]);
} while ((l_iter = l_iter->next) != l_first);
if (skip) {
continue;
}
face_hit = false;
func(
userData, efa, (const float(*)[2])face_screen_verts, total_length, poly_rect, &face_hit);
if (cage_display && face_hit) {
BLI_BITMAP_ENABLE(faces_visited, original_index);
}
}
}
if (cage_display) {
MEM_freeN(faces_visited);
}
MEM_freeN(screen_coords);
MEM_freeN(face_screen_verts);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit-Nurbs: For Each Screen Vertex
* \{ */

File diff suppressed because it is too large Load Diff

View File

@ -3948,7 +3948,7 @@ static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region,
&region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
BLI_lasso_is_edge_inside(
mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) {
mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED, false)) {
return true;
}
return false;

View File

@ -2331,6 +2331,21 @@ typedef enum eSnapTransformMode {
SCE_SNAP_TRANSFORM_MODE_SCALE = (1 << 2),
} eSnapTransformMode;
/** #ToolSettings.face_select */
enum {
FACE_AUTO = (1 << 0),
FACE_TOUCH = (1 << 1),
FACE_ENCLOSE = (1 << 2),
FACE_CENTER = (1 << 3),
};
/** #ToolSettings.edge_select */
enum {
EDGE_HYBRID = (1 << 0),
EDGE_TOUCH = (1 << 1),
EDGE_ENCLOSE = (1 << 2),
};
/** #ToolSettings.selectmode */
#define SCE_SELECT_VERTEX (1 << 0) /* for mesh */
#define SCE_SELECT_EDGE (1 << 1)

View File

@ -411,8 +411,58 @@ void WM_operator_properties_gesture_box_ex(wmOperatorType *ot, bool deselect, bo
{
PropertyRNA *prop;
static const EnumPropertyItem face_select_items[] = {
{FACE_AUTO,
"FACE_AUTO",
0,
"Auto",
"Select faces that are touched by the selection area in near select. Select faces whose "
"center is touched by the selection area in X-Ray select"},
{FACE_TOUCH,
"FACE_TOUCH",
0,
"Touch",
"Select faces that are touched by the selection area"},
{FACE_ENCLOSE,
"FACE_ENCLOSE",
0,
"Enclose",
"Select faces that are fully inside the selection area"},
{FACE_CENTER,
"FACE_CENTER",
0,
"Center",
"Select faces whose center is touched by the selection area"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem edge_select_items[] = {
{EDGE_HYBRID,
"EDGE_HYBRID",
0,
"Hybrid",
"Select edges that are fully inside the selection area. If no edges are fully inside the "
"selection area, select edges that are touched by the selection area"},
{EDGE_TOUCH,
"EDGE_TOUCH",
0,
"Touch",
"Select edges that are touched by the selection area"},
{EDGE_ENCLOSE,
"EDGE_ENCLOSE",
0,
"Enclose",
"Select edges that are fully inside the selection area"},
{0, NULL, 0, NULL, NULL},
};
WM_operator_properties_border(ot);
prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
if (deselect) {
prop = RNA_def_boolean(
ot->srna, "deselect", false, "Deselect", "Deselect rather than select items");
@ -519,6 +569,55 @@ void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
{
PropertyRNA *prop;
static const EnumPropertyItem face_select_items[] = {
{FACE_AUTO,
"FACE_AUTO",
0,
"Auto",
"Select faces that are touched by the selection area in near select. Select faces whose "
"center is touched by the selection area in X-Ray select"},
{FACE_TOUCH,
"FACE_TOUCH",
0,
"Touch",
"Select faces that are touched by the selection area"},
{FACE_ENCLOSE,
"FACE_ENCLOSE",
0,
"Enclose",
"Select faces that are fully inside the selection area"},
{FACE_CENTER,
"FACE_CENTER",
0,
"Center",
"Select faces whose center is touched by the selection area"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem edge_select_items[] = {
{EDGE_HYBRID,
"EDGE_HYBRID",
0,
"Hybrid",
"Select edges that are fully inside the selection area. If no edges are fully inside the "
"selection area, select edges that are touched by the selection area"},
{EDGE_TOUCH,
"EDGE_TOUCH",
0,
"Touch",
"Select edges that are touched by the selection area"},
{EDGE_ENCLOSE,
"EDGE_ENCLOSE",
0,
"Enclose",
"Select edges that are fully inside the selection area"},
{0, NULL, 0, NULL, NULL},
};
prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
@ -557,6 +656,45 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot)
PropertyRNA *prop;
const int radius_default = 25;
static const EnumPropertyItem face_select_items[] = {
{FACE_AUTO,
"FACE_AUTO",
0,
"Auto",
"Select faces that are touched by the selection area in near select. Select faces whose "
"center is touched by the selection area in X-Ray select"},
{FACE_TOUCH,
"FACE_TOUCH",
0,
"Touch",
"Select faces that are touched by the selection area"},
{FACE_ENCLOSE,
"FACE_ENCLOSE",
0,
"Enclose",
"Select faces that are fully inside the selection area"},
{FACE_CENTER,
"FACE_CENTER",
0,
"Center",
"Select faces whose center is touched by the selection area"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem edge_select_items[] = {
{EDGE_TOUCH,
"EDGE_TOUCH",
0,
"Touch",
"Select edges that are touched by the selection area"},
{EDGE_ENCLOSE,
"EDGE_ENCLOSE",
0,
"Enclose",
"Select edges that are fully inside the selection area"},
{0, NULL, 0, NULL, NULL},
};
prop = RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
@ -565,6 +703,10 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot)
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
void WM_operator_properties_mouse_select(wmOperatorType *ot)