Curves: Bezier handle selection support
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. Pull Request: https://projects.blender.org/blender/blender/pulls/119712
This commit is contained in:
committed by
Jacques Lucke
parent
20e07f0102
commit
4889aed8ac
@@ -407,9 +407,9 @@ static void create_edit_points_selection(const bke::CurvesGeometry &curves,
|
||||
}
|
||||
|
||||
const VArray<float> attribute_left = *curves.attributes().lookup_or_default<float>(
|
||||
".selection_handle_left", bke::AttrDomain::Point, 0.0f);
|
||||
".selection_handle_left", bke::AttrDomain::Point, 1.0f);
|
||||
const VArray<float> attribute_right = *curves.attributes().lookup_or_default<float>(
|
||||
".selection_handle_right", bke::AttrDomain::Point, 0.0f);
|
||||
".selection_handle_right", bke::AttrDomain::Point, 1.0f);
|
||||
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
|
||||
|
||||
@@ -773,8 +773,8 @@ static int curves_draw_exec(bContext *C, wmOperator *op)
|
||||
(cps->radius_taper_end != 0.0f));
|
||||
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
attributes.remove(".selection");
|
||||
Span<StringRef> selection_attribute_names = get_curves_selection_attribute_names(curves);
|
||||
remove_selection_attributes(attributes, selection_attribute_names);
|
||||
|
||||
if (cdd->curve_type == CU_BEZIER) {
|
||||
/* Allow to interpolate multiple channels */
|
||||
@@ -918,10 +918,19 @@ static int curves_draw_exec(bContext *C, wmOperator *op)
|
||||
curves.nurbs_orders_for_write()[curve_index] = order;
|
||||
curves.fill_curve_types(IndexRange(curve_index, 1), curve_type);
|
||||
|
||||
bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
|
||||
".selection", bke::AttrDomain::Curve);
|
||||
selection.varray.set(curve_index, true);
|
||||
selection.finish();
|
||||
/* If Bezier curve is being added, loop through all three names, otherwise through ones in
|
||||
* `selection_attribute_names`. */
|
||||
for (const StringRef selection_name :
|
||||
(bezier_as_nurbs ? selection_attribute_names :
|
||||
get_curves_all_selection_attribute_names()))
|
||||
{
|
||||
bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
|
||||
selection_name, bke::AttrDomain::Curve);
|
||||
if (selection_name == ".selection" || !bezier_as_nurbs) {
|
||||
selection.varray.set(curve_index, true);
|
||||
}
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
if (attributes.contains("resolution")) {
|
||||
curves.resolution_for_write()[curve_index] = 12;
|
||||
@@ -935,13 +944,21 @@ static int curves_draw_exec(bContext *C, wmOperator *op)
|
||||
"handle_type_left",
|
||||
"handle_type_right",
|
||||
"nurbs_weight",
|
||||
".selection"},
|
||||
".selection",
|
||||
".selection_handle_left",
|
||||
".selection_handle_right"},
|
||||
curves.points_by_curve()[curve_index]);
|
||||
bke::fill_attribute_range_default(
|
||||
attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{"curve_type", "resolution", "cyclic", "nurbs_order", "knots_mode", ".selection"},
|
||||
IndexRange(curve_index, 1));
|
||||
bke::fill_attribute_range_default(attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{"curve_type",
|
||||
"resolution",
|
||||
"cyclic",
|
||||
"nurbs_order",
|
||||
"knots_mode",
|
||||
".selection",
|
||||
".selection_handle_left",
|
||||
".selection_handle_right"},
|
||||
IndexRange(curve_index, 1));
|
||||
}
|
||||
|
||||
if (corners_index) {
|
||||
@@ -986,12 +1003,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
|
||||
* existing Bezier handles would be treated as selected. */
|
||||
for (const StringRef selection_name : get_curves_bezier_selection_attribute_names(curves)) {
|
||||
bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
|
||||
selection_name, bke::AttrDomain::Curve);
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
bke::fill_attribute_range_default(
|
||||
attributes, bke::AttrDomain::Point, {"position", "radius", ".selection"}, new_points);
|
||||
bke::fill_attribute_range_default(attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{"curve_type", ".selection"},
|
||||
IndexRange(curve_index, 1));
|
||||
attributes,
|
||||
bke::AttrDomain::Point,
|
||||
{"position", "radius", ".selection", ".selection_handle_left", ".selection_handle_right"},
|
||||
new_points);
|
||||
bke::fill_attribute_range_default(
|
||||
attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{"curve_type", ".selection", ".selection_handle_left", ".selection_handle_right"},
|
||||
IndexRange(curve_index, 1));
|
||||
}
|
||||
|
||||
if (is_cyclic) {
|
||||
|
||||
@@ -105,7 +105,7 @@ void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
/* Delete selection attribute so that it will not have to be resized. */
|
||||
attributes.remove(".selection");
|
||||
remove_selection_attributes(attributes);
|
||||
|
||||
curves.resize(old_points_num + num_points_to_add, old_curves_num + num_curves_to_add);
|
||||
|
||||
@@ -160,10 +160,12 @@ void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
curves.update_curve_types();
|
||||
curves.tag_topology_changed();
|
||||
|
||||
bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".selection", bke::AttrDomain::Point);
|
||||
selection.span.take_back(num_points_to_add).fill(true);
|
||||
selection.finish();
|
||||
for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
|
||||
bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
selection_name, bke::AttrDomain::Point);
|
||||
selection.span.take_back(num_points_to_add).fill(true);
|
||||
selection.finish();
|
||||
}
|
||||
}
|
||||
|
||||
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
@@ -173,7 +175,7 @@ void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
/* Delete selection attribute so that it will not have to be resized. */
|
||||
attributes.remove(".selection");
|
||||
remove_selection_attributes(attributes);
|
||||
|
||||
/* Resize the curves and copy the offsets of duplicated curves into the new offsets. */
|
||||
curves.resize(curves.points_num(), orig_curves_num + mask.size());
|
||||
@@ -215,10 +217,12 @@ void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
curves.update_curve_types();
|
||||
curves.tag_topology_changed();
|
||||
|
||||
bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".selection", bke::AttrDomain::Curve);
|
||||
selection.span.take_back(mask.size()).fill(true);
|
||||
selection.finish();
|
||||
for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
|
||||
bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
selection_name, bke::AttrDomain::Curve);
|
||||
selection.span.take_back(mask.size()).fill(true);
|
||||
selection.finish();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::curves
|
||||
|
||||
@@ -806,26 +806,29 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
|
||||
*
|
||||
* This would be unnecessary if the active attribute were stored as a string on the ID. */
|
||||
std::string active_attribute;
|
||||
if (const CustomDataLayer *layer = BKE_id_attributes_active_get(&curves_id->id)) {
|
||||
const CustomDataLayer *layer = BKE_id_attributes_active_get(&curves_id->id);
|
||||
if (layer) {
|
||||
active_attribute = layer->name;
|
||||
}
|
||||
for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
|
||||
if (const GVArray src = *attributes.lookup(selection_name, domain)) {
|
||||
const CPPType &type = src.type();
|
||||
void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
|
||||
src.materialize(dst);
|
||||
|
||||
if (const GVArray src = *attributes.lookup(".selection", domain)) {
|
||||
const CPPType &type = src.type();
|
||||
void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
|
||||
src.materialize(dst);
|
||||
|
||||
attributes.remove(".selection");
|
||||
if (!attributes.add(".selection",
|
||||
domain,
|
||||
bke::cpp_type_to_custom_data_type(type),
|
||||
bke::AttributeInitMoveArray(dst)))
|
||||
{
|
||||
MEM_freeN(dst);
|
||||
attributes.remove(selection_name);
|
||||
if (!attributes.add(selection_name,
|
||||
domain,
|
||||
bke::cpp_type_to_custom_data_type(type),
|
||||
bke::AttributeInitMoveArray(dst)))
|
||||
{
|
||||
MEM_freeN(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_id_attributes_active_set(&curves_id->id, active_attribute.c_str());
|
||||
if (!active_attribute.empty()) {
|
||||
BKE_id_attributes_active_set(&curves_id->id, active_attribute.c_str());
|
||||
}
|
||||
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
||||
* attribute for now. */
|
||||
|
||||
@@ -62,9 +62,16 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, IndexMaskMemory &mem
|
||||
}
|
||||
|
||||
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
|
||||
{
|
||||
return retrieve_selected_points(curves, ".selection", memory);
|
||||
}
|
||||
|
||||
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves,
|
||||
StringRef attribute_name,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return IndexMask::from_bools(
|
||||
*curves.attributes().lookup_or_default<bool>(".selection", bke::AttrDomain::Point, true),
|
||||
*curves.attributes().lookup_or_default<bool>(attribute_name, bke::AttrDomain::Point, true),
|
||||
memory);
|
||||
}
|
||||
|
||||
@@ -74,30 +81,187 @@ IndexMask retrieve_selected_points(const Curves &curves_id, IndexMaskMemory &mem
|
||||
return retrieve_selected_points(curves, memory);
|
||||
}
|
||||
|
||||
Span<StringRef> get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
|
||||
{
|
||||
static const std::array<StringRef, 1> selection_attribute_names{".selection"};
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
return (attributes.contains("handle_type_left") && attributes.contains("handle_type_right")) ?
|
||||
get_curves_all_selection_attribute_names() :
|
||||
selection_attribute_names;
|
||||
}
|
||||
|
||||
Span<StringRef> get_curves_all_selection_attribute_names()
|
||||
{
|
||||
static const std::array<StringRef, 3> selection_attribute_names{
|
||||
".selection", ".selection_handle_left", ".selection_handle_right"};
|
||||
return selection_attribute_names;
|
||||
}
|
||||
|
||||
Span<StringRef> get_curves_bezier_selection_attribute_names(const bke::CurvesGeometry &curves)
|
||||
{
|
||||
static const std::array<StringRef, 2> selection_attribute_names{".selection_handle_left",
|
||||
".selection_handle_right"};
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
return (attributes.contains("handle_type_left") && attributes.contains("handle_type_right")) ?
|
||||
selection_attribute_names :
|
||||
Span<StringRef>();
|
||||
}
|
||||
|
||||
void remove_selection_attributes(bke::MutableAttributeAccessor &attributes,
|
||||
Span<StringRef> selection_attribute_names)
|
||||
{
|
||||
for (const StringRef selection_name : selection_attribute_names) {
|
||||
attributes.remove(selection_name);
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<bke::GSpanAttributeWriter> init_selection_writers(bke::CurvesGeometry &curves,
|
||||
bke::AttrDomain selection_domain)
|
||||
{
|
||||
const eCustomDataType create_type = CD_PROP_BOOL;
|
||||
Span<StringRef> selection_attribute_names = get_curves_selection_attribute_names(curves);
|
||||
Vector<bke::GSpanAttributeWriter> writers;
|
||||
for (const int i : selection_attribute_names.index_range()) {
|
||||
writers.append(ensure_selection_attribute(
|
||||
curves, selection_domain, create_type, selection_attribute_names[i]));
|
||||
};
|
||||
return writers;
|
||||
}
|
||||
|
||||
static void finish_attribute_writers(MutableSpan<bke::GSpanAttributeWriter> attribute_writers)
|
||||
{
|
||||
for (auto &attribute_writer : attribute_writers) {
|
||||
attribute_writer.finish();
|
||||
}
|
||||
}
|
||||
|
||||
static bke::GSpanAttributeWriter &selection_attribute_writer_by_name(
|
||||
MutableSpan<bke::GSpanAttributeWriter> selections, StringRef attribute_name)
|
||||
{
|
||||
Span<StringRef> selection_attribute_names = get_curves_all_selection_attribute_names();
|
||||
|
||||
BLI_assert(selection_attribute_names.contains(attribute_name));
|
||||
|
||||
for (const int index : selections.index_range()) {
|
||||
if (attribute_name.size() == selection_attribute_names[index].size()) {
|
||||
return selections[index];
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return selections[0];
|
||||
}
|
||||
|
||||
void foreach_selection_attribute_writer(
|
||||
bke::CurvesGeometry &curves,
|
||||
bke::AttrDomain selection_domain,
|
||||
blender::FunctionRef<void(bke::GSpanAttributeWriter &selection)> fn)
|
||||
{
|
||||
Vector<bke::GSpanAttributeWriter> selection_writers = init_selection_writers(curves,
|
||||
selection_domain);
|
||||
for (bke::GSpanAttributeWriter &selection_writer : selection_writers) {
|
||||
fn(selection_writer);
|
||||
}
|
||||
finish_attribute_writers(selection_writers);
|
||||
}
|
||||
|
||||
static void init_selectable_foreach(const bke::CurvesGeometry &curves,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
Span<StringRef> &r_bezier_attribute_names,
|
||||
Span<float3> &r_positions,
|
||||
std::array<Span<float3>, 2> &r_bezier_handle_positions,
|
||||
IndexMaskMemory &r_memory,
|
||||
IndexMask &r_bezier_curves)
|
||||
{
|
||||
r_bezier_attribute_names = get_curves_bezier_selection_attribute_names(curves);
|
||||
r_positions = deformation.positions;
|
||||
if (r_bezier_attribute_names.size() > 0) {
|
||||
r_bezier_handle_positions[0] = curves.handle_positions_left();
|
||||
r_bezier_handle_positions[1] = curves.handle_positions_right();
|
||||
r_bezier_curves = curves.indices_for_curve_type(CURVE_TYPE_BEZIER, r_memory);
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_selectable_point_range(const bke::CurvesGeometry &curves,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
SelectionRangeFn range_consumer)
|
||||
{
|
||||
Span<StringRef> bezier_attribute_names;
|
||||
Span<float3> positions;
|
||||
std::array<Span<float3>, 2> bezier_handle_positions;
|
||||
IndexMaskMemory memory;
|
||||
IndexMask bezier_curves;
|
||||
init_selectable_foreach(curves,
|
||||
deformation,
|
||||
bezier_attribute_names,
|
||||
positions,
|
||||
bezier_handle_positions,
|
||||
memory,
|
||||
bezier_curves);
|
||||
|
||||
range_consumer(curves.points_range(), positions, ".selection");
|
||||
|
||||
OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
for (const int attribute_i : bezier_attribute_names.index_range()) {
|
||||
bezier_curves.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
|
||||
range_consumer(points_by_curve[curve_i],
|
||||
bezier_handle_positions[attribute_i],
|
||||
bezier_attribute_names[attribute_i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_selectable_curve_range(const bke::CurvesGeometry &curves,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
SelectionRangeFn range_consumer)
|
||||
{
|
||||
Span<StringRef> bezier_attribute_names;
|
||||
Span<float3> positions;
|
||||
std::array<Span<float3>, 2> bezier_handle_positions;
|
||||
IndexMaskMemory memory;
|
||||
IndexMask bezier_curves;
|
||||
init_selectable_foreach(curves,
|
||||
deformation,
|
||||
bezier_attribute_names,
|
||||
positions,
|
||||
bezier_handle_positions,
|
||||
memory,
|
||||
bezier_curves);
|
||||
|
||||
range_consumer(curves.curves_range(), positions, ".selection");
|
||||
|
||||
for (const int attribute_i : bezier_attribute_names.index_range()) {
|
||||
bezier_curves.foreach_range([&](const IndexRange curves_range) {
|
||||
range_consumer(
|
||||
curves_range, bezier_handle_positions[attribute_i], bezier_attribute_names[attribute_i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves,
|
||||
const bke::AttrDomain selection_domain,
|
||||
const eCustomDataType create_type)
|
||||
bke::AttrDomain selection_domain,
|
||||
eCustomDataType create_type,
|
||||
StringRef attribute_name)
|
||||
{
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
if (attributes.contains(".selection")) {
|
||||
bke::GSpanAttributeWriter selection_attr = attributes.lookup_for_write_span(".selection");
|
||||
if (attributes.contains(attribute_name)) {
|
||||
bke::GSpanAttributeWriter selection_attr = attributes.lookup_for_write_span(attribute_name);
|
||||
/* Check domain type. */
|
||||
if (selection_attr.domain == selection_domain) {
|
||||
return selection_attr;
|
||||
}
|
||||
selection_attr.finish();
|
||||
attributes.remove(".selection");
|
||||
attributes.remove(attribute_name);
|
||||
}
|
||||
const int domain_size = attributes.domain_size(selection_domain);
|
||||
switch (create_type) {
|
||||
case CD_PROP_BOOL:
|
||||
attributes.add(".selection",
|
||||
attributes.add(attribute_name,
|
||||
selection_domain,
|
||||
CD_PROP_BOOL,
|
||||
bke::AttributeInitVArray(VArray<bool>::ForSingle(true, domain_size)));
|
||||
break;
|
||||
case CD_PROP_FLOAT:
|
||||
attributes.add(".selection",
|
||||
attributes.add(attribute_name,
|
||||
selection_domain,
|
||||
CD_PROP_FLOAT,
|
||||
bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, domain_size)));
|
||||
@@ -105,7 +269,7 @@ bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
return attributes.lookup_for_write_span(".selection");
|
||||
return attributes.lookup_for_write_span(attribute_name);
|
||||
}
|
||||
|
||||
void fill_selection_false(GMutableSpan selection)
|
||||
@@ -239,6 +403,17 @@ bool has_anything_selected(const bke::CurvesGeometry &curves)
|
||||
return !selection || contains(selection, selection.index_range(), true);
|
||||
}
|
||||
|
||||
bool has_anything_selected(const bke::CurvesGeometry &curves, bke::AttrDomain selection_domain)
|
||||
{
|
||||
for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
|
||||
const VArray<bool> selection = *curves.attributes().lookup<bool>(selection_name,
|
||||
selection_domain);
|
||||
if (!selection || contains(selection, selection.index_range(), true))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_anything_selected(const bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
{
|
||||
const VArray<bool> selection = *curves.attributes().lookup<bool>(".selection");
|
||||
@@ -300,29 +475,29 @@ void select_all(bke::CurvesGeometry &curves,
|
||||
const bke::AttrDomain selection_domain,
|
||||
int action)
|
||||
{
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
if (action == SEL_SELECT) {
|
||||
std::optional<IndexRange> range = mask.to_range();
|
||||
if (range.has_value() &&
|
||||
(*range == IndexRange(curves.attributes().domain_size(selection_domain))))
|
||||
{
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
/* As an optimization, just remove the selection attributes when everything is selected. */
|
||||
attributes.remove(".selection");
|
||||
remove_selection_attributes(attributes);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
||||
curves, selection_domain, CD_PROP_BOOL);
|
||||
if (action == SEL_SELECT) {
|
||||
fill_selection_true(selection.span, mask);
|
||||
}
|
||||
else if (action == SEL_DESELECT) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
}
|
||||
else if (action == SEL_INVERT) {
|
||||
invert_selection(selection.span, mask);
|
||||
}
|
||||
selection.finish();
|
||||
foreach_selection_attribute_writer(
|
||||
curves, selection_domain, [&](bke::GSpanAttributeWriter &selection) {
|
||||
if (action == SEL_SELECT) {
|
||||
fill_selection_true(selection.span, mask);
|
||||
}
|
||||
else if (action == SEL_DESELECT) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
}
|
||||
else if (action == SEL_INVERT) {
|
||||
invert_selection(selection.span, mask);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void select_all(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain, int action)
|
||||
@@ -334,17 +509,31 @@ void select_all(bke::CurvesGeometry &curves, const bke::AttrDomain selection_dom
|
||||
void select_linked(bke::CurvesGeometry &curves, const IndexMask &curves_mask)
|
||||
{
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
||||
curves, bke::AttrDomain::Point, CD_PROP_BOOL);
|
||||
const VArray<int8_t> curve_types = curves.curve_types();
|
||||
|
||||
Vector<bke::GSpanAttributeWriter> selection_writers = init_selection_writers(
|
||||
curves, bke::AttrDomain::Point);
|
||||
|
||||
curves_mask.foreach_index(GrainSize(256), [&](const int64_t curve_i) {
|
||||
GMutableSpan selection_curve = selection.span.slice(points_by_curve[curve_i]);
|
||||
if (has_anything_selected(selection_curve)) {
|
||||
fill_selection_true(selection_curve);
|
||||
for (const int i : selection_writers.index_range()) {
|
||||
bke::GSpanAttributeWriter &selection = selection_writers[i];
|
||||
GMutableSpan selection_curve = selection.span.slice(points_by_curve[curve_i]);
|
||||
if (has_anything_selected(selection_curve)) {
|
||||
fill_selection_true(selection_curve);
|
||||
for (const int j : selection_writers.index_range()) {
|
||||
if (j == i) {
|
||||
continue;
|
||||
}
|
||||
fill_selection_true(selection_writers[j].span.slice(points_by_curve[curve_i]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (curve_types[curve_i] != CURVE_TYPE_BEZIER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
selection.finish();
|
||||
finish_attribute_writers(selection_writers);
|
||||
}
|
||||
|
||||
void select_linked(bke::CurvesGeometry &curves)
|
||||
@@ -695,68 +884,89 @@ std::optional<FindClosestData> closest_elem_find_screen_space(
|
||||
|
||||
bool select_box(const ViewContext &vc,
|
||||
bke::CurvesGeometry &curves,
|
||||
const Span<float3> positions,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const float4x4 &projection,
|
||||
const IndexMask &mask,
|
||||
const bke::AttrDomain selection_domain,
|
||||
const rcti &rect,
|
||||
const eSelectOp sel_op)
|
||||
{
|
||||
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
||||
curves, selection_domain, CD_PROP_BOOL);
|
||||
Vector<bke::GSpanAttributeWriter> selection_writers = init_selection_writers(curves,
|
||||
selection_domain);
|
||||
|
||||
bool changed = false;
|
||||
if (sel_op == SEL_OP_SET) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
if (selection_domain == bke::AttrDomain::Point) {
|
||||
mask.foreach_index(GrainSize(1024), [&](const int point_i) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[point_i], projection);
|
||||
if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
|
||||
apply_selection_operation_at_index(selection.span, point_i, sel_op);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
foreach_selectable_point_range(
|
||||
curves,
|
||||
deformation,
|
||||
[&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
|
||||
mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point_i) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[point_i], projection);
|
||||
if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
|
||||
apply_selection_operation_at_index(
|
||||
selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
|
||||
.span,
|
||||
point_i,
|
||||
sel_op);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (selection_domain == bke::AttrDomain::Curve) {
|
||||
mask.foreach_index(GrainSize(512), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() == 1) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[points.first()], projection);
|
||||
if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
changed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const int segment_i : points.drop_back(1)) {
|
||||
const float3 pos1 = positions[segment_i];
|
||||
const float3 pos2 = positions[segment_i + 1];
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
foreach_selectable_curve_range(
|
||||
curves,
|
||||
deformation,
|
||||
[&](const IndexRange range,
|
||||
const Span<float3> positions,
|
||||
StringRef /* selection_attribute_name */) {
|
||||
mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() == 1) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[points.first()], projection);
|
||||
if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const int segment_i : points.drop_back(1)) {
|
||||
const float3 pos1 = positions[segment_i];
|
||||
const float3 pos2 = positions[segment_i + 1];
|
||||
|
||||
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
|
||||
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
|
||||
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
|
||||
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
|
||||
|
||||
if (BLI_rcti_isect_segment(&rect, int2(pos1_proj), int2(pos2_proj))) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (BLI_rcti_isect_segment(&rect, int2(pos1_proj), int2(pos2_proj))) {
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
};
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
selection.finish();
|
||||
|
||||
finish_attribute_writers(selection_writers);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool select_lasso(const ViewContext &vc,
|
||||
bke::CurvesGeometry &curves,
|
||||
const Span<float3> positions,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const float4x4 &projection_matrix,
|
||||
const IndexMask &mask,
|
||||
const bke::AttrDomain selection_domain,
|
||||
@@ -765,76 +975,99 @@ bool select_lasso(const ViewContext &vc,
|
||||
{
|
||||
rcti bbox;
|
||||
BLI_lasso_boundbox(&bbox, lasso_coords);
|
||||
|
||||
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
||||
curves, selection_domain, CD_PROP_BOOL);
|
||||
|
||||
Vector<bke::GSpanAttributeWriter> selection_writers = init_selection_writers(curves,
|
||||
selection_domain);
|
||||
bool changed = false;
|
||||
if (sel_op == SEL_OP_SET) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
if (selection_domain == bke::AttrDomain::Point) {
|
||||
mask.foreach_index(GrainSize(1024), [&](const int point_i) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[point_i], projection_matrix);
|
||||
/* Check the lasso bounding box first as an optimization. */
|
||||
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
|
||||
BLI_lasso_is_point_inside(lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
|
||||
{
|
||||
apply_selection_operation_at_index(selection.span, point_i, sel_op);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
foreach_selectable_point_range(
|
||||
curves,
|
||||
deformation,
|
||||
[&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
|
||||
mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point_i) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[point_i], projection_matrix);
|
||||
/* Check the lasso bounding box first as an optimization. */
|
||||
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
|
||||
BLI_lasso_is_point_inside(
|
||||
lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
|
||||
{
|
||||
apply_selection_operation_at_index(
|
||||
selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
|
||||
.span,
|
||||
point_i,
|
||||
sel_op);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (selection_domain == bke::AttrDomain::Curve) {
|
||||
mask.foreach_index(GrainSize(512), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() == 1) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[points.first()], projection_matrix);
|
||||
/* Check the lasso bounding box first as an optimization. */
|
||||
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
|
||||
BLI_lasso_is_point_inside(lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
|
||||
{
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
changed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const int segment_i : points.drop_back(1)) {
|
||||
const float3 pos1 = positions[segment_i];
|
||||
const float3 pos2 = positions[segment_i + 1];
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
foreach_selectable_curve_range(
|
||||
curves,
|
||||
deformation,
|
||||
[&](const IndexRange range,
|
||||
const Span<float3> positions,
|
||||
StringRef /* selection_attribute_name */) {
|
||||
mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() == 1) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[points.first()], projection_matrix);
|
||||
/* Check the lasso bounding box first as an optimization. */
|
||||
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
|
||||
BLI_lasso_is_point_inside(
|
||||
lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
|
||||
{
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const int segment_i : points.drop_back(1)) {
|
||||
const float3 pos1 = positions[segment_i];
|
||||
const float3 pos2 = positions[segment_i + 1];
|
||||
|
||||
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection_matrix);
|
||||
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection_matrix);
|
||||
const float2 pos1_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, pos1, projection_matrix);
|
||||
const float2 pos2_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, pos2, projection_matrix);
|
||||
|
||||
/* Check the lasso bounding box first as an optimization. */
|
||||
if (BLI_rcti_isect_segment(&bbox, int2(pos1_proj), int2(pos2_proj)) &&
|
||||
BLI_lasso_is_edge_inside(lasso_coords,
|
||||
int(pos1_proj.x),
|
||||
int(pos1_proj.y),
|
||||
int(pos2_proj.x),
|
||||
int(pos2_proj.y),
|
||||
IS_CLIPPED))
|
||||
{
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
/* Check the lasso bounding box first as an optimization. */
|
||||
if (BLI_rcti_isect_segment(&bbox, int2(pos1_proj), int2(pos2_proj)) &&
|
||||
BLI_lasso_is_edge_inside(lasso_coords,
|
||||
int(pos1_proj.x),
|
||||
int(pos1_proj.y),
|
||||
int(pos2_proj.x),
|
||||
int(pos2_proj.y),
|
||||
IS_CLIPPED))
|
||||
{
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
}
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
selection.finish();
|
||||
|
||||
finish_attribute_writers(selection_writers);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool select_circle(const ViewContext &vc,
|
||||
bke::CurvesGeometry &curves,
|
||||
const Span<float3> positions,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const float4x4 &projection,
|
||||
const IndexMask &mask,
|
||||
const bke::AttrDomain selection_domain,
|
||||
@@ -843,57 +1076,77 @@ bool select_circle(const ViewContext &vc,
|
||||
const eSelectOp sel_op)
|
||||
{
|
||||
const float radius_sq = pow2f(radius);
|
||||
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
|
||||
curves, selection_domain, CD_PROP_BOOL);
|
||||
|
||||
Vector<bke::GSpanAttributeWriter> selection_writers = init_selection_writers(curves,
|
||||
selection_domain);
|
||||
bool changed = false;
|
||||
if (sel_op == SEL_OP_SET) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
fill_selection_false(selection.span, mask);
|
||||
};
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
if (selection_domain == bke::AttrDomain::Point) {
|
||||
mask.foreach_index(GrainSize(1024), [&](const int point_i) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[point_i], projection);
|
||||
if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
|
||||
apply_selection_operation_at_index(selection.span, point_i, sel_op);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
foreach_selectable_point_range(
|
||||
curves,
|
||||
deformation,
|
||||
[&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
|
||||
mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point_i) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[point_i], projection);
|
||||
if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
|
||||
apply_selection_operation_at_index(
|
||||
selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
|
||||
.span,
|
||||
point_i,
|
||||
sel_op);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (selection_domain == bke::AttrDomain::Curve) {
|
||||
mask.foreach_index(GrainSize(512), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() == 1) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[points.first()], projection);
|
||||
if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
changed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const int segment_i : points.drop_back(1)) {
|
||||
const float3 pos1 = positions[segment_i];
|
||||
const float3 pos2 = positions[segment_i + 1];
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
foreach_selectable_curve_range(
|
||||
curves,
|
||||
deformation,
|
||||
[&](const IndexRange range,
|
||||
const Span<float3> positions,
|
||||
StringRef /* selection_attribute_name */) {
|
||||
mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
if (points.size() == 1) {
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
vc.region, positions[points.first()], projection);
|
||||
if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const int segment_i : points.drop_back(1)) {
|
||||
const float3 pos1 = positions[segment_i];
|
||||
const float3 pos2 = positions[segment_i + 1];
|
||||
|
||||
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
|
||||
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
|
||||
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
|
||||
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
|
||||
|
||||
const float distance_proj_sq = dist_squared_to_line_segment_v2(
|
||||
float2(coord), pos1_proj, pos2_proj);
|
||||
if (distance_proj_sq <= radius_sq) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
const float distance_proj_sq = dist_squared_to_line_segment_v2(
|
||||
float2(coord), pos1_proj, pos2_proj);
|
||||
if (distance_proj_sq <= radius_sq) {
|
||||
for (bke::GSpanAttributeWriter &selection : selection_writers) {
|
||||
apply_selection_operation_at_index(selection.span, curve_i, sel_op);
|
||||
}
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
selection.finish();
|
||||
|
||||
finish_attribute_writers(selection_writers);
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,52 @@ void keymap_curves(wmKeyConfig *keyconf);
|
||||
*/
|
||||
float (*point_normals_array_create(const Curves *curves_id))[3];
|
||||
|
||||
/**
|
||||
* Get selection attribute names need for given curve.
|
||||
* Possible outcomes: [".selection"] if Bezier curves are present,
|
||||
* [".selection", ".selection_handle_left", ".selection_handle_right"] otherwise. */
|
||||
Span<StringRef> get_curves_selection_attribute_names(const bke::CurvesGeometry &curves);
|
||||
|
||||
/* Get all possible curve selection attribute names. */
|
||||
Span<StringRef> get_curves_all_selection_attribute_names();
|
||||
|
||||
/**
|
||||
* Returns [".selection_handle_left", ".selection_handle_right"] if argument contains Bezier
|
||||
* curves, empty span otherwise.
|
||||
*/
|
||||
Span<StringRef> get_curves_bezier_selection_attribute_names(const bke::CurvesGeometry &curves);
|
||||
|
||||
/**
|
||||
* Used to select everything or to delete selection attribute so that it will not have to be
|
||||
* resized.
|
||||
*/
|
||||
void remove_selection_attributes(
|
||||
bke::MutableAttributeAccessor &attributes,
|
||||
Span<StringRef> selection_attribute_names = get_curves_all_selection_attribute_names());
|
||||
|
||||
using SelectionRangeFn = FunctionRef<void(
|
||||
IndexRange range, Span<float3> positions, StringRef selection_attribute_name)>;
|
||||
/**
|
||||
* Traverses all ranges of control points possible select. Callback function is provided with a
|
||||
* range being visited, positions (deformed if possible) referenced by the range and selection
|
||||
* attribute name positions belongs to:
|
||||
* curves.positions() belong to ".selection",
|
||||
* curves.handle_positions_left() belong to ".selection_handle_left",
|
||||
* curves.handle_positions_right() belong to ".selection_handle_right".
|
||||
*/
|
||||
void foreach_selectable_point_range(const bke::CurvesGeometry &curves,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
SelectionRangeFn range_consumer);
|
||||
|
||||
/**
|
||||
* Same logic as in foreach_selectable_point_range, just ranges reference curves instead of
|
||||
* positions directly. Futher positions can be referenced by using curves.points_by_curve() in a
|
||||
* callback function.
|
||||
*/
|
||||
void foreach_selectable_curve_range(const bke::CurvesGeometry &curves,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
SelectionRangeFn range_consumer);
|
||||
|
||||
bool object_has_editable_curves(const Main &bmain, const Object &object);
|
||||
bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
|
||||
VectorSet<Curves *> get_unique_editable_curves(const bContext &C);
|
||||
@@ -146,6 +192,7 @@ void fill_selection_true(GMutableSpan selection, const IndexMask &mask);
|
||||
* Return true if any element is selected, on either domain with either type.
|
||||
*/
|
||||
bool has_anything_selected(const bke::CurvesGeometry &curves);
|
||||
bool has_anything_selected(const bke::CurvesGeometry &curves, bke::AttrDomain selection_domain);
|
||||
bool has_anything_selected(const bke::CurvesGeometry &curves, const IndexMask &mask);
|
||||
|
||||
/**
|
||||
@@ -167,14 +214,23 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, IndexMaskMemory &mem
|
||||
* or points in curves with a selection factor greater than zero).
|
||||
*/
|
||||
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory);
|
||||
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves,
|
||||
StringRef attribute_name,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask retrieve_selected_points(const Curves &curves_id, IndexMaskMemory &memory);
|
||||
|
||||
/**
|
||||
* If the ".selection" attribute doesn't exist, create it with the requested type (bool or float).
|
||||
* If the selection_id attribute doesn't exist, create it with the requested type (bool or float).
|
||||
*/
|
||||
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves,
|
||||
bke::AttrDomain selection_domain,
|
||||
eCustomDataType create_type);
|
||||
eCustomDataType create_type,
|
||||
StringRef attribute_name = ".selection");
|
||||
|
||||
void foreach_selection_attribute_writer(
|
||||
bke::CurvesGeometry &curves,
|
||||
bke::AttrDomain selection_domain,
|
||||
FunctionRef<void(bke::GSpanAttributeWriter &selection)> fn);
|
||||
|
||||
/** Apply a change to a single curve or point. Avoid using this when affecting many elements. */
|
||||
void apply_selection_operation_at_index(GMutableSpan selection, int index, eSelectOp sel_op);
|
||||
@@ -246,7 +302,7 @@ std::optional<FindClosestData> closest_elem_find_screen_space(const ViewContext
|
||||
*/
|
||||
bool select_box(const ViewContext &vc,
|
||||
bke::CurvesGeometry &curves,
|
||||
Span<float3> deformed_positions,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const float4x4 &projection,
|
||||
const IndexMask &mask,
|
||||
bke::AttrDomain selection_domain,
|
||||
@@ -258,7 +314,7 @@ bool select_box(const ViewContext &vc,
|
||||
*/
|
||||
bool select_lasso(const ViewContext &vc,
|
||||
bke::CurvesGeometry &curves,
|
||||
Span<float3> deformed_positions,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const float4x4 &projection_matrix,
|
||||
const IndexMask &mask,
|
||||
bke::AttrDomain selection_domain,
|
||||
@@ -270,7 +326,7 @@ bool select_lasso(const ViewContext &vc,
|
||||
*/
|
||||
bool select_circle(const ViewContext &vc,
|
||||
bke::CurvesGeometry &curves,
|
||||
Span<float3> deformed_positions,
|
||||
const bke::crazyspace::GeometryDeformation &deformation,
|
||||
const float4x4 &projection,
|
||||
const IndexMask &mask,
|
||||
bke::AttrDomain selection_domain,
|
||||
|
||||
@@ -1210,7 +1210,7 @@ static bool do_lasso_select_grease_pencil(const ViewContext *vc,
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc->rv3d, layer_to_world);
|
||||
changed = ed::curves::select_lasso(*vc,
|
||||
info.drawing.strokes_for_write(),
|
||||
deformation.positions,
|
||||
deformation,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
@@ -1440,14 +1440,8 @@ static bool view3d_lasso_select(bContext *C,
|
||||
const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
|
||||
const IndexRange elements(curves.attributes().domain_size(selection_domain));
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
|
||||
changed = ed::curves::select_lasso(*vc,
|
||||
curves,
|
||||
deformation.positions,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
mcoords,
|
||||
sel_op);
|
||||
changed = ed::curves::select_lasso(
|
||||
*vc, curves, deformation, projection, elements, selection_domain, mcoords, sel_op);
|
||||
if (changed) {
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
|
||||
* generic attribute for now. */
|
||||
@@ -3081,6 +3075,7 @@ static bool ed_wpaint_vertex_select_pick(bContext *C,
|
||||
}
|
||||
|
||||
struct ClosestCurveDataBlock {
|
||||
blender::StringRef selection_name;
|
||||
Curves *curves_id = nullptr;
|
||||
blender::ed::curves::FindClosestData elem = {};
|
||||
};
|
||||
@@ -3116,20 +3111,33 @@ static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPi
|
||||
bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, *vc.obedit);
|
||||
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &curves_ob);
|
||||
const IndexRange elements(curves.attributes().domain_size(selection_domain));
|
||||
std::optional<ed::curves::FindClosestData> new_closest_elem =
|
||||
ed::curves::closest_elem_find_screen_space(vc,
|
||||
curves.points_by_curve(),
|
||||
deformation.positions,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
mval,
|
||||
new_closest.elem);
|
||||
if (new_closest_elem) {
|
||||
new_closest.elem = *new_closest_elem;
|
||||
new_closest.curves_id = &curves_id;
|
||||
const IndexMask elements(curves.attributes().domain_size(selection_domain));
|
||||
const auto range_consumer =
|
||||
[&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
|
||||
IndexMask mask = elements.slice_content(range);
|
||||
|
||||
std::optional<ed::curves::FindClosestData> new_closest_elem =
|
||||
ed::curves::closest_elem_find_screen_space(vc,
|
||||
curves.points_by_curve(),
|
||||
positions,
|
||||
projection,
|
||||
mask,
|
||||
selection_domain,
|
||||
mval,
|
||||
new_closest.elem);
|
||||
if (new_closest_elem) {
|
||||
new_closest.selection_name = selection_attribute_name;
|
||||
new_closest.elem = *new_closest_elem;
|
||||
new_closest.curves_id = &curves_id;
|
||||
}
|
||||
};
|
||||
|
||||
if (selection_domain == bke::AttrDomain::Point) {
|
||||
ed::curves::foreach_selectable_point_range(curves, deformation, range_consumer);
|
||||
}
|
||||
else if (selection_domain == bke::AttrDomain::Curve) {
|
||||
ed::curves::foreach_selectable_curve_range(curves, deformation, range_consumer);
|
||||
};
|
||||
}
|
||||
return new_closest;
|
||||
},
|
||||
@@ -3143,13 +3151,14 @@ static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPi
|
||||
for (Base *base : bases.as_span().slice(range)) {
|
||||
Curves &curves_id = *static_cast<Curves *>(base->object->data);
|
||||
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
||||
if (!ed::curves::has_anything_selected(curves)) {
|
||||
if (!ed::curves::has_anything_selected(curves, selection_domain)) {
|
||||
continue;
|
||||
}
|
||||
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
|
||||
curves, selection_domain, CD_PROP_BOOL);
|
||||
ed::curves::fill_selection_false(selection.span);
|
||||
selection.finish();
|
||||
|
||||
ed::curves::foreach_selection_attribute_writer(
|
||||
curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
|
||||
ed::curves::fill_selection_false(selection.span);
|
||||
});
|
||||
|
||||
deselected = true;
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
|
||||
@@ -3164,11 +3173,25 @@ static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPi
|
||||
return deselected;
|
||||
}
|
||||
|
||||
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
|
||||
closest.curves_id->geometry.wrap(), selection_domain, CD_PROP_BOOL);
|
||||
ed::curves::apply_selection_operation_at_index(
|
||||
selection.span, closest.elem.index, params.sel_op);
|
||||
selection.finish();
|
||||
if (selection_domain == bke::AttrDomain::Point) {
|
||||
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
|
||||
closest.curves_id->geometry.wrap(),
|
||||
bke::AttrDomain::Point,
|
||||
CD_PROP_BOOL,
|
||||
closest.selection_name);
|
||||
ed::curves::apply_selection_operation_at_index(
|
||||
selection.span, closest.elem.index, params.sel_op);
|
||||
selection.finish();
|
||||
}
|
||||
else if (selection_domain == bke::AttrDomain::Curve) {
|
||||
ed::curves::foreach_selection_attribute_writer(
|
||||
closest.curves_id->geometry.wrap(),
|
||||
bke::AttrDomain::Curve,
|
||||
[&](bke::GSpanAttributeWriter &selection) {
|
||||
ed::curves::apply_selection_operation_at_index(
|
||||
selection.span, closest.elem.index, params.sel_op);
|
||||
});
|
||||
}
|
||||
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
|
||||
* generic attribute for now. */
|
||||
@@ -4245,7 +4268,7 @@ static bool do_grease_pencil_box_select(const ViewContext *vc,
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc->rv3d, layer_to_world);
|
||||
changed |= ed::curves::select_box(*vc,
|
||||
info.drawing.strokes_for_write(),
|
||||
deformation.positions,
|
||||
deformation,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
@@ -4334,14 +4357,8 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
|
||||
const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
|
||||
const IndexRange elements(curves.attributes().domain_size(selection_domain));
|
||||
changed = ed::curves::select_box(vc,
|
||||
curves,
|
||||
deformation.positions,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
rect,
|
||||
sel_op);
|
||||
changed = ed::curves::select_box(
|
||||
vc, curves, deformation, projection, elements, selection_domain, rect, sel_op);
|
||||
if (changed) {
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
|
||||
* generic attribute for now. */
|
||||
@@ -5113,7 +5130,7 @@ static bool grease_pencil_circle_select(const ViewContext *vc,
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc->rv3d, layer_to_world);
|
||||
changed = ed::curves::select_circle(*vc,
|
||||
info.drawing.strokes_for_write(),
|
||||
deformation.positions,
|
||||
deformation,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
@@ -5173,15 +5190,8 @@ static bool obedit_circle_select(bContext *C,
|
||||
const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
|
||||
const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
|
||||
const IndexRange elements(curves.attributes().domain_size(selection_domain));
|
||||
changed = ed::curves::select_circle(*vc,
|
||||
curves,
|
||||
deformation.positions,
|
||||
projection,
|
||||
elements,
|
||||
selection_domain,
|
||||
mval,
|
||||
rad,
|
||||
sel_op);
|
||||
changed = ed::curves::select_circle(
|
||||
*vc, curves, deformation, projection, elements, selection_domain, mval, rad, sel_op);
|
||||
if (changed) {
|
||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
|
||||
* generic attribute for now. */
|
||||
|
||||
Reference in New Issue
Block a user