Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
| Show First 20 Lines • Show All 357 Lines • ▼ Show 20 Lines | static int select_random_exec(bContext *C, wmOperator *op) | ||||
| const auto next_partial_random_value = [&]() { | const auto next_partial_random_value = [&]() { | ||||
| return rng.get_float() * (1.0f - min_value) + min_value; | return rng.get_float() * (1.0f - min_value) + min_value; | ||||
| }; | }; | ||||
| const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; }; | const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; }; | ||||
| 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); | ||||
| const bool was_anything_selected = curves::has_anything_selected(*curves_id); | const bool was_anything_selected = curves::has_anything_selected(*curves_id); | ||||
| switch (curves_id->selection_domain) { | |||||
| case ATTR_DOMAIN_POINT: { | bke::SpanAttributeWriter<float> attribute = float_selection_ensure(*curves_id); | ||||
| MutableSpan<float> selection = curves.selection_point_float_for_write(); | MutableSpan<float> selection = attribute.span; | ||||
| if (!was_anything_selected) { | if (!was_anything_selected) { | ||||
| selection.fill(1.0f); | selection.fill(1.0f); | ||||
| } | } | ||||
| switch (curves_id->selection_domain) { | |||||
| case ATTR_DOMAIN_POINT: { | |||||
| if (partial) { | if (partial) { | ||||
| if (constant_per_curve) { | if (constant_per_curve) { | ||||
| for (const int curve_i : curves.curves_range()) { | for (const int curve_i : curves.curves_range()) { | ||||
| const float random_value = next_partial_random_value(); | const float random_value = next_partial_random_value(); | ||||
| const IndexRange points = curves.points_for_curve(curve_i); | const IndexRange points = curves.points_for_curve(curve_i); | ||||
| for (const int point_i : points) { | for (const int point_i : points) { | ||||
| selection[point_i] *= random_value; | selection[point_i] *= random_value; | ||||
| } | } | ||||
| Show All 23 Lines | switch (curves_id->selection_domain) { | ||||
| selection[point_i] = 0.0f; | selection[point_i] = 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case ATTR_DOMAIN_CURVE: { | case ATTR_DOMAIN_CURVE: { | ||||
| MutableSpan<float> selection = curves.selection_curve_float_for_write(); | |||||
| if (!was_anything_selected) { | |||||
| selection.fill(1.0f); | |||||
| } | |||||
| if (partial) { | if (partial) { | ||||
| for (const int curve_i : curves.curves_range()) { | for (const int curve_i : curves.curves_range()) { | ||||
| const float random_value = next_partial_random_value(); | const float random_value = next_partial_random_value(); | ||||
| selection[curve_i] *= random_value; | selection[curve_i] *= random_value; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| for (const int curve_i : curves.curves_range()) { | for (const int curve_i : curves.curves_range()) { | ||||
| const bool random_value = next_bool_random_value(); | const bool random_value = next_bool_random_value(); | ||||
| if (!random_value) { | if (!random_value) { | ||||
| selection[curve_i] = 0.0f; | selection[curve_i] = 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? | |||||
| curves.selection_point_float_for_write() : | |||||
| curves.selection_curve_float_for_write(); | |||||
| const bool was_any_selected = std::any_of( | const bool was_any_selected = std::any_of( | ||||
| selection.begin(), selection.end(), [](const float v) { return v > 0.0f; }); | selection.begin(), selection.end(), [](const float v) { return v > 0.0f; }); | ||||
| if (was_any_selected) { | if (was_any_selected) { | ||||
| for (float &v : selection) { | for (float &v : selection) { | ||||
| v *= rng.get_float(); | v *= rng.get_float(); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| for (float &v : selection) { | for (float &v : selection) { | ||||
| v = rng.get_float(); | v = rng.get_float(); | ||||
| } | } | ||||
| } | } | ||||
| attribute.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); | ||||
| } | } | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
| static int select_end_exec(bContext *C, wmOperator *op) | static int select_end_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C); | VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C); | ||||
| const bool end_points = RNA_boolean_get(op->ptr, "end_points"); | const bool end_points = RNA_boolean_get(op->ptr, "end_points"); | ||||
| const int amount = RNA_int_get(op->ptr, "amount"); | const int amount = RNA_int_get(op->ptr, "amount"); | ||||
| 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(); | |||||
| const bool was_anything_selected = curves::has_anything_selected(*curves_id); | const bool was_anything_selected = curves::has_anything_selected(*curves_id); | ||||
| MutableSpan<float> selection = curves.selection_point_float_for_write(); | curves::ensure_selection_attribute(*curves_id, CD_PROP_BOOL); | ||||
| bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection"); | |||||
| if (!was_anything_selected) { | if (!was_anything_selected) { | ||||
| selection.fill(1.0f); | curves::fill_selection_true(selection.span); | ||||
| } | |||||
| selection.span.type().to_static_type_tag<bool, float>([&](auto type_tag) { | |||||
| using T = typename decltype(type_tag)::type; | |||||
| if constexpr (std::is_void_v<T>) { | |||||
| BLI_assert_unreachable(); | |||||
| } | } | ||||
| else { | |||||
| MutableSpan<T> selection_typed = selection.span.typed<T>(); | |||||
| threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { | threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { | ||||
| for (const int curve_i : range) { | for (const int curve_i : range) { | ||||
| const IndexRange points = curves.points_for_curve(curve_i); | const IndexRange points = curves.points_for_curve(curve_i); | ||||
| if (end_points) { | if (end_points) { | ||||
| selection.slice(points.drop_back(amount)).fill(0.0f); | selection_typed.slice(points.drop_back(amount)).fill(T(0)); | ||||
| } | } | ||||
| else { | else { | ||||
| selection.slice(points.drop_front(amount)).fill(0.0f); | selection_typed.slice(points.drop_front(amount)).fill(T(0)); | ||||
| } | |||||
| } | } | ||||
| }); | |||||
| } | } | ||||
| }); | }); | ||||
| 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); | ||||
| } | } | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| Show All 19 Lines | static void SCULPT_CURVES_OT_select_end(wmOperatorType *ot) | ||||
| RNA_def_int( | RNA_def_int( | ||||
| ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX); | ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX); | ||||
| } | } | ||||
| namespace select_grow { | namespace select_grow { | ||||
| struct GrowOperatorDataPerCurve : NonCopyable, NonMovable { | struct GrowOperatorDataPerCurve : NonCopyable, NonMovable { | ||||
| Curves *curves_id; | Curves *curves_id; | ||||
| Vector<int> selected_points; | Vector<int64_t> selected_point_indices; | ||||
| Vector<int> unselected_points; | Vector<int64_t> unselected_point_indices; | ||||
| IndexMask selected_points; | |||||
| IndexMask unselected_points; | |||||
| Array<float> distances_to_selected; | Array<float> distances_to_selected; | ||||
| Array<float> distances_to_unselected; | Array<float> distances_to_unselected; | ||||
| Array<float> original_selection; | GArray<> original_selection; | ||||
| float pixel_to_distance_factor; | float pixel_to_distance_factor; | ||||
| }; | }; | ||||
| struct GrowOperatorData { | struct GrowOperatorData { | ||||
| int initial_mouse_x; | int initial_mouse_x; | ||||
| Vector<std::unique_ptr<GrowOperatorDataPerCurve>> per_curve; | Vector<std::unique_ptr<GrowOperatorDataPerCurve>> per_curve; | ||||
| }; | }; | ||||
| static void update_points_selection(const GrowOperatorDataPerCurve &data, | static void update_points_selection(const GrowOperatorDataPerCurve &data, | ||||
| const float distance, | const float distance, | ||||
| MutableSpan<float> points_selection) | MutableSpan<float> points_selection) | ||||
| { | { | ||||
| if (distance > 0.0f) { | if (distance > 0.0f) { | ||||
| threading::parallel_for( | threading::parallel_for( | ||||
| data.unselected_points.index_range(), 256, [&](const IndexRange range) { | data.unselected_points.index_range(), 256, [&](const IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| const int point_i = data.unselected_points[i]; | const int point_i = data.unselected_points[i]; | ||||
| const float distance_to_selected = data.distances_to_selected[i]; | const float distance_to_selected = data.distances_to_selected[i]; | ||||
| const float selection = distance_to_selected <= distance ? 1.0f : 0.0f; | const float selection = distance_to_selected <= distance ? 1.0f : 0.0f; | ||||
| points_selection[point_i] = selection; | points_selection[point_i] = selection; | ||||
| } | } | ||||
| }); | }); | ||||
| threading::parallel_for(data.selected_points.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(data.selected_points.index_range(), 512, [&](const IndexRange range) { | ||||
| for (const int point_i : data.selected_points.as_span().slice(range)) { | for (const int point_i : data.selected_points.slice(range)) { | ||||
| points_selection[point_i] = 1.0f; | points_selection[point_i] = 1.0f; | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| else { | else { | ||||
| threading::parallel_for(data.selected_points.index_range(), 256, [&](const IndexRange range) { | threading::parallel_for(data.selected_points.index_range(), 256, [&](const IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| const int point_i = data.selected_points[i]; | const int point_i = data.selected_points[i]; | ||||
| const float distance_to_unselected = data.distances_to_unselected[i]; | const float distance_to_unselected = data.distances_to_unselected[i]; | ||||
| const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f; | const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f; | ||||
| points_selection[point_i] = selection; | points_selection[point_i] = selection; | ||||
| } | } | ||||
| }); | }); | ||||
| threading::parallel_for( | threading::parallel_for( | ||||
| data.unselected_points.index_range(), 512, [&](const IndexRange range) { | data.unselected_points.index_range(), 512, [&](const IndexRange range) { | ||||
| for (const int point_i : data.unselected_points.as_span().slice(range)) { | for (const int point_i : data.unselected_points.slice(range)) { | ||||
| points_selection[point_i] = 0.0f; | points_selection[point_i] = 0.0f; | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x) | static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x) | ||||
| { | { | ||||
| GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata); | GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata); | ||||
| for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { | for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { | ||||
| Curves &curves_id = *curve_op_data->curves_id; | Curves &curves_id = *curve_op_data->curves_id; | ||||
| CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | ||||
| const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x; | const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x; | ||||
| bke::SpanAttributeWriter<float> selection = float_selection_ensure(curves_id); | |||||
| /* Grow or shrink selection based on precomputed distances. */ | /* Grow or shrink selection based on precomputed distances. */ | ||||
| switch (curves_id.selection_domain) { | switch (selection.domain) { | ||||
| case ATTR_DOMAIN_POINT: { | case ATTR_DOMAIN_POINT: { | ||||
| MutableSpan<float> points_selection = curves.selection_point_float_for_write(); | update_points_selection(*curve_op_data, distance, selection.span); | ||||
| update_points_selection(*curve_op_data, distance, points_selection); | |||||
| break; | break; | ||||
| } | } | ||||
| case ATTR_DOMAIN_CURVE: { | case ATTR_DOMAIN_CURVE: { | ||||
| Array<float> new_points_selection(curves.points_num()); | Array<float> new_points_selection(curves.points_num()); | ||||
| update_points_selection(*curve_op_data, distance, new_points_selection); | update_points_selection(*curve_op_data, distance, new_points_selection); | ||||
| /* Propagate grown point selection to the curve selection. */ | /* Propagate grown point selection to the curve selection. */ | ||||
| MutableSpan<float> curves_selection = curves.selection_curve_float_for_write(); | MutableSpan<float> curves_selection = selection.span; | ||||
| for (const int curve_i : curves.curves_range()) { | for (const int curve_i : curves.curves_range()) { | ||||
| const IndexRange points = curves.points_for_curve(curve_i); | const IndexRange points = curves.points_for_curve(curve_i); | ||||
| const Span<float> points_selection = new_points_selection.as_span().slice(points); | const Span<float> points_selection = new_points_selection.as_span().slice(points); | ||||
| const float max_selection = *std::max_element(points_selection.begin(), | const float max_selection = *std::max_element(points_selection.begin(), | ||||
| points_selection.end()); | points_selection.end()); | ||||
| curves_selection[curve_i] = max_selection; | curves_selection[curve_i] = max_selection; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| default: | |||||
| BLI_assert_unreachable(); | |||||
| } | } | ||||
| 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); | ||||
| } | } | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| static void select_grow_invoke_per_curve(Curves &curves_id, | static void select_grow_invoke_per_curve(const Curves &curves_id, | ||||
| Object &curves_ob, | const Object &curves_ob, | ||||
| const ARegion ®ion, | const ARegion ®ion, | ||||
| const View3D &v3d, | const View3D &v3d, | ||||
| const RegionView3D &rv3d, | const RegionView3D &rv3d, | ||||
| GrowOperatorDataPerCurve &curve_op_data) | GrowOperatorDataPerCurve &curve_op_data) | ||||
| { | { | ||||
| curve_op_data.curves_id = &curves_id; | const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | ||||
| CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | |||||
| const Span<float3> positions = curves.positions(); | const Span<float3> positions = curves.positions(); | ||||
| /* Find indices of selected and unselected points. */ | if (const bke::GAttributeReader original_selection = curves.attributes().lookup(".selection")) { | ||||
| switch (curves_id.selection_domain) { | curve_op_data.original_selection = GArray<>(original_selection.varray.type(), | ||||
| case ATTR_DOMAIN_POINT: { | original_selection.varray.size()); | ||||
| const VArray<float> points_selection = curves.selection_point_float(); | original_selection.varray.materialize(curve_op_data.original_selection.data()); | ||||
| curve_op_data.original_selection.reinitialize(points_selection.size()); | |||||
| points_selection.materialize(curve_op_data.original_selection); | |||||
| for (const int point_i : points_selection.index_range()) { | |||||
| const float point_selection = points_selection[point_i]; | |||||
| if (point_selection > 0.0f) { | |||||
| curve_op_data.selected_points.append(point_i); | |||||
| } | |||||
| else { | |||||
| curve_op_data.unselected_points.append(point_i); | |||||
| } | |||||
| } | } | ||||
| break; | /* Find indices of selected and unselected points. */ | ||||
| } | curve_op_data.selected_points = curves::retrieve_selected_points( | ||||
| case ATTR_DOMAIN_CURVE: { | curves_id, curve_op_data.selected_point_indices); | ||||
| const VArray<float> curves_selection = curves.selection_curve_float(); | curve_op_data.unselected_points = curve_op_data.selected_points.invert( | ||||
| curve_op_data.original_selection.reinitialize(curves_selection.size()); | curves.points_range(), curve_op_data.unselected_point_indices); | ||||
| curves_selection.materialize(curve_op_data.original_selection); | |||||
| for (const int curve_i : curves_selection.index_range()) { | |||||
| const float curve_selection = curves_selection[curve_i]; | |||||
| const IndexRange points = curves.points_for_curve(curve_i); | |||||
| if (curve_selection > 0.0f) { | |||||
| for (const int point_i : points) { | |||||
| curve_op_data.selected_points.append(point_i); | |||||
| } | |||||
| } | |||||
| else { | |||||
| for (const int point_i : points) { | |||||
| curve_op_data.unselected_points.append(point_i); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| threading::parallel_invoke( | threading::parallel_invoke( | ||||
| 1024 < curve_op_data.selected_points.size() + curve_op_data.unselected_points.size(), | 1024 < curve_op_data.selected_points.size() + curve_op_data.unselected_points.size(), | ||||
| [&]() { | [&]() { | ||||
| /* Build KD-tree for the selected points. */ | /* Build KD-tree for the selected points. */ | ||||
| KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_points.size()); | KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_points.size()); | ||||
| BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); | BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); | ||||
| for (const int point_i : curve_op_data.selected_points) { | for (const int point_i : curve_op_data.selected_points) { | ||||
| ▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__); | GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__); | ||||
| op->customdata = op_data; | op->customdata = op_data; | ||||
| op_data->initial_mouse_x = event->xy[0]; | op_data->initial_mouse_x = event->xy[0]; | ||||
| Curves &curves_id = *static_cast<Curves *>(active_ob->data); | Curves &curves_id = *static_cast<Curves *>(active_ob->data); | ||||
| auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>(); | auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>(); | ||||
| curve_op_data->curves_id = &curves_id; | |||||
| select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data); | select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data); | ||||
| op_data->per_curve.append(std::move(curve_op_data)); | op_data->per_curve.append(std::move(curve_op_data)); | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event) | static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| Show All 11 Lines | case LEFTMOUSE: { | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| case EVT_ESCKEY: | case EVT_ESCKEY: | ||||
| case RIGHTMOUSE: { | case RIGHTMOUSE: { | ||||
| /* Undo operator by resetting the selection to the original value. */ | /* Undo operator by resetting the selection to the original value. */ | ||||
| for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { | for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { | ||||
| Curves &curves_id = *curve_op_data->curves_id; | Curves &curves_id = *curve_op_data->curves_id; | ||||
| CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); | ||||
| switch (curves_id.selection_domain) { | bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); | ||||
| case ATTR_DOMAIN_POINT: { | |||||
| MutableSpan<float> points_selection = curves.selection_point_float_for_write(); | attributes.remove(".selection"); | ||||
| points_selection.copy_from(curve_op_data->original_selection); | if (!curve_op_data->original_selection.is_empty()) { | ||||
| break; | attributes.add( | ||||
| } | ".selection", | ||||
| case ATTR_DOMAIN_CURVE: { | eAttrDomain(curves_id.selection_domain), | ||||
| MutableSpan<float> curves_seletion = curves.selection_curve_float_for_write(); | bke::cpp_type_to_custom_data_type(curve_op_data->original_selection.type()), | ||||
| curves_seletion.copy_from(curve_op_data->original_selection); | bke::AttributeInitVArray(GVArray::ForSpan(curve_op_data->original_selection))); | ||||
| break; | |||||
| } | |||||
| } | } | ||||
| /* 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); | ||||
| } | } | ||||
| MEM_delete(&op_data); | MEM_delete(&op_data); | ||||
| ▲ Show 20 Lines • Show All 394 Lines • Show Last 20 Lines | |||||