Curves: Bezier handle selection support #119712

Merged
Jacques Lucke merged 28 commits from laurynas/blender:select-bezier-handles into main 2024-04-03 16:40:49 +02:00
Contributor

Adds ".selection_handle_left" and ".selection_handle_right" attributes to CurvesGeometry and their support in curve's select all, pick, box, circle and lasso select tools.

Adds ".selection_handle_left" and ".selection_handle_right" attributes to `CurvesGeometry` and their support in curve's select all, pick, box, circle and lasso select tools. <video src="/attachments/c763e101-02dd-4f9b-876c-6374dab06d2a" title="select.webm" controls></video>
Laurynas Duburas added 1 commit 2024-03-20 18:36:21 +01:00
Iliya Katushenock added this to the Nodes & Physics project 2024-03-20 18:37:14 +01:00
Iliya Katushenock added the
Module
Modeling
Interest
EEVEE & Viewport
labels 2024-03-20 18:37:21 +01:00
Laurynas Duburas added 1 commit 2024-03-22 17:44:13 +01:00
Laurynas Duburas added 1 commit 2024-03-22 18:58:23 +01:00
Laurynas Duburas changed title from WIP: Curves: Bezier handle selection support to Curves: Bezier handle selection support 2024-03-22 19:01:00 +01:00
Laurynas Duburas requested review from Jacques Lucke 2024-03-22 19:02:01 +01:00
Laurynas Duburas requested review from Hans Goudey 2024-03-22 19:02:01 +01:00
Laurynas Duburas added 2 commits 2024-03-23 21:35:39 +01:00
Member

Thanks for working on this!

  • It feels like SelectionAttributeList introduces unnecessary complexity. Couldn't there just be a function like get_curves_selection_attribute_names(const CurvesGeometry &curves) -> Span<std::string>?
  • Generally should avoid the use of c-style arrays. E.g. in Span<float3> positions_[3]. Often one can just use std::array instead.
  • I wonder if it's necessary for SelectableRangeList to be a class instead of just having its foreach methods as standalone functions.
  • Evaluated data shouldn't be changed by drawing code, but it looks like this is currently done in curves_draw.cc because it seems to remove the selection attributes. noticed that this is not actually drawing code but an operator
Thanks for working on this! * It feels like `SelectionAttributeList` introduces unnecessary complexity. Couldn't there just be a function like `get_curves_selection_attribute_names(const CurvesGeometry &curves) -> Span<std::string>`? * Generally should avoid the use of c-style arrays. E.g. in `Span<float3> positions_[3]`. Often one can just use `std::array` instead. * I wonder if it's necessary for `SelectableRangeList` to be a class instead of just having its `foreach` methods as standalone functions. * ~~Evaluated data shouldn't be changed by drawing code, but it looks like this is currently done in `curves_draw.cc` because it seems to remove the selection attributes.~~ noticed that this is not actually drawing code but an operator
Author
Contributor
  • It feels like SelectionAttributeList introduces unnecessary complexity. Couldn't there just be a function like get_curves_selection_attribute_names(const CurvesGeometry &curves) -> Span<std::string>?

Regarding SelectionAttributeList absolutely agree. I came with a SelectionAttributeWriterList, then separated SelectionAttributeList from it and missed the fact it could be just Span.
Still I'd rather go with get_curves_selection_attribute_ids(const CurvesGeometry &curves) -> Span<bke::AttributeIDRef>.
All selection functions take AttributeIDRef as an argument and it feels like it is more effective to have it ready.

I wonder if it's necessary for SelectableRangeList to be a class instead of just having its foreach methods as standalone functions.

In functions like select_box SelectableRangeList is used in a loop. Having class here helps to calculate bezier_curves_ only once. Another option is to pass them as a parameter, but this would separate logic that always go together.

> * It feels like `SelectionAttributeList` introduces unnecessary complexity. Couldn't there just be a function like `get_curves_selection_attribute_names(const CurvesGeometry &curves) -> Span<std::string>`? Regarding `SelectionAttributeList` absolutely agree. I came with a `SelectionAttributeWriterList`, then separated `SelectionAttributeList` from it and missed the fact it could be just `Span`. Still I'd rather go with `get_curves_selection_attribute_ids(const CurvesGeometry &curves) -> Span<bke::AttributeIDRef>`. All selection functions take `AttributeIDRef` as an argument and it feels like it is more effective to have it ready. >I wonder if it's necessary for SelectableRangeList to be a class instead of just having its foreach methods as standalone functions. In functions like `select_box` `SelectableRangeList` is used in a loop. Having class here helps to calculate `bezier_curves_` only once. Another option is to pass them as a parameter, but this would separate logic that always go together.
Author
Contributor

Maybe I hurried to answer regarding SelectableRangeList.

Maybe I hurried to answer regarding `SelectableRangeList`.
Laurynas Duburas added 2 commits 2024-03-25 20:25:08 +01:00
Laurynas Duburas added 1 commit 2024-03-25 20:42:13 +01:00
Laurynas Duburas added 1 commit 2024-03-25 20:57:47 +01:00
Jacques Lucke requested changes 2024-03-25 23:30:57 +01:00
@ -922,3 +924,1 @@
".selection", bke::AttrDomain::Curve);
selection.varray.set(curve_index, true);
selection.finish();
/* If Bezier curve is being added, loop through all three ids. */
Member
  • bezier should be lower case here, same below.
  • three shouldn't be mentioned here because it's a detail from somewhere else.
* `bezier` should be lower case here, same below. * `three` shouldn't be mentioned here because it's a detail from somewhere else.
Author
Contributor

Bezier is a proper noun, in wikipedia curves are referenced as "Bézier curves". Am I missing something?

"three" is decided exactly here. If we create bezier_as_nurbs, Bezier handles will be needed only if Bezier curves were already there. So selection_attribute_names is used as to reflects initial state.
If the Bezier curve has just been created, it is known for sure that attributes for Bezier handles are needed.
It must be confusing because those selection attributes we deleted on line 777. I'll explain in more detail in reply to another message.

Bezier is a proper noun, in wikipedia curves are referenced as "Bézier curves". Am I missing something? "three" is decided exactly here. If we create bezier_as_nurbs, Bezier handles will be needed only if Bezier curves were already there. So `selection_attribute_names` is used as to reflects initial state. If the Bezier curve has just been created, it is known for sure that attributes for Bezier handles are needed. It must be confusing because those selection attributes we deleted on line 777. I'll explain in more detail in reply to another message.
Member

Bezier is a proper noun, in wikipedia curves are referenced as "Bézier curves". Am I missing something?

Ok, either way is fine with me. Looks like we are inconsistent with that in Blender currently.

> Bezier is a proper noun, in wikipedia curves are referenced as "Bézier curves". Am I missing something? Ok, either way is fine with me. Looks like we are inconsistent with that in Blender currently.
JacquesLucke marked this conversation as resolved
@ -986,12 +1002,24 @@ static int curves_draw_exec(bContext *C, wmOperator *op)
selection.varray.set(curve_index, true);
selection.finish();
/* Creates ".selection_handle_left" and ".selection_handle_right" attributes, otherwise all
Member

Aren't those created further up already?

Aren't those created further up already?
Author
Contributor

No.
I think it is answered in the reply below.

No. I think it is answered in the reply below.
JacquesLucke marked this conversation as resolved
@ -988,1 +1004,4 @@
/* Creates ".selection_handle_left" and ".selection_handle_right" attributes, otherwise all
* existing Bezier handles would be treated as selected. */
for (const int i : selection_attribute_ids.index_range().drop_front(1)) {
Member

Weird knowledge of implementation details by calling drop_front(1).
Better just use the attribute names directly here and only use selection_attribute_ids when you actually want to refer to all selection attributes. Then those also don't have to mentioned in the comment.

Weird knowledge of implementation details by calling `drop_front(1)`. Better just use the attribute names directly here and only use `selection_attribute_ids` when you actually want to refer to all selection attributes. Then those also don't have to mentioned in the comment.
Author
Contributor

for loop's body here will be executed either twice or will be skipped. I prefer here for over if with the body repeated twice, to avoid later mistakes on changes. Furthermore with if same comment would be needed.

Intention from UI side is that user after drawing gets his new curve with all control points selected and control points of all other deselected. So to append new curve curves.resize() is called and my guess is that to avoid reallocating memory for .selection attribute and then cleaning values out, in f5adfa6 the attribute was removed attributes.remove(".selection");and latter added:

      bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
          ".selection", bke::AttrDomain::Curve);
      selection.varray.set(curve_index, true);
      selection.finish();

As I added two more attributes did the same the same with them. Maybe just didn't express right in the comment.

Also as writing of this I understood there is a small flaw where I deal with NURBS and Bezier cases. If NURBS curve is added and curves already had Bezier then code will mark left and right handles for the NURBS curve as selected. User will not see it, but logically it is a mistake.

`for` loop's body here will be executed either twice or will be skipped. I prefer here `for` over `if` with the body repeated twice, to avoid later mistakes on changes. Furthermore with `if` same comment would be needed. Intention from UI side is that user after drawing gets his new curve with all control points selected and control points of all other deselected. So to append new curve `curves.resize()` is called and my guess is that to avoid reallocating memory for `.selection` attribute and then cleaning values out, in f5adfa6 the attribute was removed `attributes.remove(".selection");`and latter added: ```c bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>( ".selection", bke::AttrDomain::Curve); selection.varray.set(curve_index, true); selection.finish(); ``` As I added two more attributes did the same the same with them. Maybe just didn't express right in the comment. Also as writing of this I understood there is a small flaw where I deal with NURBS and Bezier cases. If NURBS curve is added and curves already had Bezier then code will mark left and right handles for the NURBS curve as selected. User will not see it, but logically it is a mistake.
Author
Contributor

With get_curves_bezier_selection_attribute_names this one should look better, but the reasoning for whole for loop still stays deeper.

With `get_curves_bezier_selection_attribute_names` this one should look better, but the reasoning for whole `for` loop still stays deeper.
JacquesLucke marked this conversation as resolved
@ -50,6 +50,58 @@ float (*ED_curves_point_normals_array_create(const Curves *curves_id))[3];
namespace blender::ed::curves {
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids(const bke::CurvesGeometry &curves);
Member

I kind of think that this should be a Span<std::string>, just because it can be. It avoids that the reader has to consider what else it might be. I'd assume that this doesn't make using these functions any harder, but that can be tried.

I kind of think that this should be a `Span<std::string>`, just because it can be. It avoids that the reader has to consider what else it might be. I'd assume that this doesn't make using these functions any harder, but that can be tried.
Author
Contributor

I did it, but still doubt about it. For me it's easier to follow things when return type and parameter type matches without even implicit conversions.

Again just think about how much power will go to waste on conversion to AttributeIDRef every time artists selects curve control points all over the world. Think about pollution, about climate change. Think about whales!
It is not too late to revert it.

I did it, but still doubt about it. For me it's easier to follow things when return type and parameter type matches without even implicit conversions. Again just think about how much power will go to waste on conversion to `AttributeIDRef` every time artists selects curve control points all over the world. Think about pollution, about climate change. Think about whales! It is not too late to revert it.
laurynas marked this conversation as resolved
@ -51,2 +51,4 @@
namespace blender::ed::curves {
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids(const bke::CurvesGeometry &curves);
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids(const int size);
Member

This seems like a weird public function. It's not obvious what it does. Maybe it should be removed or at least be removed from the header?

This seems like a weird public function. It's not obvious what it does. Maybe it should be removed or at least be removed from the header?
laurynas marked this conversation as resolved
@ -52,1 +52,4 @@
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids(const bke::CurvesGeometry &curves);
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids(const int size);
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids();
Member

This functions should have comments describing what they return.

This functions should have comments describing what they return.
laurynas marked this conversation as resolved
@ -53,0 +54,4 @@
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids(const int size);
Span<bke::AttributeIDRef> get_curves_selection_attribute_ids();
class SelectionAttributeWriterList {
Member

I'm not sure this class is really needed either. Seems like just using Vector<bke::GSpanAttributeWriter> would work too. Maybe this can also use bke::SpanAttributeWriter<bool> instead, not sure if float selection may be allowed here.

I'm not sure this class is really needed either. Seems like just using `Vector<bke::GSpanAttributeWriter>` would work too. Maybe this can also use `bke::SpanAttributeWriter<bool>` instead, not sure if float selection may be allowed here.
Author
Contributor

not sure if float selection may be allowed here.

Use case. Create empty hair, go to sculpt mode, add some curves and Paint select something. Go to edit mode and box select. In select_box()>>fill_selection_false() condition selection.type().is<float>() is true.

In defense of SelectionAttributeWriterList I find destructor calling selection.finish() automatically most important.
It allows compact notations concentrated on the problem without an extra noise. Such as:

  for (bke::GSpanAttributeWriter &selection : 
    ed::curves::SelectionAttributeWriterList(curves, selection_domain))
  {
        ed::curves::fill_selection_false(selection.span);
  };

In methods like select_box, select_circle the class spares extra for loop (or util function) with finish(). Frees from necessity to remember it, also decide where to put it.

Off topic. I noticed that after drawing Bezier curve in "Edit mode" in "Empty Hair" selection mode in floats is lost.
Most likely because of deletion and recreation of selection attributes. So it also has to be rewritten with GSpanAttributeWriter.
Changes didn't help. Also started to doubt if I got design intentions right.

> not sure if float selection may be allowed here. Use case. Create empty hair, go to sculpt mode, add some curves and Paint select something. Go to edit mode and box select. In `select_box()>>fill_selection_false()` condition `selection.type().is<float>()` is `true`. In defense of `SelectionAttributeWriterList` I find destructor calling `selection.finish()` automatically most important. It allows compact notations concentrated on the problem without an extra noise. Such as: ```c for (bke::GSpanAttributeWriter &selection : ed::curves::SelectionAttributeWriterList(curves, selection_domain)) { ed::curves::fill_selection_false(selection.span); }; ``` In methods like `select_box`, `select_circle` the class spares extra `for` loop (or util function) with `finish()`. Frees from necessity to remember it, also decide where to put it. ~~Off topic. I noticed that after drawing Bezier curve in "Edit mode" in "Empty Hair" selection mode in `float`s is lost. Most likely because of deletion and recreation of selection attributes. So it also has to be rewritten with `GSpanAttributeWriter`.~~ Changes didn't help. Also started to doubt if I got design intentions right.
Member

In defense of SelectionAttributeWriterList I find destructor calling selection.finish() automatically most important.

Note that the call to .finish() is intentionally not part of the destructor. Calling it may invalidate some caches etc. in some cases which should happen at an explicit point in time.

> In defense of SelectionAttributeWriterList I find destructor calling selection.finish() automatically most important. Note that the call to `.finish()` is intentionally not part of the destructor. Calling it may invalidate some caches etc. in some cases which should happen at an explicit point in time.
JacquesLucke marked this conversation as resolved
@ -53,0 +63,4 @@
const bke::AttrDomain selection_domain);
~SelectionAttributeWriterList();
inline const Span<bke::AttributeIDRef> &attribute_ids() const
Member

No need for inline here. Methods defined inside in class scope are inline implicitly.

No need for `inline` here. Methods defined inside in class scope are `inline` implicitly.
laurynas marked this conversation as resolved
@ -53,0 +67,4 @@
{
return attribute_ids_;
}
inline const int size() const
Member

The const on values that are returned by value is meaningless.

The `const` on values that are returned by value is meaningless.
laurynas marked this conversation as resolved
@ -53,0 +90,4 @@
}
};
typedef std::function<void(
Member
  • Should probably use FunctionRef here.
  • Use using instead of typedef.
* Should probably use `FunctionRef` here. * Use `using` instead of `typedef`.
laurynas marked this conversation as resolved
@ -53,0 +91,4 @@
};
typedef std::function<void(
const IndexRange range, const Span<float3> positions, const int selection_attribute_index)>
Member
  • Not sure if selection_attribute_index is a good abstraction level here. It's certainly missing a comment, but may be worth trying to find alternatives that don't need this index.
  • The const in this declaration is meaningless.
* Not sure if `selection_attribute_index` is a good abstraction level here. It's certainly missing a comment, but may be worth trying to find alternatives that don't need this index. * The `const` in this declaration is meaningless.
laurynas marked this conversation as resolved
@ -53,0 +94,4 @@
const IndexRange range, const Span<float3> positions, const int selection_attribute_index)>
SelectableRangeConsumer;
void foreach_selectable_point_range(const bke::CurvesGeometry &curves,
Member

It's not really clear to me what a selected point range is based on this header. Some comment is necessary.

It's not really clear to me what a `selected point range` is based on this header. Some comment is necessary.
laurynas marked this conversation as resolved
@ -184,0 +240,4 @@
const eCustomDataType create_type,
const bke::AttributeIDRef &attribute_id = ".selection");
template<typename Fn>
Member

Does not seem likely that using a template parameter here instead of a FunctionRef has any benefits in practice.

Does not seem likely that using a template parameter here instead of a `FunctionRef` has any benefits in practice.
laurynas marked this conversation as resolved
Laurynas Duburas added 1 commit 2024-03-26 09:10:39 +01:00
Laurynas Duburas added 1 commit 2024-03-26 10:05:39 +01:00
Laurynas Duburas added 1 commit 2024-03-26 12:17:12 +01:00
Laurynas Duburas added 1 commit 2024-03-26 12:53:26 +01:00
Jacques Lucke reviewed 2024-03-26 14:53:47 +01:00
@ -776,2 +776,2 @@
attributes.remove(".selection");
Span<std::string> selection_attribute_names = get_curves_selection_attribute_names(curves);
for (const bke::AttributeIDRef selection_attribute_id : selection_attribute_names) {
Member

I think these loops should work like this: const StringRef selection_name : selection_attribute_names.
The conversion to AttributeIDRef is done implicitly in the call to attributes.remove.

The name selection_name should probably be used in other places too instead of selection_attribute_id.

I think these loops should work like this: `const StringRef selection_name : selection_attribute_names`. The conversion to `AttributeIDRef` is done implicitly in the call to `attributes.remove`. The name `selection_name` should probably be used in other places too instead of `selection_attribute_id`.
laurynas marked this conversation as resolved
@ -74,30 +76,140 @@ IndexMask retrieve_selected_points(const Curves &curves_id, IndexMaskMemory &mem
return retrieve_selected_points(curves, memory);
}
static const std::array<std::string, 3> selection_attribute_names_{
Member

Move this into get_curves_all_selection_attribute_names to avoid the static initialization order fiasco.

Move this into `get_curves_all_selection_attribute_names` to avoid the static initialization order fiasco.
laurynas marked this conversation as resolved
@ -77,0 +89,4 @@
Span<std::string> get_curves_all_selection_attribute_names()
{
return Span<std::string>(selection_attribute_names_.data(), selection_attribute_names_.size());
Member

std::array can be cast to Span implicitly.

`std::array` can be cast to `Span` implicitly.
laurynas marked this conversation as resolved
@ -77,0 +153,4 @@
range_consumer(curves.points_range(), positions[0], selection_attribute_names[0]);
if (selection_attribute_names.size() > 1) {
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
for (const int attribute_i : IndexRange(1, 2)) {
Member

It's less bad now than before because the order of selection attribute names is documented in the header, but this still feels very brittle. It's totally not obvious here what the 1 and 2 mean. May also be fine though, I don't have a better idea to structure this right now.

Also note that it may be better to use named constructors for creating such index masks nowadays: IndexRange::from_*.

It's less bad now than before because the order of selection attribute names is documented in the header, but this still feels very brittle. It's totally not obvious here what the 1 and 2 mean. May also be fine though, I don't have a better idea to structure this right now. Also note that it may be better to use named constructors for creating such index masks nowadays: `IndexRange::from_*`.
Author
Contributor

Added function get_curves_bezier_selection_attribute_names. I think it is good now.

Added function `get_curves_bezier_selection_attribute_names`. I think it is good now.
JacquesLucke marked this conversation as resolved
@ -53,0 +55,4 @@
* [".selection", ".selection_handle_left", ".selection_handle_right"] otherwise. */
Span<std::string> get_curves_selection_attribute_names(const bke::CurvesGeometry &curves);
/* Get all possible curve selection attribute names. */
Member

Comment style. Use /** when documenting symbols.

[Comment style](https://developer.blender.org/docs/handbook/guidelines/c_cpp/#comments). Use `/**` when documenting symbols.
laurynas marked this conversation as resolved
@ -53,0 +84,4 @@
return selections_[index];
}
bke::GSpanAttributeWriter &operator[](const std::string &attribute_name);
Member

operator[] generally shouldn't be used for such non-trivial operations. Better use a method with a name.
Also note that this should use StringRef instead of const std::string &.

`operator[]` generally shouldn't be used for such non-trivial operations. Better use a method with a name. Also note that this should use `StringRef` instead of `const std::string &`.
laurynas marked this conversation as resolved
@ -53,0 +100,4 @@
using SelectableRangeConsumer =
blender::FunctionRef<void(const IndexRange range,
const Span<float3> positions,
const std::string &selection_attribute_name)>;
Member

Instead of const std::string & we generally either use StringRefNull or StringRef.

Instead of `const std::string &` we generally either use `StringRefNull` or `StringRef`.
laurynas marked this conversation as resolved
@ -53,0 +102,4 @@
const Span<float3> positions,
const std::string &selection_attribute_name)>;
/**
* Traverses all ranges of control points posible select. Callback function is provided with a
Member

Typo (posible), same somewhere else.

Typo (`posible`), same somewhere else.
laurynas marked this conversation as resolved
Laurynas Duburas added 1 commit 2024-03-26 21:12:25 +01:00
Laurynas Duburas added 1 commit 2024-03-26 21:17:50 +01:00
Laurynas Duburas added 2 commits 2024-03-29 13:51:28 +01:00
Laurynas Duburas requested review from Jacques Lucke 2024-03-29 13:51:52 +01:00
Laurynas Duburas added 1 commit 2024-03-29 18:56:39 +01:00
Laurynas Duburas added 1 commit 2024-03-30 00:15:53 +01:00
Laurynas Duburas added 1 commit 2024-03-30 17:47:31 +01:00
8fa41ee4e7 Fix crash on select in empty curve
Removing small optimization to reduce complexity, because even assert for it caused crashes.
Jacques Lucke approved these changes 2024-04-01 13:05:30 +02:00
Jacques Lucke left a comment
Member

Sorry for the late reply, was a bit busy this week. Got a few more comments, but overall looks good to me now.

Sorry for the late reply, was a bit busy this week. Got a few more comments, but overall looks good to me now.
@ -826,3 +828,2 @@
}
BKE_id_attributes_active_set(&curves_id->id, active_attribute.c_str());
if (layer) {
Member

The pointer to the CustomDataLayer is unfortunately not stable when attributes are added/removed. So layer may be dangling here, so it's probably better not to use it but to check for active_attribute instead.

The pointer to the `CustomDataLayer` is unfortunately not stable when attributes are added/removed. So `layer` may be dangling here, so it's probably better not to use it but to check for `active_attribute` instead.
laurynas marked this conversation as resolved
@ -77,0 +110,4 @@
}
}
inline MutableSpan<bke::GSpanAttributeWriter> init_selection_writers(
Member

inline shouldn't be used here.

Also, could this maybe just return the Vector instead of returning a span into the passed in vector?

`inline` shouldn't be used here. Also, could this maybe just return the `Vector` instead of returning a span into the passed in vector?
laurynas marked this conversation as resolved
@ -78,2 +224,3 @@
const bke::AttrDomain selection_domain,
const eCustomDataType create_type)
const eCustomDataType create_type,
const bke::AttributeIDRef &attribute_id)
Member

rename attribute_id to selection_name or so in a few places, because that conveys more semantic information.

rename `attribute_id` to `selection_name` or so in a few places, because that conveys more semantic information.
laurynas marked this conversation as resolved
@ -53,0 +88,4 @@
SelectableRangeConsumer range_consumer);
/**
* Same logic as in foreach_selectable_point_range, just ranges reference curves insted of
Member

typo (instead)

typo (`instead`)
laurynas marked this conversation as resolved
@ -184,0 +244,4 @@
bke::GSpanAttributeWriter &selection_attribute_writer_by_name(
MutableSpan<bke::GSpanAttributeWriter> selections, StringRef attribute_name);
inline void foreach_selection_attribute_writer(
Member

The function implementation can be moved to a .cc file.

The function implementation can be moved to a `.cc` file.
laurynas marked this conversation as resolved
Laurynas Duburas added 1 commit 2024-04-01 13:16:09 +02:00
Laurynas Duburas added 1 commit 2024-04-01 13:39:13 +02:00
Jacques Lucke reviewed 2024-04-01 18:16:08 +02:00
@ -64,1 +64,3 @@
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves,
IndexMaskMemory &memory,
const std::string &attribute_name)
Member

Generally use StringRef or StringRefNull instead of attribute_name.

Generally use `StringRef` or `StringRefNull` instead of `attribute_name`.
Author
Contributor

Sorry, I was in a hurry. You told me this already.

Sorry, I was in a hurry. You told me this already.
laurynas marked this conversation as resolved
Hans Goudey requested changes 2024-04-01 18:28:11 +02:00
Dismissed
Hans Goudey left a comment
Member

Thanks for working on this. Big picture it looks reasonable. Just left some smaller cleanup comments.

Thanks for working on this. Big picture it looks reasonable. Just left some smaller cleanup comments.
@ -164,3 +163,1 @@
".selection", bke::AttrDomain::Point);
selection.span.take_back(num_points_to_add).fill(true);
selection.finish();
for (const StringRef selection_name : ed::curves::get_curves_selection_attribute_names(curves)) {
Member

We're already in the ed::curves namespace here, no need to write that again

We're already in the `ed::curves` namespace here, no need to write that again
laurynas marked this conversation as resolved
@ -63,2 +63,3 @@
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves,
IndexMaskMemory &memory,
Member

Also the non-const memory argument can be last

Also the non-const `memory` argument can be last
Author
Contributor

It has default value for the attribute_name in a header. Should I add third version of retrieve_selected_points?

It has default value for the `attribute_name` in a header. Should I add third version of `retrieve_selected_points`?
Member

Oh, my bad. I don't have a strong opinion, but a third overload does sound a bit nicer.

Oh, my bad. I don't have a strong opinion, but a third overload does sound a bit nicer.
laurynas marked this conversation as resolved
@ -77,0 +85,4 @@
selection_attribute_names;
}
Span<std::string> get_curves_all_selection_attribute_names()
Member

How about Span<StringRef>, since these strings have static ownership?

How about `Span<StringRef>`, since these strings have static ownership?
Author
Contributor

I'm doing it, but just for the future: what is the benefit?

I'm doing it, but just for the future: what is the benefit?
Member

Thanks for asking! Constructing StringRef from a static string is "free", whereas constructing a std::string will have to allocate heap memory if the inline buffer (usually 11-14 bytes) isn't large enough. Also, just in general, using reference types like Span is preferable to owning types like Array) since they give more flexibility about data ownership, etc.

Thanks for asking! Constructing `StringRef` from a static string is "free", whereas constructing a `std::string` will have to allocate heap memory if the inline buffer (usually 11-14 bytes) isn't large enough. Also, just in general, using reference types like `Span` is preferable to owning types like `Array`) since they give more flexibility about data ownership, etc.
laurynas marked this conversation as resolved
@ -77,0 +110,4 @@
}
}
Vector<bke::GSpanAttributeWriter> &init_selection_writers(
Member

It seems unnecessary to return a reference to the vector here, and maybe a bit more confusing than necessary?

Or the argument could be removed and it could just be a return value?

It seems unnecessary to return a reference to the vector here, and maybe a bit more confusing than necessary? Or the argument could be removed and it could just be a return value?
Author
Contributor

Removed argument, but now it is tempting to use a one-liner:

MutableSpan<bke::GSpanAttributeWriter> selections = init_selection_writers(curves, selection_domain);

Returning by value looks dangerous to me.

Removed argument, but now it is tempting to use a one-liner: ```c MutableSpan<bke::GSpanAttributeWriter> selections = init_selection_writers(curves, selection_domain); ``` Returning by value looks dangerous to me.
Member

I think that's just something people have to know about spans and vectors. There are so many places you could make the same mistake, and using return values is very nice from a code readability perspective.

I think that's just something people have to know about spans and vectors. There are so many places you could make the same mistake, and using return values is very nice from a code readability perspective.
laurynas marked this conversation as resolved
@ -77,0 +124,4 @@
return writers;
}
inline void finish_attribute_writers(MutableSpan<bke::GSpanAttributeWriter> attribute_writers)
Member

Not sure these need to be inline, that could change to static

Not sure these need to be inline, that could change to `static`
laurynas marked this conversation as resolved
@ -77,0 +157,4 @@
MutableSpan<bke::GSpanAttributeWriter> selection_writers = init_selection_writers(
writers_buffer, curves, selection_domain);
/* TODO: maybe add threading */
Member

I'd remove this todo comment. This would only ever be able to use 3 threads, and there are potential pitfalls that probably wouldn't be worth it

I'd remove this todo comment. This would only ever be able to use 3 threads, and there are potential pitfalls that probably wouldn't be worth it
laurynas marked this conversation as resolved
@ -53,0 +73,4 @@
const Span<std::string> selection_attribute_names =
get_curves_all_selection_attribute_names());
using SelectableRangeConsumer = blender::FunctionRef<void(
Member

This name feels a bit off to me. It seems to imply that it's some object oriented thing when it's really just a callback function. How about SelectionRangeFn or something?

Also, const can be removed in this declaration, and blender:: can be removed too, we're already in that namespace

This name feels a bit off to me. It seems to imply that it's some object oriented thing when it's really just a callback function. How about `SelectionRangeFn` or something? Also, `const` can be removed in this declaration, and `blender::` can be removed too, we're already in that namespace
laurynas marked this conversation as resolved
@ -184,0 +231,4 @@
bke::GSpanAttributeWriter ensure_selection_attribute(
bke::CurvesGeometry &curves,
const bke::AttrDomain selection_domain,
const eCustomDataType create_type,
Member

const is meaningless for types passed by value in declarations. Same in a few other functions in this header now.

`const` is meaningless for types passed by value in declarations. Same in a few other functions in this header now.
Author
Contributor

I like to put const everywhere I can as a quick hint for eyes, that variable is not intended to change. Maybe it could save from faulty using it as reference.
But don't have deep attachment to this. Fixed.

I like to put `const` everywhere I can as a quick hint for eyes, that variable is not intended to change. Maybe it could save from faulty using it as reference. But don't have deep attachment to this. Fixed.
laurynas marked this conversation as resolved
@ -184,0 +232,4 @@
bke::CurvesGeometry &curves,
const bke::AttrDomain selection_domain,
const eCustomDataType create_type,
const std::string &attribute_name = ".selection");
Member

const std::string & -> StringRef

`const std::string &` -> `StringRef`
laurynas marked this conversation as resolved
Laurynas Duburas added 3 commits 2024-04-01 20:24:47 +02:00
Laurynas Duburas added 1 commit 2024-04-01 21:12:06 +02:00
Laurynas Duburas requested review from Hans Goudey 2024-04-02 06:37:17 +02:00
Hans Goudey requested changes 2024-04-02 13:53:39 +02:00
Dismissed
@ -77,0 +156,4 @@
bke::AttrDomain selection_domain,
blender::FunctionRef<void(bke::GSpanAttributeWriter &selection)> fn)
{
Vector<bke::GSpanAttributeWriter> writers_buffer = init_selection_writers(curves,
Member

You can rely on the implicit conversion from Vector to MutableSpan when calling functions. That should make having the separate Vector and MutableSpan variables unnecessary everywhere (so here, writers_buffer is renamed selection_writers and the original selection_writers is removed).

You can rely on the implicit conversion from Vector to MutableSpan when calling functions. That should make having the separate Vector and MutableSpan variables unnecessary everywhere (so here, `writers_buffer` is renamed `selection_writers` and the original `selection_writers` is removed).
Author
Contributor

That is what I was talking about yesterday :)
In that case Vector would be temporary (or r-value) and it's destructor will get called just before leaving expression. The Span will be left initialized with invalid pointer.

I hold myself yesterday from noting that though it's an easy thing to remember, but API should pass a much as possible things to compiler to worry about. There is enough things to keep in a head.

That is what I was talking about yesterday :) In that case `Vector` would be temporary (or r-value) and it's destructor will get called just before leaving expression. The `Span` will be left initialized with invalid pointer. I hold myself yesterday from noting that though it's an easy thing to remember, but API should pass a much as possible things to compiler to worry about. There is enough things to keep in a head.
Author
Contributor

OK. Unless you suggest removing Span variable completely.
In that case I don't like the fact that in functions like select_box conversion to Span will happen several times.
I prefer getting Span once and then dealing with it as pleased.

OK. Unless you suggest removing `Span` variable completely. In that case I don't like the fact that in functions like `select_box` conversion to `Span` will happen several times. I prefer getting `Span` once and then dealing with it as pleased.
Member

In that case I don't like the fact that in functions like select_box conversion to Span will happen several times.

Converting to Span is free, no need to consider that from a performance perspective.

Duplicating all these variables just seems messy and gets in the way TBH

> In that case I don't like the fact that in functions like select_box conversion to Span will happen several times. Converting to Span is free, no need to consider that from a performance perspective. Duplicating all these variables just seems messy and gets in the way TBH
Laurynas Duburas added 1 commit 2024-04-02 18:45:49 +02:00
Hans Goudey approved these changes 2024-04-02 18:47:25 +02:00
Author
Contributor

Don't merge it yet. It crashes on Release build.

Don't merge it yet. It crashes on Release build.
Laurynas Duburas changed title from Curves: Bezier handle selection support to WIP: Curves: Bezier handle selection support 2024-04-02 18:59:23 +02:00

Crash in release, but not in debug? Are you on MSVC?

Crash in release, but not in debug? Are you on MSVC?
Author
Contributor

Crash in release, but not in debug? Do you on MSVC?

MacOS, VSCode.

Don't know if it is same thing, but I was cheking #117709 on XCode.
Maybe this PR has nothing to with the crash. I usually don't check Relase build.

> Crash in release, but not in debug? Do you on MSVC? MacOS, VSCode. Don't know if it is same thing, but I was cheking https://projects.blender.org/blender/blender/issues/117709 on XCode. Maybe this PR has nothing to with the crash. I usually don't check Relase build.
Laurynas Duburas reviewed 2024-04-02 21:14:45 +02:00
@ -3129,1 +3112,3 @@
new_closest.curves_id = &curves_id;
const IndexMask elements(curves.attributes().domain_size(selection_domain));
const ed::curves::SelectionRangeFn range_consumer =
[&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
Author
Contributor

Problem is with this variable, captures by reference go crazy in Release build.
Tried to play by passing one by one, only captures by value work well.

If to define type SelectionRangeFn as:

using SelectionRangeFn = std::function<void(
    IndexRange range, Span<float3> positions, StringRef selection_attribute_name)>;

instead of using FunctionRef everything works fine.

Problem is with this variable, captures by reference go crazy in `Release` build. Tried to play by passing one by one, only captures by value work well. If to define type `SelectionRangeFn` as: ```c using SelectionRangeFn = std::function<void( IndexRange range, Span<float3> positions, StringRef selection_attribute_name)>; ``` instead of using `FunctionRef` everything works fine.
Member

Ah right, you can't assign a lambda to a FunctionRef directly (mentioned in the description of FunctionRef I think).
Just use auto range_consumer = [&]...;

Ah right, you can't assign a lambda to a `FunctionRef` directly (mentioned in the description of `FunctionRef` I think). Just use `auto range_consumer = [&]...;`
Laurynas Duburas added 1 commit 2024-04-02 21:26:12 +02:00
buildbot/vexp-code-patch-lint Build done. Details
buildbot/vexp-code-patch-linux-x86_64 Build done. Details
buildbot/vexp-code-patch-darwin-x86_64 Build done. Details
buildbot/vexp-code-patch-darwin-arm64 Build done. Details
buildbot/vexp-code-patch-windows-amd64 Build done. Details
buildbot/vexp-code-patch-coordinator Build done. Details
612977f854
FunctionRef to auto for variable type
Laurynas Duburas changed title from WIP: Curves: Bezier handle selection support to Curves: Bezier handle selection support 2024-04-02 21:29:22 +02:00
Member

@blender-bot build

@blender-bot build
Member

Can you add some more information to the patch description?

Can you add some more information to the patch description?
Author
Contributor

Can you add some more information to the patch description?

Just checked, indeed little provided :)

> Can you add some more information to the patch description? Just checked, indeed little provided :)
Jacques Lucke merged commit 4889aed8ac into main 2024-04-03 16:40:49 +02:00
Laurynas Duburas deleted branch select-bezier-handles 2024-04-03 16:44:18 +02:00
Sign in to join this conversation.
No reviewers
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No Assignees
4 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#119712
No description provided.