Curves: Add select pick operator #104406

Merged
Hans Goudey merged 1 commits from filedescriptor/blender:curves-mouse-selection into main 2023-02-07 17:50:41 +01:00
4 changed files with 169 additions and 6 deletions

View File

@ -9,11 +9,13 @@
#include "BLI_rand.hh"
#include "BKE_attribute.hh"
#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
#include "ED_curves.h"
#include "ED_object.h"
#include "ED_select_utils.h"
#include "ED_view3d.h"
namespace blender::ed::curves {
@ -283,4 +285,137 @@ void select_random(bke::CurvesGeometry &curves,
selection.finish();
}
/**
* Helper struct for `find_closest_point_to_screen_co`.
*/
struct FindClosestPointData {
int index = -1;
float distance = FLT_MAX;
};
static bool find_closest_point_to_screen_co(const Depsgraph &depsgraph,
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const bke::CurvesGeometry &curves,
float2 mouse_pos,
float radius,
FindClosestPointData &closest_data)
{
float4x4 projection;
ED_view3d_ob_project_mat_get(rv3d, &object, projection.ptr());
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(depsgraph, object);
const float radius_sq = pow2f(radius);
auto [min_point_index, min_distance] = threading::parallel_reduce(
curves.points_range(),
1024,
FindClosestPointData(),
[&](const IndexRange point_range, const FindClosestPointData &init) {
FindClosestPointData best_match = init;
for (const int point_i : point_range) {
const float3 pos = deformation.positions[point_i];
/* Find the position of the point in screen space. */
float2 pos_proj;
ED_view3d_project_float_v2_m4(region, pos, pos_proj, projection.ptr());
const float distance_proj_sq = math::distance_squared(pos_proj, mouse_pos);
filedescriptor marked this conversation as resolved
Review

distance_proj_sq feels a bit more consistent/natural IMO

`distance_proj_sq` feels a bit more consistent/natural IMO
if (distance_proj_sq > radius_sq ||
distance_proj_sq > best_match.distance * best_match.distance) {
/* Ignore the point because it's too far away or there is already a better point. */
continue;
}
FindClosestPointData better_candidate;
better_candidate.index = point_i;
better_candidate.distance = std::sqrt(distance_proj_sq);
best_match = better_candidate;
}
return best_match;
},
[](const FindClosestPointData &a, const FindClosestPointData &b) {
return std::min(a.distance, b.distance);
});
if (min_point_index > 0) {
closest_data.index = min_point_index;
closest_data.distance = min_distance;
return true;
}
return false;
}
bool select_pick(const ViewContext &vc,
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const SelectPick_Params &params,
const int2 mval)
{
FindClosestPointData closest_point;
bool found = find_closest_point_to_screen_co(*vc.depsgraph,
vc.region,
vc.rv3d,
*vc.obact,
curves,
float2(mval),
ED_view3d_select_dist_px(),
closest_point);
bool changed = false;
if (params.sel_op == SEL_OP_SET) {
if (found || params.deselect_all) {
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
fill_selection_false(selection.span);
selection.finish();
changed = true;
}
}
if (found) {
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
int elem_index = closest_point.index;
if (selection_domain == ATTR_DOMAIN_CURVE) {
/* Find the curve index for the found point. */
auto it = std::upper_bound(
curves.offsets().begin(), curves.offsets().end(), closest_point.index);
BLI_assert(it != curves.offsets().end());
elem_index = std::distance(curves.offsets().begin(), it) - 1;
}
selection.span.type().to_static_type_tag<bool, float>([&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_void_v<T>) {
BLI_assert_unreachable();
}
else {
MutableSpan<T> selection_typed = selection.span.typed<T>();
switch (params.sel_op) {
case SEL_OP_ADD:
case SEL_OP_SET:
selection_typed[elem_index] = T(1);
break;
case SEL_OP_SUB:
selection_typed[elem_index] = T(0);
break;
case SEL_OP_XOR:
selection_typed[elem_index] = T(1 - selection_typed[elem_index]);
break;
default:
break;
}
}
});
selection.finish();
}
return changed || found;
}
} // namespace blender::ed::curves

View File

@ -9,6 +9,8 @@
struct bContext;
struct Curves;
struct UndoType;
struct SelectPick_Params;
struct ViewContext;
#ifdef __cplusplus
extern "C" {
@ -134,6 +136,17 @@ void select_random(bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
uint32_t random_seed,
float probability);
/**
* Select point or curve under the cursor.
*/
bool select_pick(const ViewContext &vc,
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const SelectPick_Params &params,
const int2 mval);
/** \} */
} // namespace blender::ed::curves

View File

@ -73,6 +73,7 @@ set(SRC
)
set(LIB
bf_editor_curves
bf_editor_lattice
bf_editor_mesh
)

View File

@ -69,6 +69,7 @@
#include "ED_armature.h"
#include "ED_curve.h"
#include "ED_curves.h"
#include "ED_gpencil.h"
#include "ED_lattice.h"
#include "ED_mball.h"
@ -2968,10 +2969,15 @@ static bool ed_wpaint_vertex_select_pick(bContext *C,
static int view3d_select_exec(bContext *C, wmOperator *op)
{
using namespace blender;
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
Object *obact = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
SelectPick_Params params{};
ED_select_pick_params_from_operator(op->ptr, &params);
@ -3021,10 +3027,6 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
}
else if (obedit->type == OB_ARMATURE) {
if (enumerate) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
GPUSelectResult buffer[MAXPICKELEMS];
const int hits = mixed_bones_object_selectbuffer(
&vc, buffer, ARRAY_SIZE(buffer), mval, VIEW3D_SELECT_FILTER_NOP, false, true, false);
@ -3047,6 +3049,19 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
else if (obedit->type == OB_FONT) {
changed = ED_curve_editfont_select_pick(C, mval, &params);
}
else if (obedit->type == OB_CURVES) {
Curves &curves_id = *static_cast<Curves *>(obact->data);
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
changed = ed::curves::select_pick(
vc, curves, eAttrDomain(curves_id.selection_domain), params, mval);
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(&curves_id.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
return true;
}
}
}
else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
changed = PE_mouse_particles(C, mval, &params);
@ -3535,8 +3550,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(