From c7f1a1571b90b9599e511fddd74eea365194340c Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 10 Feb 2023 14:07:15 +0100 Subject: [PATCH 1/5] Curves: Add select linked This adds a new `select_linked` function that selects all the points on a curve if there is at least one point already selected. This also adds a keymap for the operator. --- .../keyconfig/keymap_data/blender_default.py | 1 + release/scripts/startup/bl_ui/space_view3d.py | 1 + .../editors/curves/intern/curves_ops.cc | 58 ++++++++++++++----- .../editors/curves/intern/curves_selection.cc | 35 +++++++++-- source/blender/editors/include/ED_curves.h | 10 ++-- 5 files changed, 80 insertions(+), 25 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 637580364dc..8a3eb28c5ae 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -5627,6 +5627,7 @@ def km_curves(params): ("curves.disable_selection", {"type": 'ONE', "value": 'PRESS', "alt": True}, None), ("curves.disable_selection", {"type": 'TWO', "value": 'PRESS', "alt": True}, None), *_template_items_select_actions(params, "curves.select_all"), + ("curves.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ]) return keymap diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 1ce457480e5..6e8e4155c0b 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2052,6 +2052,7 @@ class VIEW3D_MT_select_edit_curves(Menu): layout.operator("curves.select_all", text="Invert").action = 'INVERT' layout.operator("curves.select_random", text="Random") layout.operator("curves.select_end", text="Endpoints") + layout.operator("curves.select_linked", text="Linked") class VIEW3D_MT_select_sculpt_curves(Menu): diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 59af21f8702..f2dbcb24e95 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -157,6 +157,19 @@ bool curves_poll(bContext *C) return curves_poll_impl(C, false, false, false); } +static bool editable_curves_point_domain_poll(bContext *C) +{ + if (!curves::editable_curves_poll(C)) { + return false; + } + const Curves *curves_id = static_cast(CTX_data_active_object(C)->data); + if (curves_id->selection_domain != ATTR_DOMAIN_POINT) { + CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode"); + return false; + } + return true; +} + using bke::CurvesGeometry; namespace convert_to_particle_system { @@ -931,19 +944,6 @@ static void CURVES_OT_select_random(wmOperatorType *ot) 1.0f); } -static bool select_end_poll(bContext *C) -{ - if (!curves::editable_curves_poll(C)) { - return false; - } - const Curves *curves_id = static_cast(CTX_data_active_object(C)->data); - if (curves_id->selection_domain != ATTR_DOMAIN_POINT) { - CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode"); - return false; - } - return true; -} - static int select_end_exec(bContext *C, wmOperator *op) { VectorSet unique_curves = curves::get_unique_editable_curves(*C); @@ -952,7 +952,7 @@ static int select_end_exec(bContext *C, wmOperator *op) for (Curves *curves_id : unique_curves) { CurvesGeometry &curves = curves_id->geometry.wrap(); - select_ends(curves, eAttrDomain(curves_id->selection_domain), amount, end_points); + select_ends(curves, amount, end_points); /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic * attribute for now. */ @@ -970,7 +970,7 @@ static void CURVES_OT_select_end(wmOperatorType *ot) ot->description = "Select end points of curves"; ot->exec = select_end_exec; - ot->poll = select_end_poll; + ot->poll = editable_curves_point_domain_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -983,6 +983,33 @@ static void CURVES_OT_select_end(wmOperatorType *ot) ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX); } +static int select_linked_exec(bContext *C, wmOperator */*op*/) +{ + VectorSet unique_curves = curves::get_unique_editable_curves(*C); + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = curves_id->geometry.wrap(); + select_linked(curves); + /* 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 OPERATOR_FINISHED; +} + +static void CURVES_OT_select_linked(wmOperatorType *ot) +{ + ot->name = "Select Linked"; + ot->idname = __func__; + ot->description = "Select linked points of curves"; + + ot->exec = select_linked_exec; + ot->poll = editable_curves_point_domain_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + namespace surface_set { static bool surface_set_poll(bContext *C) @@ -1078,6 +1105,7 @@ void ED_operatortypes_curves() WM_operatortype_append(CURVES_OT_select_all); WM_operatortype_append(CURVES_OT_select_random); WM_operatortype_append(CURVES_OT_select_end); + WM_operatortype_append(CURVES_OT_select_linked); WM_operatortype_append(CURVES_OT_surface_set); } diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 875ee2ae9ec..99110a0b1d5 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -203,14 +203,12 @@ void select_all(bke::CurvesGeometry &curves, const eAttrDomain selection_domain, } } -void select_ends(bke::CurvesGeometry &curves, - const eAttrDomain selection_domain, - int amount, - bool end_points) +void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points) { const bool was_anything_selected = has_anything_selected(curves); + const OffsetIndices points_by_curve = curves.points_by_curve(); bke::GSpanAttributeWriter selection = ensure_selection_attribute( - curves, selection_domain, CD_PROP_BOOL); + curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL); if (!was_anything_selected) { fill_selection_true(selection.span); } @@ -223,7 +221,6 @@ void select_ends(bke::CurvesGeometry &curves, MutableSpan selection_typed = selection.span.typed(); threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { for (const int curve_i : range) { - const OffsetIndices points_by_curve = curves.points_by_curve(); if (end_points) { selection_typed.slice(points_by_curve[curve_i].drop_back(amount)).fill(T(0)); } @@ -237,6 +234,32 @@ void select_ends(bke::CurvesGeometry &curves, selection.finish(); } +void select_linked(bke::CurvesGeometry &curves) +{ + const OffsetIndices points_by_curve = curves.points_by_curve(); + bke::GSpanAttributeWriter selection = ensure_selection_attribute( + curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL); + + selection.span.type().to_static_type_tag([&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_void_v) { + BLI_assert_unreachable(); + } + else { + MutableSpan selection_typed = selection.span.typed(); + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + MutableSpan selection_curve_typed = selection_typed.slice(points_by_curve[curve_i]); + if (selection_curve_typed.as_span().contains(T(1))) { + selection_curve_typed.fill(T(1)); + } + } + }); + } + }); + selection.finish(); +} + void select_random(bke::CurvesGeometry &curves, const eAttrDomain selection_domain, uint32_t random_seed, diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 734c1118dfc..622a585d118 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -123,10 +123,12 @@ void select_all(bke::CurvesGeometry &curves, const eAttrDomain selection_domain, * \param amount: The amount of points to select from the front or back. * \param end_points: If true, select the last point(s), if false, select the first point(s). */ -void select_ends(bke::CurvesGeometry &curves, - const eAttrDomain selection_domain, - int amount, - bool end_points); +void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points); + +/** + * Select the points of all curves that have at least one point selected. + */ +void select_linked(bke::CurvesGeometry &curves); /** * Select random points or curves. -- 2.30.2 From 5cfd7002e2f9c018b31640e6195cf5a86f96c048 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 10 Feb 2023 18:31:01 +0100 Subject: [PATCH 2/5] Remove namespace --- source/blender/editors/curves/intern/curves_ops.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index f2dbcb24e95..79dcf7ca158 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -983,9 +983,9 @@ static void CURVES_OT_select_end(wmOperatorType *ot) ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX); } -static int select_linked_exec(bContext *C, wmOperator */*op*/) +static int select_linked_exec(bContext *C, wmOperator * /*op*/) { - VectorSet unique_curves = curves::get_unique_editable_curves(*C); + VectorSet unique_curves = get_unique_editable_curves(*C); for (Curves *curves_id : unique_curves) { CurvesGeometry &curves = curves_id->geometry.wrap(); select_linked(curves); -- 2.30.2 From 04451212dd5e1f6193e0a55b173f49978e63ddbb Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 10 Feb 2023 18:31:14 +0100 Subject: [PATCH 3/5] Fix description --- source/blender/editors/curves/intern/curves_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 79dcf7ca158..77641d7a916 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -1002,7 +1002,7 @@ static void CURVES_OT_select_linked(wmOperatorType *ot) { ot->name = "Select Linked"; ot->idname = __func__; - ot->description = "Select linked points of curves"; + ot->description = "Select all points in curves with any point selection"; ot->exec = select_linked_exec; ot->poll = editable_curves_point_domain_poll; -- 2.30.2 From 2baa30fb06974a782025d6383b98161f70a69f27 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 10 Feb 2023 18:32:11 +0100 Subject: [PATCH 4/5] Pull "has_any_selected" logic out into own function --- .../editors/curves/intern/curves_selection.cc | 36 +++++++++++-------- source/blender/editors/include/ED_curves.h | 5 +++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 99110a0b1d5..8dcb4ab8bb2 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -164,6 +164,21 @@ bool has_anything_selected(const bke::CurvesGeometry &curves) return !selection || contains(selection, true); } +bool has_any_selected(const GSpan selection) +{ + if (selection.type().is()) { + return selection.typed().contains(true); + } + else if (selection.type().is()) { + for (const float elem : selection.typed()) { + if (elem > 0.0f) { + return true; + } + } + } + return false; +} + static void invert_selection(MutableSpan selection) { threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { @@ -240,21 +255,12 @@ void select_linked(bke::CurvesGeometry &curves) bke::GSpanAttributeWriter selection = ensure_selection_attribute( curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL); - selection.span.type().to_static_type_tag([&](auto type_tag) { - using T = typename decltype(type_tag)::type; - if constexpr (std::is_void_v) { - BLI_assert_unreachable(); - } - else { - MutableSpan selection_typed = selection.span.typed(); - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { - for (const int curve_i : range) { - MutableSpan selection_curve_typed = selection_typed.slice(points_by_curve[curve_i]); - if (selection_curve_typed.as_span().contains(T(1))) { - selection_curve_typed.fill(T(1)); - } - } - }); + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + GMutableSpan selection_curve = selection.span.slice(points_by_curve[curve_i]); + if (has_any_selected(selection_curve)) { + fill_selection_true(selection_curve); + } } }); selection.finish(); diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 622a585d118..114a5991bdc 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -89,6 +89,11 @@ void fill_selection_true(GMutableSpan span); */ bool has_anything_selected(const bke::CurvesGeometry &curves); +/** + * Return true if any element in the span is selected, on either domain with either type. + */ +bool has_any_selected(const GSpan selection); + /** * Find curves that have any point selected (a selection factor greater than zero), * or curves that have their own selection factor greater than zero. -- 2.30.2 From fefe499f0dc3bf06ea4f1998792149d55048d312 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 10 Feb 2023 18:57:33 +0100 Subject: [PATCH 5/5] Cleanup --- source/blender/editors/curves/intern/curves_selection.cc | 4 ++-- source/blender/editors/include/ED_curves.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 8dcb4ab8bb2..b285c96b584 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -164,7 +164,7 @@ bool has_anything_selected(const bke::CurvesGeometry &curves) return !selection || contains(selection, true); } -bool has_any_selected(const GSpan selection) +bool has_anything_selected(const GSpan selection) { if (selection.type().is()) { return selection.typed().contains(true); @@ -258,7 +258,7 @@ void select_linked(bke::CurvesGeometry &curves) threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { for (const int curve_i : range) { GMutableSpan selection_curve = selection.span.slice(points_by_curve[curve_i]); - if (has_any_selected(selection_curve)) { + if (has_anything_selected(selection_curve)) { fill_selection_true(selection_curve); } } diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 114a5991bdc..ccbc69d55a6 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -92,7 +92,7 @@ bool has_anything_selected(const bke::CurvesGeometry &curves); /** * Return true if any element in the span is selected, on either domain with either type. */ -bool has_any_selected(const GSpan selection); +bool has_anything_selected(GSpan selection); /** * Find curves that have any point selected (a selection factor greater than zero), -- 2.30.2