Curves: Select linked pick operator
This implements the "Select Linked Pick" operator, by default exposed in the keymap with `L` and `Shift-L`. This mirrors the existing operator in legacy-curve edit mode and mesh edit mode. The implementation is pretty simple, we just find the curve closest to the mouse and change the selection of the points in that curve. Pull Request: https://projects.blender.org/blender/blender/pulls/129885
This commit is contained in:
@@ -5707,6 +5707,9 @@ def km_edit_curves(params):
|
||||
*_template_items_select_actions(params, "curves.select_all"),
|
||||
("curves.extrude_move", {"type": 'E', "value": 'PRESS'}, None),
|
||||
("curves.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
|
||||
("curves.select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}),
|
||||
("curves.select_linked_pick", {"type": 'L', "value": 'PRESS',
|
||||
"shift": True}, {"properties": [("deselect", True)]}),
|
||||
("curves.delete", {"type": 'X', "value": 'PRESS'}, None),
|
||||
("curves.delete", {"type": 'DEL', "value": 'PRESS'}, None),
|
||||
("curves.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||
|
||||
@@ -31,6 +31,7 @@ set(SRC
|
||||
intern/curves_ops.cc
|
||||
intern/curves_selection.cc
|
||||
intern/curves_undo.cc
|
||||
intern/select_linked_pick.cc
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
@@ -1777,6 +1777,7 @@ void operatortypes_curves()
|
||||
WM_operatortype_append(CURVES_OT_select_random);
|
||||
WM_operatortype_append(CURVES_OT_select_ends);
|
||||
WM_operatortype_append(CURVES_OT_select_linked);
|
||||
WM_operatortype_append(CURVES_OT_select_linked_pick);
|
||||
WM_operatortype_append(CURVES_OT_select_more);
|
||||
WM_operatortype_append(CURVES_OT_select_less);
|
||||
WM_operatortype_append(CURVES_OT_surface_set);
|
||||
|
||||
143
source/blender/editors/curves/intern/select_linked_pick.cc
Normal file
143
source/blender/editors/curves/intern/select_linked_pick.cc
Normal file
@@ -0,0 +1,143 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_layer.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_curves.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
namespace blender::ed::curves {
|
||||
|
||||
struct ClosestCurveDataBlock {
|
||||
Curves *curves_id = nullptr;
|
||||
FindClosestData elem = {};
|
||||
};
|
||||
static ClosestCurveDataBlock find_closest_curve(const Depsgraph &depsgraph,
|
||||
const ViewContext &vc,
|
||||
const Span<Base *> bases,
|
||||
const int2 &mval)
|
||||
{
|
||||
return threading::parallel_reduce(
|
||||
bases.index_range(),
|
||||
1L,
|
||||
ClosestCurveDataBlock(),
|
||||
[&](const IndexRange range, const ClosestCurveDataBlock &init) {
|
||||
ClosestCurveDataBlock new_closest = init;
|
||||
for (Base *base : bases.slice(range)) {
|
||||
Object &curves_ob = *base->object;
|
||||
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
|
||||
bke::crazyspace::GeometryDeformation deformation =
|
||||
bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob);
|
||||
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &curves_ob);
|
||||
foreach_selectable_curve_range(
|
||||
curves,
|
||||
deformation,
|
||||
eHandleDisplay(vc.v3d->overlay.handle_display),
|
||||
[&](IndexRange range, Span<float3> positions, StringRef /*selection_name*/) {
|
||||
std::optional<FindClosestData> new_closest_elem = closest_elem_find_screen_space(
|
||||
vc,
|
||||
curves.points_by_curve(),
|
||||
positions,
|
||||
curves.cyclic(),
|
||||
projection,
|
||||
range,
|
||||
bke::AttrDomain::Curve,
|
||||
mval,
|
||||
new_closest.elem);
|
||||
if (new_closest_elem) {
|
||||
new_closest.elem = *new_closest_elem;
|
||||
new_closest.curves_id = &curves_id;
|
||||
}
|
||||
});
|
||||
}
|
||||
return new_closest;
|
||||
},
|
||||
[](const ClosestCurveDataBlock &a, const ClosestCurveDataBlock &b) {
|
||||
return (a.elem.distance < b.elem.distance) ? a : b;
|
||||
});
|
||||
}
|
||||
|
||||
static bool select_linked_pick(bContext &C, const int2 &mval, const SelectPick_Params ¶ms)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(&C);
|
||||
const ViewContext vc = ED_view3d_viewcontext_init(&C, depsgraph);
|
||||
const Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
|
||||
vc.scene, vc.view_layer, vc.v3d);
|
||||
|
||||
const ClosestCurveDataBlock closest = find_closest_curve(*depsgraph, vc, bases, mval);
|
||||
if (!closest.curves_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bke::CurvesGeometry &closest_curves = closest.curves_id->geometry.wrap();
|
||||
const bke::AttrDomain selection_domain = bke::AttrDomain(closest.curves_id->selection_domain);
|
||||
|
||||
if (selection_domain == bke::AttrDomain::Point) {
|
||||
const OffsetIndices points_by_curve = closest_curves.points_by_curve();
|
||||
foreach_selection_attribute_writer(
|
||||
closest_curves, bke::AttrDomain::Point, [&](bke::GSpanAttributeWriter &selection) {
|
||||
for (const int point : points_by_curve[closest.elem.index]) {
|
||||
apply_selection_operation_at_index(selection.span, point, params.sel_op);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (selection_domain == bke::AttrDomain::Curve) {
|
||||
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
||||
closest_curves, bke::AttrDomain::Curve, CD_PROP_BOOL);
|
||||
apply_selection_operation_at_index(selection.span, closest.elem.index, params.sel_op);
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
|
||||
* generic attribute for now. */
|
||||
DEG_id_tag_update(&closest.curves_id->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, closest.curves_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
SelectPick_Params params{};
|
||||
params.sel_op = RNA_boolean_get(op->ptr, "deselect") ? SEL_OP_SUB : SEL_OP_ADD;
|
||||
params.deselect_all = false;
|
||||
params.select_passthrough = false;
|
||||
|
||||
if (!select_linked_pick(*C, event->mval, params)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void CURVES_OT_select_linked_pick(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select Linked";
|
||||
ot->idname = "CURVES_OT_select_linked_pick";
|
||||
ot->description = "Select all points in the curve under the cursor";
|
||||
|
||||
ot->invoke = select_linked_pick_invoke;
|
||||
ot->poll = editable_curves_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"deselect",
|
||||
false,
|
||||
"Deselect",
|
||||
"Deselect linked control points rather than selecting them");
|
||||
}
|
||||
|
||||
} // namespace blender::ed::curves
|
||||
@@ -134,6 +134,7 @@ bool curves_poll(bContext *C);
|
||||
void CURVES_OT_attribute_set(wmOperatorType *ot);
|
||||
void CURVES_OT_draw(wmOperatorType *ot);
|
||||
void CURVES_OT_extrude(wmOperatorType *ot);
|
||||
void CURVES_OT_select_linked_pick(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user