Changeset View
Standalone View
source/blender/editors/curves/intern/curves_ops.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| /** \file | /** \file | ||||
| * \ingroup edcurves | * \ingroup edcurves | ||||
| */ | */ | ||||
| #include <atomic> | #include <atomic> | ||||
| #include "BLI_array_utils.hh" | |||||
| #include "BLI_devirtualize_parameters.hh" | #include "BLI_devirtualize_parameters.hh" | ||||
| #include "BLI_index_mask_ops.hh" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_vector_set.hh" | #include "BLI_vector_set.hh" | ||||
| #include "ED_curves.h" | #include "ED_curves.h" | ||||
| #include "ED_object.h" | #include "ED_object.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_select_utils.h" | #include "ED_select_utils.h" | ||||
| ▲ Show 20 Lines • Show All 725 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain")); | const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain")); | ||||
| for (Curves *curves_id : get_unique_editable_curves(*C)) { | for (Curves *curves_id : get_unique_editable_curves(*C)) { | ||||
| if (curves_id->selection_domain == domain) { | if (curves_id->selection_domain == domain) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain); | |||||
| curves_id->selection_domain = domain; | curves_id->selection_domain = domain; | ||||
| CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); | CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); | ||||
| bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); | bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); | ||||
| if (curves.points_num() == 0) { | if (curves.points_num() == 0) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const GVArray src = attributes.lookup(".selection", domain); | |||||
| if (src.is_empty()) { | |||||
| continue; | |||||
| } | |||||
| if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) { | const CPPType &type = src.type(); | ||||
| VArray<float> curve_selection = curves.adapt_domain( | void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__); | ||||
| curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); | src.materialize(dst); | ||||
| curve_selection.materialize(curves.selection_curve_float_for_write()); | |||||
| attributes.remove(".selection_point_float"); | attributes.remove(".selection"); | ||||
| } | if (!attributes.add(".selection", | ||||
| else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) { | domain, | ||||
| VArray<float> point_selection = curves.adapt_domain( | bke::cpp_type_to_custom_data_type(type), | ||||
| curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); | bke::AttributeInitMoveArray(dst))) { | ||||
| point_selection.materialize(curves.selection_point_float_for_write()); | MEM_freeN(dst); | ||||
| attributes.remove(".selection_curve_float"); | |||||
| } | } | ||||
| /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic | /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic | ||||
| * attribute for now. */ | * attribute for now. */ | ||||
| DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); | ||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); | WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); | ||||
| } | } | ||||
| Show All 17 Lines | static void CURVES_OT_set_selection_domain(wmOperatorType *ot) | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| ot->prop = prop = RNA_def_enum( | ot->prop = prop = RNA_def_enum( | ||||
| ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", ""); | ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", ""); | ||||
| RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); | RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); | ||||
| } | } | ||||
| static bool varray_contains_nonzero(const VArray<float> &data) | static bool contains(const VArray<bool> &varray, const bool value) | ||||
| { | { | ||||
| bool contains_nonzero = false; | const CommonVArrayInfo info = varray.common_info(); | ||||
| devirtualize_varray(data, [&](const auto array) { | if (info.type == CommonVArrayInfo::Type::Single) { | ||||
| for (const int i : data.index_range()) { | return *static_cast<const bool *>(info.data) == value; | ||||
JacquesLucke: This should be compared to `value`. | |||||
| if (array[i] != 0.0f) { | } | ||||
| contains_nonzero = true; | if (info.type == CommonVArrayInfo::Type::Span) { | ||||
| break; | const Span<bool> span(static_cast<const bool *>(info.data), varray.size()); | ||||
Done Inline ActionsI feel like this kind of devirtualization is wrong here. Mainly because in the case where it is a single value, the loop is not necessary. JacquesLucke: I feel like this kind of devirtualization is wrong here. Mainly because in the case where it is… | |||||
| return threading::parallel_reduce( | |||||
| span.index_range(), | |||||
| 4096, | |||||
Done Inline ActionsShould start using atomics properly when writing to the same variable from different threads. JacquesLucke: Should start using atomics properly when writing to the same variable from different threads. | |||||
Done Inline ActionsI generalized this a bit to bool contains(const VArray<bool> &varray, const bool value). I thought of putting it in BLI_array_utils.hh, but maybe best to wait until it's needed elsewhere. HooglyBoogly: I generalized this a bit to `bool contains(const VArray<bool> &varray, const bool value)`. I… | |||||
| false, | |||||
| [&](const IndexRange range, const bool init) { | |||||
| return init || span.slice(range).contains(value); | |||||
| }, | |||||
| [&](const bool a, const bool b) { return a || b; }); | |||||
| } | |||||
| return threading::parallel_reduce( | |||||
| varray.index_range(), | |||||
| 2048, | |||||
| false, | |||||
| [&](const IndexRange range, const bool init) { | |||||
| if (init) { | |||||
| return init; | |||||
| } | |||||
| /* Alternatively, this could use #materialize to retrieve many values at once. */ | |||||
| for (const int64_t i : range) { | |||||
| if (varray[i] == value) { | |||||
| return true; | |||||
| } | } | ||||
Done Inline ActionsThe return value of std::find can't be used that way. You have to compare it to chunk.end(). JacquesLucke: The return value of `std::find` can't be used that way. You have to compare it to `chunk.end()`. | |||||
Done Inline ActionsEek, thanks for finding these problems. I wish std::find and other similar functions would just return a null pointer. The way they are has prevented me from using them a few times, I just forgot here. HooglyBoogly: Eek, thanks for finding these problems. I wish `std::find` and other similar functions would… | |||||
| } | } | ||||
| }); | return false; | ||||
| return contains_nonzero; | }, | ||||
| [&](const bool a, const bool b) { return a || b; }); | |||||
| } | } | ||||
| bool has_anything_selected(const Curves &curves_id) | bool has_anything_selected(const Curves &curves_id) | ||||
| { | { | ||||
| const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | ||||
| switch (curves_id.selection_domain) { | const VArray<bool> selection = curves.attributes().lookup<bool>(".selection"); | ||||
| case ATTR_DOMAIN_POINT: | return !selection || contains(selection, true); | ||||
| return varray_contains_nonzero(curves.selection_point_float()); | |||||
| case ATTR_DOMAIN_CURVE: | |||||
| return varray_contains_nonzero(curves.selection_curve_float()); | |||||
| } | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| static bool any_point_selected(const CurvesGeometry &curves) | |||||
| { | |||||
| return varray_contains_nonzero(curves.selection_point_float()); | |||||
| } | } | ||||
| static bool any_point_selected(const Span<Curves *> curves_ids) | static bool has_anything_selected(const Span<Curves *> curves_ids) | ||||
| { | { | ||||
| for (const Curves *curves_id : curves_ids) { | return std::any_of(curves_ids.begin(), curves_ids.end(), [](const Curves *curves_id) { | ||||
| if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) { | return has_anything_selected(*curves_id); | ||||
| return true; | }); | ||||
| } | |||||
| } | |||||
| return false; | |||||
| } | } | ||||
| namespace select_all { | namespace select_all { | ||||
| static void invert_selection(MutableSpan<float> selection) | static void invert_selection(MutableSpan<float> selection) | ||||
| { | { | ||||
| threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { | threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| selection[i] = 1.0f - selection[i]; | selection[i] = 1.0f - selection[i]; | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| static void invert_selection(GMutableSpan selection) | |||||
| { | |||||
| if (selection.type().is<bool>()) { | |||||
| array_utils::invert_booleans(selection.typed<bool>()); | |||||
| } | |||||
| else if (selection.type().is<float>()) { | |||||
| invert_selection(selection.typed<float>()); | |||||
| } | |||||
| } | |||||
| static int select_all_exec(bContext *C, wmOperator *op) | static int select_all_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| int action = RNA_enum_get(op->ptr, "action"); | int action = RNA_enum_get(op->ptr, "action"); | ||||
| VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C); | VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C); | ||||
| if (action == SEL_TOGGLE) { | if (action == SEL_TOGGLE) { | ||||
| action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT; | action = has_anything_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT; | ||||
| } | } | ||||
| for (Curves *curves_id : unique_curves) { | for (Curves *curves_id : unique_curves) { | ||||
| CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); | CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); | ||||
| bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); | |||||
| if (action == SEL_SELECT) { | if (action == SEL_SELECT) { | ||||
| /* As an optimization, just remove the selection attributes when everything is selected. */ | /* As an optimization, just remove the selection attributes when everything is selected. */ | ||||
| bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); | attributes.remove(".selection"); | ||||
| attributes.remove(".selection_point_float"); | } | ||||
| attributes.remove(".selection_curve_float"); | else if (!attributes.contains(".selection")) { | ||||
| BLI_assert(ELEM(action, SEL_INVERT, SEL_DESELECT)); | |||||
| /* If the attribute doesn't exist and it's either deleted or inverted, create | |||||
| * it with nothing selected, since that means everything was selected before. */ | |||||
| attributes.add(".selection", | |||||
| eAttrDomain(curves_id->selection_domain), | |||||
| CD_PROP_BOOL, | |||||
| bke::AttributeInitDefaultValue()); | |||||
| } | } | ||||
| else { | else { | ||||
| MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? | bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection"); | ||||
| curves.selection_point_float_for_write() : | |||||
| curves.selection_curve_float_for_write(); | |||||
| if (action == SEL_DESELECT) { | if (action == SEL_DESELECT) { | ||||
| selection.fill(0.0f); | fill_selection_false(selection.span); | ||||
| } | } | ||||
| else if (action == SEL_INVERT) { | else if (action == SEL_INVERT) { | ||||
| invert_selection(selection); | invert_selection(selection.span); | ||||
| } | } | ||||
| selection.finish(); | |||||
| } | } | ||||
| /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic | /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic | ||||
| * attribute for now. */ | * attribute for now. */ | ||||
| DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); | ||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); | WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 114 Lines • Show Last 20 Lines | |||||
This should be compared to value.