From c7cebf5f6d3e8a3af0fd2a823516b4eef00bfd64 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 6 Nov 2024 21:25:43 +0100 Subject: [PATCH] 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 --- .../keyconfig/keymap_data/blender_default.py | 3 + source/blender/editors/curves/CMakeLists.txt | 1 + .../editors/curves/intern/curves_ops.cc | 1 + .../curves/intern/select_linked_pick.cc | 143 ++++++++++++++++++ source/blender/editors/include/ED_curves.hh | 1 + 5 files changed, 149 insertions(+) create mode 100644 source/blender/editors/curves/intern/select_linked_pick.cc diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index fd6cfd61d22..95d169d9062 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -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), diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index a8a2e0b95b5..8b46dc9d6c3 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -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 diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index f1d31fb6766..679baca3987 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -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); diff --git a/source/blender/editors/curves/intern/select_linked_pick.cc b/source/blender/editors/curves/intern/select_linked_pick.cc new file mode 100644 index 00000000000..47ed0c1541c --- /dev/null +++ b/source/blender/editors/curves/intern/select_linked_pick.cc @@ -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 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_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 positions, StringRef /*selection_name*/) { + std::optional 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 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 diff --git a/source/blender/editors/include/ED_curves.hh b/source/blender/editors/include/ED_curves.hh index bb785572eb7..21ba843659a 100644 --- a/source/blender/editors/include/ED_curves.hh +++ b/source/blender/editors/include/ED_curves.hh @@ -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); /** \} */