Curves: Add lasso and circle select #104560

Merged
Falk David merged 7 commits from filedescriptor/blender:curves-mouse-selection into main 2023-02-10 19:06:10 +01:00
3 changed files with 182 additions and 11 deletions

View File

@ -6,6 +6,7 @@
#include "BLI_array_utils.hh"
#include "BLI_index_mask_ops.hh"
#include "BLI_lasso_2d.h"
#include "BLI_rand.hh"
#include "BLI_rect.h"
@ -426,7 +427,7 @@ bool select_pick(const ViewContext &vc,
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const SelectPick_Params &params,
const int2 mval)
const int2 coord)
{
FindClosestPointData closest_point;
bool found = find_closest_point_to_screen_co(*vc.depsgraph,
@ -434,7 +435,7 @@ bool select_pick(const ViewContext &vc,
vc.rv3d,
*vc.obact,
curves,
float2(mval),
float2(coord),
ED_view3d_select_dist_px(),
closest_point);
@ -475,8 +476,64 @@ bool select_box(const ViewContext &vc,
const rcti &rect,
const eSelectOp sel_op)
{
rctf rectf;
BLI_rctf_rcti_copy(&rectf, &rect);
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
bool changed = false;
if (sel_op == SEL_OP_SET) {
fill_selection_false(selection.span);
changed = true;
}
float4x4 projection;
ED_view3d_ob_project_mat_get(vc.rv3d, vc.obact, projection.ptr());
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, *vc.obact);
const OffsetIndices points_by_curve = curves.points_by_curve();
if (selection_domain == ATTR_DOMAIN_POINT) {
threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange point_range) {
for (const int point_i : point_range) {
float2 pos_proj;
ED_view3d_project_float_v2_m4(
vc.region, deformation.positions[point_i], pos_proj, projection.ptr());
if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
apply_selection_operation_at_index(selection.span, point_i, sel_op);
changed = true;
Review

In the definition const is still nice though ;)

Span<int2> coords -> const Span<int2> coords

In the definition const is still nice though ;) `Span<int2> coords` -> `const Span<int2> coords`
}
}
});
}
else if (selection_domain == ATTR_DOMAIN_CURVE) {
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
for (const int point_i : points_by_curve[curve_i]) {
float2 pos_proj;
ED_view3d_project_float_v2_m4(
vc.region, deformation.positions[point_i], pos_proj, projection.ptr());
if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
changed = true;
break;
}
}
}
});
}
selection.finish();
return changed;
}
bool select_lasso(const ViewContext &vc,
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const Span<int2> coords,
const eSelectOp sel_op)
{
rcti bbox;
const int(*coord_array)[2] = reinterpret_cast<const int(*)[2]>(coords.data());
BLI_lasso_boundbox(&bbox, coord_array, coords.size());
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
@ -499,7 +556,10 @@ bool select_box(const ViewContext &vc,
float2 pos_proj;
ED_view3d_project_float_v2_m4(
vc.region, deformation.positions[point_i], pos_proj, projection.ptr());
if (BLI_rctf_isect_pt_v(&rectf, pos_proj)) {
/* Check the lasso bounding box first as an optimization. */
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
BLI_lasso_is_point_inside(
coord_array, coords.size(), int(pos_proj.x), int(pos_proj.y), IS_CLIPPED)) {
apply_selection_operation_at_index(selection.span, point_i, sel_op);
changed = true;
}
@ -513,7 +573,67 @@ bool select_box(const ViewContext &vc,
float2 pos_proj;
ED_view3d_project_float_v2_m4(
vc.region, deformation.positions[point_i], pos_proj, projection.ptr());
if (BLI_rctf_isect_pt_v(&rectf, pos_proj)) {
/* Check the lasso bounding box first as an optimization. */
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
BLI_lasso_is_point_inside(
coord_array, coords.size(), int(pos_proj.x), int(pos_proj.y), IS_CLIPPED)) {
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
changed = true;
break;
}
}
}
});
}
selection.finish();
return changed;
}
bool select_circle(const ViewContext &vc,
filedescriptor marked this conversation as resolved
Review

math::distance_squared

`math::distance_squared`
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const int2 coord,
const float radius,
const eSelectOp sel_op)
{
const float radius_sq = pow2f(radius);
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
bool changed = false;
if (sel_op == SEL_OP_SET) {
fill_selection_false(selection.span);
changed = true;
}
float4x4 projection;
ED_view3d_ob_project_mat_get(vc.rv3d, vc.obact, projection.ptr());
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, *vc.obact);
const OffsetIndices points_by_curve = curves.points_by_curve();
if (selection_domain == ATTR_DOMAIN_POINT) {
threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange point_range) {
for (const int point_i : point_range) {
float2 pos_proj;
ED_view3d_project_float_v2_m4(
vc.region, deformation.positions[point_i], pos_proj, projection.ptr());
if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
apply_selection_operation_at_index(selection.span, point_i, sel_op);
changed = true;
}
}
});
}
else if (selection_domain == ATTR_DOMAIN_CURVE) {
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
for (const int point_i : points_by_curve[curve_i]) {
float2 pos_proj;
ED_view3d_project_float_v2_m4(
vc.region, deformation.positions[point_i], pos_proj, projection.ptr());
if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
changed = true;
break;

View File

@ -148,13 +148,13 @@ void select_random(bke::CurvesGeometry &curves,
float probability);
/**
* Select point or curve under the cursor.
* Select point or curve at a (screen-space) point.
*/
bool select_pick(const ViewContext &vc,
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const SelectPick_Params &params,
const int2 mval);
const int2 coord);
/**
* Select points or curves in a (screen-space) rectangle.
@ -164,6 +164,25 @@ bool select_box(const ViewContext &vc,
const eAttrDomain selection_domain,
const rcti &rect,
const eSelectOp sel_op);
filedescriptor marked this conversation as resolved
Review

const eAttrDomain selection_domain -> eAttrDomain selection_domain

Const for by-value types is meaningless in declarations, it only means something in the definition. Same with the other by-value arguments.

`const eAttrDomain selection_domain` -> `eAttrDomain selection_domain` Const for by-value types is meaningless in declarations, it only means something in the definition. Same with the other by-value arguments.
Review

Looks like this is the case for a whole bunch of declarations. Should I change that in a separate commit?

Looks like this is the case for a whole bunch of declarations. Should I change that in a separate commit?
Review

Sure, but the new code should follow that IMO

Sure, but the new code should follow that IMO
filedescriptor marked this conversation as resolved
Review

Span<int2> coords

`Span<int2> coords`
/**
* Select points or curves in a (screen-space) poly shape.
*/
bool select_lasso(const ViewContext &vc,
bke::CurvesGeometry &curves,
eAttrDomain selection_domain,
Span<int2> coords,
eSelectOp sel_op);
/**
* Select points or curves in a (screen-space) circle.
*/
bool select_circle(const ViewContext &vc,
bke::CurvesGeometry &curves,
eAttrDomain selection_domain,
int2 coord,
float radius,
eSelectOp sel_op);
/** \} */
} // namespace blender::ed::curves

View File

@ -1303,6 +1303,7 @@ static bool view3d_lasso_select(bContext *C,
const int mcoords_len,
const eSelectOp sel_op)
{
using namespace blender;
Object *ob = CTX_data_active_object(C);
bool changed_multi = false;
@ -1362,6 +1363,23 @@ static bool view3d_lasso_select(bContext *C,
case OB_MBALL:
changed = do_lasso_select_meta(vc, mcoords, mcoords_len, sel_op);
break;
case OB_CURVES: {
Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
filedescriptor marked this conversation as resolved
Review

Looks like we use vc->obact here and vc->obedit for the update tab below. Is that on purpose?

Looks like we use `vc->obact` here and `vc->obedit` for the update tab below. Is that on purpose?
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
changed = ed::curves::select_lasso(
*vc,
curves,
eAttrDomain(curves_id.selection_domain),
Span<int2>(reinterpret_cast<const int2 *>(mcoords), mcoords_len),
sel_op);
if (changed) {
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */
DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, vc->obedit->data);
}
break;
}
default:
BLI_assert_msg(0, "lasso select on incorrect object type");
break;
@ -3550,8 +3568,7 @@ static bool do_mesh_box_select(ViewContext *vc,
}
if (ts->selectmode & SCE_SELECT_EDGE) {
/* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
struct BoxSelectUserData_ForMeshEdge cb_data {
};
struct BoxSelectUserData_ForMeshEdge cb_data {};
cb_data.data = &data;
cb_data.esel = use_zbuf ? esel : nullptr;
cb_data.backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
@ -3957,7 +3974,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
}
break;
case OB_CURVES: {
Curves &curves_id = *static_cast<Curves *>(vc.obact->data);
Curves &curves_id = *static_cast<Curves *>(vc.obedit->data);
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
changed = ed::curves::select_box(
vc, curves, eAttrDomain(curves_id.selection_domain), rect, sel_op);
@ -4701,6 +4718,7 @@ static bool obedit_circle_select(bContext *C,
const int mval[2],
float rad)
{
using namespace blender;
bool changed = false;
BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
switch (vc->obedit->type) {
@ -4723,6 +4741,20 @@ static bool obedit_circle_select(bContext *C,
case OB_MBALL:
changed = mball_circle_select(vc, sel_op, mval, rad);
break;
case OB_CURVES: {
Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
changed = ed::curves::select_circle(
*vc, curves, eAttrDomain(curves_id.selection_domain), mval, rad, sel_op);
if (changed) {
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */
DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, vc->obedit->data);
}
break;
}
default:
BLI_assert(0);
break;