Curves: Add select linked #104569

Merged
Falk David merged 5 commits from filedescriptor/blender:curves-select-linked into main 2023-02-10 18:58:10 +01:00
5 changed files with 80 additions and 25 deletions
Showing only changes of commit c7f1a1571b - Show all commits

View File

@ -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

View File

@ -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):

View File

@ -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<const Curves *>(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<const Curves *>(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<Curves *> 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<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
filedescriptor marked this conversation as resolved
Review

(I know, copied code) but the curves:: namespace specification shouldn't be necessary here, since we're already in that namespace.

(I know, copied code) but the `curves::` namespace specification shouldn't be necessary here, since we're already in that namespace.
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
Review

Might be nice to replace these three lines with a utility function like update_tag_selection_changed(C, curves_id), especially so that comment doesn't have to be repeated everywhere. A separate commit though obviously.

Might be nice to replace these three lines with a utility function like `update_tag_selection_changed(C, curves_id)`, especially so that comment doesn't have to be repeated everywhere. A separate commit though obviously.
Review

I can create a PR for this after, yes.

I can create a PR for this after, yes.
* 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";
filedescriptor marked this conversation as resolved
Review

Best to avoid reusing the word "linked" in the description. How about Select all points in curves with any point selection?

Best to avoid reusing the word "linked" in the description. How about `Select all points in curves with any point selection`?
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);
}

View File

@ -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<T> selection_typed = selection.span.typed<T>();
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<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>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
MutableSpan<T> selection_curve_typed = selection_typed.slice(points_by_curve[curve_i]);
if (selection_curve_typed.as_span().contains(T(1))) {
filedescriptor marked this conversation as resolved
Review

For proper float support, this should probably check for anything greater than 0, right?
I'd suggest separating that check to a small local function BTW as a way to give it a nice name and to make it easier to generalize in the future if we want to.

For proper float support, this should probably check for anything greater than 0, right? I'd suggest separating that check to a small local function BTW as a way to give it a nice name and to make it easier to generalize in the future if we want to.
selection_curve_typed.fill(T(1));
}
}
});
}
});
selection.finish();
}
void select_random(bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
uint32_t random_seed,

View File

@ -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.