Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2021-06-22 07:04:30 +0300
committerCampbell Barton <ideasman42@gmail.com>2021-06-22 07:23:37 +0300
commit338be95874bddec300a863c9583652cda0ccf5de (patch)
treea0a0a22466b5c33bce5daa3bc7af18bfafd30152 /source/blender/editors
parent9ac56bad4c08750edfdf5ad8e98e257fb6ed4474 (diff)
Fix bone select failing with end-points outside the view
Apply the same fix for T32214 (edge-select failing) to bones which also failed when their end-points were outside of the view. - Add V3D_PROJ_TEST_CLIP_CONTENT support for edit & pose bone iterator and use for selection operators. - Remove unnecessarily complicated checks with pose-mode lasso tagging. - Correct error in pose-mode LassoSelectUserData.is_changed (currently harmless as it's not read back).
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/space_view3d/view3d_iterators.c340
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c132
2 files changed, 295 insertions, 177 deletions
diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c
index 0eb594fb3dc..5e0101e4160 100644
--- a/source/blender/editors/space_view3d/view3d_iterators.c
+++ b/source/blender/editors/space_view3d/view3d_iterators.c
@@ -97,6 +97,110 @@ static int content_planes_from_clip_flag(const ARegion *region,
return planes_len;
}
+/**
+ * Edge projection is more involved since part of the edge may be behind the view
+ * or extend beyond the far limits. In the case of single points, these can be ignored.
+ * However it just may still be visible on screen, so constrained the edge to planes
+ * defined by the port to ensure both ends of the edge can be projected, see T32214.
+ *
+ * \note This is unrelated to #V3D_PROJ_TEST_CLIP_BB which must be checked separately.
+ */
+static bool view3d_project_segment_to_screen_with_content_clip_planes(
+ const ARegion *region,
+ const float v_a[3],
+ const float v_b[3],
+ const eV3DProjTest clip_flag,
+ const rctf *win_rect,
+ const float content_planes[][4],
+ const int content_planes_len,
+ /* Output. */
+ float r_screen_co_a[2],
+ float r_screen_co_b[2])
+{
+ /* Clipping already handled, no need to check in projection. */
+ eV3DProjTest clip_flag_nowin = clip_flag & ~V3D_PROJ_TEST_CLIP_WIN;
+
+ const eV3DProjStatus status_a = ED_view3d_project_float_object(
+ region, v_a, r_screen_co_a, clip_flag_nowin);
+ const eV3DProjStatus status_b = ED_view3d_project_float_object(
+ region, v_b, r_screen_co_b, clip_flag_nowin);
+
+ if ((status_a == V3D_PROJ_RET_OK) && (status_b == V3D_PROJ_RET_OK)) {
+ if (clip_flag & V3D_PROJ_TEST_CLIP_WIN) {
+ if (!BLI_rctf_isect_segment(win_rect, r_screen_co_a, r_screen_co_b)) {
+ return false;
+ }
+ }
+ }
+ else {
+ if (content_planes_len == 0) {
+ return false;
+ }
+
+ /* Both too near, ignore. */
+ if ((status_a & V3D_PROJ_TEST_CLIP_NEAR) && (status_b & V3D_PROJ_TEST_CLIP_NEAR)) {
+ return false;
+ }
+
+ /* Both too far, ignore. */
+ if ((status_a & V3D_PROJ_TEST_CLIP_FAR) && (status_b & V3D_PROJ_TEST_CLIP_FAR)) {
+ return false;
+ }
+
+ /* Simple cases have been ruled out, clip by viewport planes, then re-project. */
+ float v_a_clip[3], v_b_clip[3];
+ if (!clip_segment_v3_plane_n(
+ v_a, v_b, content_planes, content_planes_len, v_a_clip, v_b_clip)) {
+ return false;
+ }
+
+ if ((ED_view3d_project_float_object(region, v_a_clip, r_screen_co_a, clip_flag_nowin) !=
+ V3D_PROJ_RET_OK) ||
+ (ED_view3d_project_float_object(region, v_b_clip, r_screen_co_b, clip_flag_nowin) !=
+ V3D_PROJ_RET_OK)) {
+ return false;
+ }
+
+ /* No need for #V3D_PROJ_TEST_CLIP_WIN check here,
+ * clipping the segment by planes handle this. */
+ }
+
+ return true;
+}
+
+/**
+ * Project an edge, points that fail to project are tagged with #IS_CLIPPED.
+ */
+static bool view3d_project_segment_to_screen_with_clip_tag(const ARegion *region,
+ const float v_a[3],
+ const float v_b[3],
+ const eV3DProjTest clip_flag,
+ /* Output. */
+ float r_screen_co_a[2],
+ float r_screen_co_b[2])
+{
+ int count = 0;
+
+ if (ED_view3d_project_float_object(region, v_a, r_screen_co_a, clip_flag) == V3D_PROJ_RET_OK) {
+ count++;
+ }
+ else {
+ r_screen_co_a[0] = IS_CLIPPED; /* weak */
+ /* screen_co_a[1]: intentionally don't set this so we get errors on misuse */
+ }
+
+ if (ED_view3d_project_float_object(region, v_b, r_screen_co_b, clip_flag) == V3D_PROJ_RET_OK) {
+ count++;
+ }
+ else {
+ r_screen_co_b[0] = IS_CLIPPED; /* weak */
+ /* screen_co_b[1]: intentionally don't set this so we get errors on misuse */
+ }
+
+ /* Caller may want to know this value, for now it's not needed. */
+ return count != 0;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -261,76 +365,6 @@ void mesh_foreachScreenVert(
/** \name Edit-Mesh: For Each Screen Mesh Edge
* \{ */
-/**
- * Edge projection is more involved since part of the edge may be behind the view
- * or extend beyond the far limits. In the case of single points, these can be ignored.
- * However it just may still be visible on screen, so constrained the edge to planes
- * defined by the port to ensure both ends of the edge can be projected, see T32214.
- *
- * \note This is unrelated to #V3D_PROJ_TEST_CLIP_BB which must be checked separately.
- */
-static bool mesh_foreachScreenEdge_shared_project_and_test(const ARegion *region,
- const float v_a[3],
- const float v_b[3],
- const eV3DProjTest clip_flag,
- const rctf *win_rect,
- const float content_planes[][4],
- const int content_planes_len,
- /* Output. */
- float r_screen_co_a[2],
- float r_screen_co_b[2])
-{
- /* Clipping already handled, no need to check in projection. */
- eV3DProjTest clip_flag_nowin = clip_flag & ~V3D_PROJ_TEST_CLIP_WIN;
-
- const eV3DProjStatus status_a = ED_view3d_project_float_object(
- region, v_a, r_screen_co_a, clip_flag_nowin);
- const eV3DProjStatus status_b = ED_view3d_project_float_object(
- region, v_b, r_screen_co_b, clip_flag_nowin);
-
- if ((status_a == V3D_PROJ_RET_OK) && (status_b == V3D_PROJ_RET_OK)) {
- if (clip_flag & V3D_PROJ_TEST_CLIP_WIN) {
- if (!BLI_rctf_isect_segment(win_rect, r_screen_co_a, r_screen_co_b)) {
- return false;
- }
- }
- }
- else {
- if (content_planes_len == 0) {
- return false;
- }
-
- /* Both too near, ignore. */
- if ((status_a & V3D_PROJ_TEST_CLIP_NEAR) && (status_b & V3D_PROJ_TEST_CLIP_NEAR)) {
- return false;
- }
-
- /* Both too far, ignore. */
- if ((status_a & V3D_PROJ_TEST_CLIP_FAR) && (status_b & V3D_PROJ_TEST_CLIP_FAR)) {
- return false;
- }
-
- /* Simple cases have been ruled out, clip by viewport planes, then re-project. */
- float v_a_clip[3], v_b_clip[3];
- if (!clip_segment_v3_plane_n(
- v_a, v_b, content_planes, content_planes_len, v_a_clip, v_b_clip)) {
- return false;
- }
-
- if ((ED_view3d_project_float_object(region, v_a_clip, r_screen_co_a, clip_flag_nowin) !=
- V3D_PROJ_RET_OK) ||
- (ED_view3d_project_float_object(region, v_b_clip, r_screen_co_b, clip_flag_nowin) !=
- V3D_PROJ_RET_OK)) {
- return false;
- }
-
- /* No need for #V3D_PROJ_TEST_CLIP_WIN check here,
- * clipping the segment by planes handle this. */
- }
-
- return true;
-}
-
static void mesh_foreachScreenEdge__mapFunc(void *userData,
int index,
const float v_a[3],
@@ -343,15 +377,15 @@ static void mesh_foreachScreenEdge__mapFunc(void *userData,
}
float screen_co_a[2], screen_co_b[2];
- if (!mesh_foreachScreenEdge_shared_project_and_test(data->vc.region,
- v_a,
- v_b,
- data->clip_flag,
- &data->win_rect,
- data->content_planes,
- data->content_planes_len,
- screen_co_a,
- screen_co_b)) {
+ if (!view3d_project_segment_to_screen_with_content_clip_planes(data->vc.region,
+ v_a,
+ v_b,
+ data->clip_flag,
+ &data->win_rect,
+ data->content_planes,
+ data->content_planes_len,
+ screen_co_a,
+ screen_co_b)) {
return;
}
@@ -430,15 +464,15 @@ static void mesh_foreachScreenEdge_clip_bb_segment__mapFunc(void *userData,
}
float screen_co_a[2], screen_co_b[2];
- if (!mesh_foreachScreenEdge_shared_project_and_test(data->vc.region,
- v_a_clip,
- v_b_clip,
- data->clip_flag,
- &data->win_rect,
- data->content_planes,
- data->content_planes_len,
- screen_co_a,
- screen_co_b)) {
+ if (!view3d_project_segment_to_screen_with_content_clip_planes(data->vc.region,
+ v_a_clip,
+ v_b_clip,
+ data->clip_flag,
+ &data->win_rect,
+ data->content_planes,
+ data->content_planes_len,
+ screen_co_a,
+ screen_co_b)) {
return;
}
@@ -728,35 +762,51 @@ void armature_foreachScreenBone(struct ViewContext *vc,
ED_view3d_check_mats_rv3d(vc->rv3d);
- for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
- if (EBONE_VISIBLE(arm, ebone)) {
- float screen_co_a[2], screen_co_b[2];
- int points_proj_tot = 0;
+ float content_planes[6][4];
+ int content_planes_len;
+ rctf win_rect;
- /* project head location to screenspace */
- if (ED_view3d_project_float_object(vc->region, ebone->head, screen_co_a, clip_flag) ==
- V3D_PROJ_RET_OK) {
- points_proj_tot++;
- }
- else {
- screen_co_a[0] = IS_CLIPPED; /* weak */
- /* screen_co_a[1]: intentionally don't set this so we get errors on misuse */
- }
+ if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) {
+ content_planes_len = content_planes_from_clip_flag(
+ vc->region, vc->obedit, clip_flag, content_planes);
+ win_rect.xmin = 0;
+ win_rect.ymin = 0;
+ win_rect.xmax = vc->region->winx;
+ win_rect.ymax = vc->region->winy;
+ }
+ else {
+ content_planes_len = 0;
+ }
- /* project tail location to screenspace */
- if (ED_view3d_project_float_object(vc->region, ebone->tail, screen_co_b, clip_flag) ==
- V3D_PROJ_RET_OK) {
- points_proj_tot++;
- }
- else {
- screen_co_b[0] = IS_CLIPPED; /* weak */
- /* screen_co_b[1]: intentionally don't set this so we get errors on misuse */
- }
+ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
+ if (!EBONE_VISIBLE(arm, ebone)) {
+ continue;
+ }
- if (points_proj_tot) { /* at least one point's projection worked */
- func(userData, ebone, screen_co_a, screen_co_b);
+ float screen_co_a[2], screen_co_b[2];
+ const float *v_a = ebone->head, *v_b = ebone->tail;
+
+ if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) {
+ if (!view3d_project_segment_to_screen_with_content_clip_planes(vc->region,
+ v_a,
+ v_b,
+ clip_flag,
+ &win_rect,
+ content_planes,
+ content_planes_len,
+ screen_co_a,
+ screen_co_b)) {
+ continue;
+ }
+ }
+ else {
+ if (!view3d_project_segment_to_screen_with_clip_tag(
+ vc->region, v_a, v_b, clip_flag, screen_co_a, screen_co_b)) {
+ continue;
}
}
+
+ func(userData, ebone, screen_co_a, screen_co_b);
}
}
@@ -783,36 +833,52 @@ void pose_foreachScreenBone(struct ViewContext *vc,
ED_view3d_check_mats_rv3d(vc->rv3d);
+ float content_planes[6][4];
+ int content_planes_len;
+ rctf win_rect;
+
+ if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) {
+ content_planes_len = content_planes_from_clip_flag(
+ vc->region, ob_eval, clip_flag, content_planes);
+ win_rect.xmin = 0;
+ win_rect.ymin = 0;
+ win_rect.xmax = vc->region->winx;
+ win_rect.ymax = vc->region->winy;
+ }
+ else {
+ content_planes_len = 0;
+ }
+
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- if (PBONE_VISIBLE(arm_eval, pchan->bone)) {
- bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name);
- float screen_co_a[2], screen_co_b[2];
- int points_proj_tot = 0;
-
- /* project head location to screenspace */
- if (ED_view3d_project_float_object(
- vc->region, pchan_eval->pose_head, screen_co_a, clip_flag) == V3D_PROJ_RET_OK) {
- points_proj_tot++;
- }
- else {
- screen_co_a[0] = IS_CLIPPED; /* weak */
- /* screen_co_a[1]: intentionally don't set this so we get errors on misuse */
- }
+ if (!PBONE_VISIBLE(arm_eval, pchan->bone)) {
+ continue;
+ }
- /* project tail location to screenspace */
- if (ED_view3d_project_float_object(
- vc->region, pchan_eval->pose_tail, screen_co_b, clip_flag) == V3D_PROJ_RET_OK) {
- points_proj_tot++;
+ bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name);
+ float screen_co_a[2], screen_co_b[2];
+ const float *v_a = pchan_eval->pose_head, *v_b = pchan_eval->pose_tail;
+
+ if (clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) {
+ if (!view3d_project_segment_to_screen_with_content_clip_planes(vc->region,
+ v_a,
+ v_b,
+ clip_flag,
+ &win_rect,
+ content_planes,
+ content_planes_len,
+ screen_co_a,
+ screen_co_b)) {
+ continue;
}
- else {
- screen_co_b[0] = IS_CLIPPED; /* weak */
- /* screen_co_b[1]: intentionally don't set this so we get errors on misuse */
- }
-
- if (points_proj_tot) { /* at least one point's projection worked */
- func(userData, pchan, screen_co_a, screen_co_b);
+ }
+ else {
+ if (!view3d_project_segment_to_screen_with_clip_tag(
+ vc->region, v_a, v_b, clip_flag, screen_co_a, screen_co_b)) {
+ continue;
}
}
+
+ func(userData, pchan, screen_co_a, screen_co_b);
}
}
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index fcd735d63a2..02ed9f6d791 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -525,39 +525,12 @@ static void do_lasso_select_pose__do_tag(void *userData,
return;
}
- bool is_point_done = false;
- int points_proj_tot = 0;
-
- /* project head location to screenspace */
- if (screen_co_a[0] != IS_CLIPPED) {
- points_proj_tot++;
- if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
- BLI_lasso_is_point_inside(
- data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) {
- is_point_done = true;
- }
- }
-
- /* project tail location to screenspace */
- if (screen_co_b[0] != IS_CLIPPED) {
- points_proj_tot++;
- if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
- BLI_lasso_is_point_inside(
- data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) {
- is_point_done = true;
- }
- }
-
- /* if one of points selected, we skip the bone itself */
- if ((is_point_done == true) || ((is_point_done == false) && (points_proj_tot == 2) &&
- BLI_lasso_is_edge_inside(data->mcoords,
- data->mcoords_len,
- UNPACK2(screen_co_a),
- UNPACK2(screen_co_b),
- INT_MAX))) {
+ if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) &&
+ BLI_lasso_is_edge_inside(
+ data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) {
pchan->bone->flag |= BONE_DONE;
+ data->is_changed = true;
}
- data->is_changed |= is_point_done;
}
static void do_lasso_tag_pose(ViewContext *vc,
Object *ob,
@@ -581,7 +554,11 @@ static void do_lasso_tag_pose(ViewContext *vc,
ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d);
- pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__do_tag, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+ /* Treat bones as clipped segments (no joints). */
+ pose_foreachScreenBone(&vc_tmp,
+ do_lasso_select_pose__do_tag,
+ &data,
+ V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT);
}
static bool do_lasso_select_objects(ViewContext *vc,
@@ -1071,6 +1048,34 @@ static void do_lasso_select_armature__doSelectBone(void *userData,
ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
}
+static void do_lasso_select_armature__doSelectBone_clip_content(void *userData,
+ EditBone *ebone,
+ const float screen_co_a[2],
+ const float screen_co_b[2])
+{
+ LassoSelectUserData *data = userData;
+ bArmature *arm = data->vc->obedit->data;
+ if (!EBONE_VISIBLE(arm, ebone)) {
+ return;
+ }
+
+ const int is_ignore_flag = ebone->temp.i << 16;
+ int is_inside_flag = ebone->temp.i & ~0xFFFF;
+
+ /* - When #BONESEL_BONE is set, there is nothing to do.
+ * - When #BONE_ROOTSEL or #BONE_TIPSEL have been set - they take priority over bone selection.
+ */
+ if (is_inside_flag & (BONESEL_BONE | BONE_ROOTSEL | BONE_TIPSEL)) {
+ return;
+ }
+
+ if (BLI_lasso_is_edge_inside(
+ data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) {
+ is_inside_flag |= BONESEL_BONE;
+ }
+
+ ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
+}
static bool do_lasso_select_armature(ViewContext *vc,
const int mcoords[][2],
@@ -1094,9 +1099,18 @@ static bool do_lasso_select_armature(ViewContext *vc,
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+ /* Operate on fully visible (non-clipped) points. */
armature_foreachScreenBone(
vc, do_lasso_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+ /* Operate on bones as segments clipped to the viewport bounds
+ * (needed to handle bones with both points outside the view).
+ * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
+ armature_foreachScreenBone(vc,
+ do_lasso_select_armature__doSelectBone_clip_content,
+ &data,
+ V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT);
+
data.is_changed |= ED_armature_edit_select_op_from_tagged(vc->obedit->data, sel_op);
if (data.is_changed) {
@@ -4102,8 +4116,11 @@ static bool pose_circle_select(ViewContext *vc,
ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
- pose_foreachScreenBone(
- vc, do_circle_select_pose__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+ /* Treat bones as clipped segments (no joints). */
+ pose_foreachScreenBone(vc,
+ do_circle_select_pose__doSelectBone,
+ &data,
+ V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT);
if (data.is_changed) {
ED_pose_bone_select_tag_update(vc->obact);
@@ -4150,7 +4167,11 @@ static void do_circle_select_armature__doSelectBone(void *userData,
return;
}
+ /* When true, ignore in the next pass. */
+ ebone->temp.i = false;
+
bool is_point_done = false;
+ bool is_edge_done = false;
int points_proj_tot = 0;
/* project head location to screenspace */
@@ -4178,17 +4199,39 @@ static void do_circle_select_armature__doSelectBone(void *userData,
* otherwise there is no way to circle select joints alone */
if ((is_point_done == false) && (points_proj_tot == 2) &&
edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
- if (data->select) {
- ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- else {
- ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
+ SET_FLAG_FROM_TEST(ebone->flag, data->select, BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ is_edge_done = true;
data->is_changed = true;
}
+ if (is_point_done || is_edge_done) {
+ ebone->temp.i = true;
+ }
+
data->is_changed |= is_point_done;
}
+static void do_circle_select_armature__doSelectBone_clip_content(void *userData,
+ struct EditBone *ebone,
+ const float screen_co_a[2],
+ const float screen_co_b[2])
+{
+ CircleSelectUserData *data = userData;
+ bArmature *arm = data->vc->obedit->data;
+
+ if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) {
+ return;
+ }
+
+ /* Set in the first pass, needed so circle select prioritizes joints. */
+ if (ebone->temp.i == true) {
+ return;
+ }
+
+ if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
+ SET_FLAG_FROM_TEST(ebone->flag, data->select, BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ data->is_changed = true;
+ }
+}
static bool armature_circle_select(ViewContext *vc,
const eSelectOp sel_op,
const int mval[2],
@@ -4207,9 +4250,18 @@ static bool armature_circle_select(ViewContext *vc,
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+ /* Operate on fully visible (non-clipped) points. */
armature_foreachScreenBone(
vc, do_circle_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+ /* Operate on bones as segments clipped to the viewport bounds
+ * (needed to handle bones with both points outside the view).
+ * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
+ armature_foreachScreenBone(vc,
+ do_circle_select_armature__doSelectBone_clip_content,
+ &data,
+ V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT);
+
if (data.is_changed) {
ED_armature_edit_sync_selection(arm->edbo);
ED_armature_edit_validate_active(arm);