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).
This commit is contained in:
parent
9ac56bad4c
commit
338be95874
|
@ -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);
|
||||
|
||||
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, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!EBONE_VISIBLE(arm, ebone)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
float screen_co_a[2], screen_co_b[2];
|
||||
const float *v_a = ebone->head, *v_b = ebone->tail;
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
if (points_proj_tot) { /* at least one point's projection worked */
|
||||
func(userData, ebone, screen_co_a, screen_co_b);
|
||||
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;
|
||||
if (!PBONE_VISIBLE(arm_eval, pchan->bone)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
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;
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
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);
|
||||
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, pchan, screen_co_a, screen_co_b);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue