From 9c6a7f74bb7587ecf3a4ab8c2cb89f3116bf2017 Mon Sep 17 00:00:00 2001 From: Casey Bianco-Davis Date: Sun, 3 Sep 2023 14:05:34 -0700 Subject: [PATCH 01/12] GPv3: Cyclical set operator Note adds a key bind 'alt c' to toggle, same as for curves. --- .../keyconfig/keymap_data/blender_default.py | 3 + scripts/startup/bl_ui/space_view3d.py | 4 + .../intern/grease_pencil_edit.cc | 224 ++++++++++++++++++ 3 files changed, 231 insertions(+) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 15c91ee3506..bf0230823d7 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4569,6 +4569,9 @@ def km_grease_pencil_edit(params): # Dissolve ("grease_pencil.dissolve", {"type": 'X', "value": 'PRESS', "ctrl": True}, None), ("grease_pencil.dissolve", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None), + # Cyclical set + ("grease_pencil.cyclical_set", {"type": 'F', "value": 'PRESS'}, {"properties": [("type", "CLOSE"), ("geometry", True)]}), + ("grease_pencil.cyclical_set", {"type": 'C', "value": 'PRESS', "alt": True}, {"properties": [("type", "TOGGLE")]}), ]) return keymap diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 09e1a28063b..0d0a882cc25 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -5602,6 +5602,10 @@ class VIEW3D_MT_edit_greasepencil_stroke(Menu): layout.operator("grease_pencil.stroke_smooth") layout.operator("grease_pencil.stroke_simplify") + layout.separator() + + layout.operator_enum("grease_pencil.cyclical_set", "type") + class VIEW3D_MT_edit_curves(Menu): bl_label = "Curves" diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 96e71075bb8..3fb3ab95430 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -712,6 +712,229 @@ static void GREASE_PENCIL_OT_dissolve(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Cyclical Set Operator + * \{ */ + +enum class CyclicalMode : int8_t { + /* Sets all strokes to cycle. */ + CLOSE, + /* Sets all strokes to not cycle. */ + OPEN, + /* Switchs the cyclic state of the strokes. */ + TOGGLE, +}; + +static const EnumPropertyItem prop_cyclical_types[] = { + {int(CyclicalMode::CLOSE), "CLOSE", 0, "Close All", ""}, + {int(CyclicalMode::OPEN), "OPEN", 0, "Open All", ""}, + {int(CyclicalMode::TOGGLE), "TOGGLE", 0, "Toggle", ""}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static bke::CurvesGeometry close_curves_with_geometry(const bke::CurvesGeometry &curves, + const IndexMask &curves_to_close) +{ + const bke::AnonymousAttributePropagationInfo &propagation_info = {}; + const OffsetIndices points_by_curve = curves.points_by_curve(); + + /* Start by adding the original points. */ + Array curve_point_counts(curves.curves_num(), 0); + threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + curve_point_counts[curve_i] = points.size(); + } + }); + + int total_points = curves.points_num(); + const Span positions = curves.positions(); + + curves.ensure_evaluated_lengths(); + + /* Add the extra points to close the curves. */ + curves_to_close.foreach_index([&](const int64_t curve_i) { + const IndexRange points = points_by_curve[curve_i]; + + const float curve_length = curves.evaluated_length_total_for_curve(curve_i, false); + const float end_distance = len_v3v3(positions[points.first()], positions[points.last()]); + + const int num_points_to_add = points.size() * end_distance / curve_length; + + curve_point_counts[curve_i] += num_points_to_add; + total_points += num_points_to_add; + }); + + /* Create the array containing the original points IDs. + * Note: Not all entries will be set, so initialze with zeros. */ + Array dst_to_src_point(total_points, 0); + int cur_dst_point = 0; + threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + for (const int point : points) { + dst_to_src_point[cur_dst_point++] = point; + } + cur_dst_point += curve_point_counts[curve_i] - points.size(); + } + }); + + bke::CurvesGeometry dst_curves(total_points, curves.curves_num()); + + threading::parallel_invoke( + dst_curves.curves_num() > 1024, + [&]() { + MutableSpan new_curve_offsets = dst_curves.offsets_for_write(); + array_utils::copy(curve_point_counts.as_span(), new_curve_offsets.drop_back(1)); + offset_indices::accumulate_counts_to_offsets(new_curve_offsets); + }, + [&]() { + copy_attributes(curves.attributes(), + ATTR_DOMAIN_CURVE, + propagation_info, + {}, + dst_curves.attributes_for_write()); + /* Note: all of the extra points will have a ID of zero. */ + gather_attributes(curves.attributes(), + ATTR_DOMAIN_POINT, + propagation_info, + {}, + dst_to_src_point.as_span(), + dst_curves.attributes_for_write()); + }); + + const OffsetIndices new_points_by_curve = dst_curves.points_by_curve(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + const bke::AttributeAccessor src_attributes = curves.attributes(); + + /* Interpolating the attributes between the end points. */ + for (bke::AttributeTransferData &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, {})) + { + curves_to_close.foreach_index([&](const int64_t curve_i) { + const IndexRange points = points_by_curve[curve_i]; + const IndexRange dst_points = new_points_by_curve[curve_i]; + const int num_points_added = curve_point_counts[curve_i] - points.size(); + + bke::attribute_math::convert_to_static_type(attribute.dst.span.type(), [&](auto dummy) { + using T = decltype(dummy); + const auto src_attr = attribute.src.typed(); + auto dst_attr = attribute.dst.span.typed(); + + for (const int i : IndexRange(num_points_added)) { + const float factor = float(i + 1) / (num_points_added + 1); + dst_attr[dst_points[points.size() + i]] = bke::attribute_math::mix2( + factor, src_attr[points.last()], src_attr[points.first()]); + } + }); + }); + + attribute.dst.finish(); + } + + dst_curves.runtime->type_counts = curves.runtime->type_counts; + + return dst_curves; +} + +static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) +{ + using namespace blender; + const Scene *scene = CTX_data_scene(C); + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + const CyclicalMode mode = CyclicalMode(RNA_enum_get(op->ptr, "type")); + const bool geometry = RNA_boolean_get(op->ptr, "geometry"); + + bool changed = false; + grease_pencil.foreach_editable_drawing( + scene->r.cfra, [&](int /*drawing_index*/, bke::greasepencil::Drawing &drawing) { + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + if (curves.points_num() == 0) { + return; + } + + if (!ed::curves::has_anything_selected(curves)) { + return; + } + + MutableSpan cyclic = curves.cyclic_for_write(); + const OffsetIndices points_by_curve = curves.points_by_curve(); + const VArray selection = *curves.attributes().lookup_or_default( + ".selection", ATTR_DOMAIN_POINT, true); + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + if (!ed::curves::has_anything_selected(selection, points_by_curve[curve_i])) { + continue; + } + + if (mode == CyclicalMode::CLOSE) { + cyclic[curve_i] = true; + } + else if (mode == CyclicalMode::OPEN) { + cyclic[curve_i] = false; + } + else if (mode == CyclicalMode::TOGGLE) { + cyclic[curve_i] ^= true; + } + } + }); + + if (geometry) { + IndexMaskMemory memory; + const IndexMask curves_to_close = IndexMask::from_predicate( + curves.curves_range(), GrainSize(512), memory, [&](const int64_t curve_i) { + const bool selected = ed::curves::has_anything_selected(selection, + points_by_curve[curve_i]); + return cyclic[curve_i] && selected; + }); + + if (!curves_to_close.is_empty()) { + curves = close_curves_with_geometry(curves, curves_to_close); + drawing.tag_topology_changed(); + changed = true; + } + } + + changed = true; + }); + + if (changed) { + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil); + } + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_cyclical_set(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers. */ + ot->name = "Set Cyclical State"; + ot->idname = "GREASE_PENCIL_OT_cyclical_set"; + ot->description = "Close or open the selected stroke adding a segment from last to first point"; + + /* Callbacks. */ + ot->invoke = WM_menu_invoke; + ot->exec = grease_pencil_cyclical_set_exec; + ot->poll = editable_grease_pencil_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Simplify parameters. */ + ot->prop = RNA_def_enum( + ot->srna, "type", prop_cyclical_types, int(CyclicalMode::TOGGLE), "Type", ""); + prop = RNA_def_boolean( + ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/** \} */ + } // namespace blender::ed::greasepencil void ED_operatortypes_grease_pencil_edit() @@ -720,6 +943,7 @@ void ED_operatortypes_grease_pencil_edit() WM_operatortype_append(GREASE_PENCIL_OT_stroke_smooth); WM_operatortype_append(GREASE_PENCIL_OT_stroke_simplify); WM_operatortype_append(GREASE_PENCIL_OT_dissolve); + WM_operatortype_append(GREASE_PENCIL_OT_cyclical_set); } void ED_keymap_grease_pencil(wmKeyConfig *keyconf) -- 2.30.2 From f900ee25e83b50d50f6168f66ddc90dc354be752 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Tue, 12 Sep 2023 18:32:33 -0700 Subject: [PATCH 02/12] Fix: merge error. --- .../blender/editors/grease_pencil/intern/grease_pencil_edit.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 8843cc8afc8..e1165ca511c 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -710,6 +710,8 @@ static void GREASE_PENCIL_OT_dissolve(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Delete Frame Operator * \{ */ -- 2.30.2 From 9eefaa2caecfebcde05d86d952e6185f1369ba8e Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Tue, 12 Sep 2023 18:34:35 -0700 Subject: [PATCH 03/12] Fix: merge error. --- .../blender/editors/grease_pencil/intern/grease_pencil_edit.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index e1165ca511c..b15036dbc48 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -796,6 +796,7 @@ static void GREASE_PENCIL_OT_delete_frame(wmOperatorType *ot) } /** \} */ + /* -------------------------------------------------------------------- */ /** \name Cyclical Set Operator * \{ */ -- 2.30.2 From 1c380a08970dd7fd6de37cfe903d9d07951891a2 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Mon, 2 Oct 2023 16:58:31 -0700 Subject: [PATCH 04/12] Cleanup: add comments. --- .../grease_pencil/intern/grease_pencil_edit.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index b15036dbc48..7bf5c6cadd6 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -817,6 +817,12 @@ static const EnumPropertyItem prop_cyclical_types[] = { {0, nullptr, 0, nullptr, nullptr}, }; +/* + * Creates points between the first and last points for each curve in `curves_to_close`, + * with the distance between points being the average density of the curve. + * + * Note: this function does not set the curve attribute `cyclic`. + */ static bke::CurvesGeometry close_curves_with_geometry(const bke::CurvesGeometry &curves, const IndexMask &curves_to_close) { @@ -837,7 +843,7 @@ static bke::CurvesGeometry close_curves_with_geometry(const bke::CurvesGeometry curves.ensure_evaluated_lengths(); - /* Add the extra points to close the curves. */ + /* Add the extra points to close the curves, calculated to match the average density. */ curves_to_close.foreach_index([&](const int64_t curve_i) { const IndexRange points = points_by_curve[curve_i]; @@ -906,6 +912,7 @@ static bke::CurvesGeometry close_curves_with_geometry(const bke::CurvesGeometry const auto src_attr = attribute.src.typed(); auto dst_attr = attribute.dst.span.typed(); + /* Go through the extra points and interpolate between the start and end. */ for (const int i : IndexRange(num_points_added)) { const float factor = float(i + 1) / (num_points_added + 1); dst_attr[dst_points[points.size() + i]] = bke::attribute_math::mix2( @@ -930,7 +937,7 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) GreasePencil &grease_pencil = *static_cast(object->data); const CyclicalMode mode = CyclicalMode(RNA_enum_get(op->ptr, "type")); - const bool geometry = RNA_boolean_get(op->ptr, "geometry"); + const bool add_geometry = RNA_boolean_get(op->ptr, "geometry"); bool changed = false; grease_pencil.foreach_editable_drawing( @@ -967,8 +974,9 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) } }); - if (geometry) { + if (add_geometry) { IndexMaskMemory memory; + /* Determine which curves are now closed. */ const IndexMask curves_to_close = IndexMask::from_predicate( curves.curves_range(), GrainSize(512), memory, [&](const int64_t curve_i) { const bool selected = ed::curves::has_anything_selected(selection, -- 2.30.2 From c06382930db115219e4480f536584bc435831866 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Fri, 13 Oct 2023 18:43:22 -0700 Subject: [PATCH 05/12] Cleanup: rename "drawing_index" to "layer_index" --- .../blender/editors/grease_pencil/intern/grease_pencil_edit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 7bf5c6cadd6..37cb583ba33 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -941,7 +941,7 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) bool changed = false; grease_pencil.foreach_editable_drawing( - scene->r.cfra, [&](int /*drawing_index*/, bke::greasepencil::Drawing &drawing) { + scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) { bke::CurvesGeometry &curves = drawing.strokes_for_write(); if (curves.points_num() == 0) { return; -- 2.30.2 From 99a73f55baf36d9ad40998ea6087a1c8bfb06c28 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Mon, 16 Oct 2023 16:25:04 -0700 Subject: [PATCH 06/12] Cleanup: use `retrieve_selected_curves` --- .../editors/curves/intern/curves_selection.cc | 3 +- .../intern/grease_pencil_edit.cc | 36 +++++++++---------- source/blender/editors/include/ED_curves.hh | 1 + 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index c0af4f50795..bb08915bfa2 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -23,8 +23,7 @@ namespace blender::ed::curves { -static IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, - IndexMaskMemory &memory) +IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory) { const IndexRange curves_range = curves.curves_range(); const bke::AttributeAccessor attributes = curves.attributes(); diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index d7986a2435c..30b6c24e18e 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -951,39 +951,35 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) if (curves.points_num() == 0) { return; } - if (!ed::curves::has_anything_selected(curves)) { return; } MutableSpan cyclic = curves.cyclic_for_write(); - const OffsetIndices points_by_curve = curves.points_by_curve(); - const VArray selection = *curves.attributes().lookup_or_default( - ".selection", ATTR_DOMAIN_POINT, true); - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { - for (const int curve_i : range) { - if (!ed::curves::has_anything_selected(selection, points_by_curve[curve_i])) { - continue; - } + IndexMaskMemory memory; + const IndexMask curve_selection = ed::curves::retrieve_selected_curves(curves, memory); - if (mode == CyclicalMode::CLOSE) { - cyclic[curve_i] = true; - } - else if (mode == CyclicalMode::OPEN) { - cyclic[curve_i] = false; - } - else if (mode == CyclicalMode::TOGGLE) { - cyclic[curve_i] ^= true; - } + curve_selection.foreach_index([&](const int64_t curve_i) { + if (mode == CyclicalMode::CLOSE) { + cyclic[curve_i] = true; + } + else if (mode == CyclicalMode::OPEN) { + cyclic[curve_i] = false; + } + else if (mode == CyclicalMode::TOGGLE) { + cyclic[curve_i] ^= true; } }); if (add_geometry) { - IndexMaskMemory memory; + const OffsetIndices points_by_curve = curves.points_by_curve(); + const VArray selection = *curves.attributes().lookup_or_default( + ".selection", ATTR_DOMAIN_POINT, true); + IndexMaskMemory memory2; /* Determine which curves are now closed. */ const IndexMask curves_to_close = IndexMask::from_predicate( - curves.curves_range(), GrainSize(512), memory, [&](const int64_t curve_i) { + curves.curves_range(), GrainSize(512), memory2, [&](const int64_t curve_i) { const bool selected = ed::curves::has_anything_selected(selection, points_by_curve[curve_i]); return cyclic[curve_i] && selected; diff --git a/source/blender/editors/include/ED_curves.hh b/source/blender/editors/include/ED_curves.hh index 524c54dcc83..4400d3303e9 100644 --- a/source/blender/editors/include/ED_curves.hh +++ b/source/blender/editors/include/ED_curves.hh @@ -134,6 +134,7 @@ bool has_anything_selected(const VArray &varray, IndexRange range_to_check * Find curves that have any point selected (a selection factor greater than zero), * or curves that have their own selection factor greater than zero. */ +IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory); IndexMask retrieve_selected_curves(const Curves &curves_id, IndexMaskMemory &memory); /** -- 2.30.2 From 12340f4b1155b9964414ef633d5886a0a05efddb Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Tue, 17 Oct 2023 11:48:36 -0700 Subject: [PATCH 07/12] Cleanup: remove code to and end points --- .../intern/grease_pencil_edit.cc | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 30b6c24e18e..2a158a0013f 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -822,118 +822,6 @@ static const EnumPropertyItem prop_cyclical_types[] = { {0, nullptr, 0, nullptr, nullptr}, }; -/* - * Creates points between the first and last points for each curve in `curves_to_close`, - * with the distance between points being the average density of the curve. - * - * Note: this function does not set the curve attribute `cyclic`. - */ -static bke::CurvesGeometry close_curves_with_geometry(const bke::CurvesGeometry &curves, - const IndexMask &curves_to_close) -{ - const bke::AnonymousAttributePropagationInfo &propagation_info = {}; - const OffsetIndices points_by_curve = curves.points_by_curve(); - - /* Start by adding the original points. */ - Array curve_point_counts(curves.curves_num(), 0); - threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { - for (const int curve_i : range) { - const IndexRange points = points_by_curve[curve_i]; - curve_point_counts[curve_i] = points.size(); - } - }); - - int total_points = curves.points_num(); - const Span positions = curves.positions(); - - curves.ensure_evaluated_lengths(); - - /* Add the extra points to close the curves, calculated to match the average density. */ - curves_to_close.foreach_index([&](const int64_t curve_i) { - const IndexRange points = points_by_curve[curve_i]; - - const float curve_length = curves.evaluated_length_total_for_curve(curve_i, false); - const float end_distance = len_v3v3(positions[points.first()], positions[points.last()]); - - const int num_points_to_add = points.size() * end_distance / curve_length; - - curve_point_counts[curve_i] += num_points_to_add; - total_points += num_points_to_add; - }); - - /* Create the array containing the original points IDs. - * Note: Not all entries will be set, so initialze with zeros. */ - Array dst_to_src_point(total_points, 0); - int cur_dst_point = 0; - threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { - for (const int curve_i : range) { - const IndexRange points = points_by_curve[curve_i]; - for (const int point : points) { - dst_to_src_point[cur_dst_point++] = point; - } - cur_dst_point += curve_point_counts[curve_i] - points.size(); - } - }); - - bke::CurvesGeometry dst_curves(total_points, curves.curves_num()); - - threading::parallel_invoke( - dst_curves.curves_num() > 1024, - [&]() { - MutableSpan new_curve_offsets = dst_curves.offsets_for_write(); - array_utils::copy(curve_point_counts.as_span(), new_curve_offsets.drop_back(1)); - offset_indices::accumulate_counts_to_offsets(new_curve_offsets); - }, - [&]() { - copy_attributes(curves.attributes(), - ATTR_DOMAIN_CURVE, - propagation_info, - {}, - dst_curves.attributes_for_write()); - /* Note: all of the extra points will have a ID of zero. */ - gather_attributes(curves.attributes(), - ATTR_DOMAIN_POINT, - propagation_info, - {}, - dst_to_src_point.as_span(), - dst_curves.attributes_for_write()); - }); - - const OffsetIndices new_points_by_curve = dst_curves.points_by_curve(); - bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - const bke::AttributeAccessor src_attributes = curves.attributes(); - - /* Interpolating the attributes between the end points. */ - for (bke::AttributeTransferData &attribute : bke::retrieve_attributes_for_transfer( - src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, {})) - { - curves_to_close.foreach_index([&](const int64_t curve_i) { - const IndexRange points = points_by_curve[curve_i]; - const IndexRange dst_points = new_points_by_curve[curve_i]; - const int num_points_added = curve_point_counts[curve_i] - points.size(); - - bke::attribute_math::convert_to_static_type(attribute.dst.span.type(), [&](auto dummy) { - using T = decltype(dummy); - const auto src_attr = attribute.src.typed(); - auto dst_attr = attribute.dst.span.typed(); - - /* Go through the extra points and interpolate between the start and end. */ - for (const int i : IndexRange(num_points_added)) { - const float factor = float(i + 1) / (num_points_added + 1); - dst_attr[dst_points[points.size() + i]] = bke::attribute_math::mix2( - factor, src_attr[points.last()], src_attr[points.first()]); - } - }); - }); - - attribute.dst.finish(); - } - - dst_curves.runtime->type_counts = curves.runtime->type_counts; - - return dst_curves; -} - static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) { using namespace blender; @@ -942,7 +830,6 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) GreasePencil &grease_pencil = *static_cast(object->data); const CyclicalMode mode = CyclicalMode(RNA_enum_get(op->ptr, "type")); - const bool add_geometry = RNA_boolean_get(op->ptr, "geometry"); bool changed = false; grease_pencil.foreach_editable_drawing( @@ -972,26 +859,6 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) } }); - if (add_geometry) { - const OffsetIndices points_by_curve = curves.points_by_curve(); - const VArray selection = *curves.attributes().lookup_or_default( - ".selection", ATTR_DOMAIN_POINT, true); - IndexMaskMemory memory2; - /* Determine which curves are now closed. */ - const IndexMask curves_to_close = IndexMask::from_predicate( - curves.curves_range(), GrainSize(512), memory2, [&](const int64_t curve_i) { - const bool selected = ed::curves::has_anything_selected(selection, - points_by_curve[curve_i]); - return cyclic[curve_i] && selected; - }); - - if (!curves_to_close.is_empty()) { - curves = close_curves_with_geometry(curves, curves_to_close); - drawing.tag_topology_changed(); - changed = true; - } - } - changed = true; }); @@ -1022,8 +889,6 @@ static void GREASE_PENCIL_OT_cyclical_set(wmOperatorType *ot) /* Simplify parameters. */ ot->prop = RNA_def_enum( ot->srna, "type", prop_cyclical_types, int(CyclicalMode::TOGGLE), "Type", ""); - prop = RNA_def_boolean( - ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -- 2.30.2 From e8352e599a0398d991e3ae429b4fe390ee3e5429 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Tue, 17 Oct 2023 12:33:28 -0700 Subject: [PATCH 08/12] Cleanup: fix code problem --- scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- .../blender/editors/grease_pencil/intern/grease_pencil_edit.cc | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index a3436e62cdc..f6e2005344b 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4611,7 +4611,7 @@ def km_grease_pencil_edit(params): # Keyframe Menu op_menu("VIEW3D_MT_edit_greasepencil_animation", {"type": 'I', "value": 'PRESS'}), # Cyclical set - ("grease_pencil.cyclical_set", {"type": 'F', "value": 'PRESS'}, {"properties": [("type", "CLOSE"), ("geometry", True)]}), + ("grease_pencil.cyclical_set", {"type": 'F', "value": 'PRESS'}, {"properties": [("type", "CLOSE")]}), ("grease_pencil.cyclical_set", {"type": 'C', "value": 'PRESS', "alt": True}, {"properties": [("type", "TOGGLE")]}), ]) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 2a158a0013f..6fd20b85819 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -872,8 +872,6 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) static void GREASE_PENCIL_OT_cyclical_set(wmOperatorType *ot) { - PropertyRNA *prop; - /* Identifiers. */ ot->name = "Set Cyclical State"; ot->idname = "GREASE_PENCIL_OT_cyclical_set"; @@ -889,7 +887,6 @@ static void GREASE_PENCIL_OT_cyclical_set(wmOperatorType *ot) /* Simplify parameters. */ ot->prop = RNA_def_enum( ot->srna, "type", prop_cyclical_types, int(CyclicalMode::TOGGLE), "Type", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ -- 2.30.2 From a2608cf2ba284e7faaef96df3fb2919523e4af81 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Wed, 18 Oct 2023 16:50:12 -0700 Subject: [PATCH 09/12] Cleanup: remove unneeded use of namespace. --- .../blender/editors/grease_pencil/intern/grease_pencil_edit.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 6fd20b85819..1dba6454e9f 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -824,7 +824,6 @@ static const EnumPropertyItem prop_cyclical_types[] = { static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) { - using namespace blender; const Scene *scene = CTX_data_scene(C); Object *object = CTX_data_active_object(C); GreasePencil &grease_pencil = *static_cast(object->data); -- 2.30.2 From 441ae81f88b6f82575a796b795aac79fc9a0193b Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Wed, 18 Oct 2023 17:07:43 -0700 Subject: [PATCH 10/12] Cleanup: use masked fill and invert boolean --- source/blender/blenlib/BLI_array_utils.hh | 1 + source/blender/blenlib/intern/array_utils.cc | 5 +++++ .../intern/grease_pencil_edit.cc | 22 +++++++++---------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh index cdec61e3b6a..07a23d1deb1 100644 --- a/source/blender/blenlib/BLI_array_utils.hh +++ b/source/blender/blenlib/BLI_array_utils.hh @@ -179,6 +179,7 @@ void copy_group_to_group(OffsetIndices src_offsets, void count_indices(Span indices, MutableSpan counts); void invert_booleans(MutableSpan span); +void invert_booleans(MutableSpan span, const IndexMask &mask); enum class BooleanMix { None, diff --git a/source/blender/blenlib/intern/array_utils.cc b/source/blender/blenlib/intern/array_utils.cc index ada428f2c3a..627a43ecb39 100644 --- a/source/blender/blenlib/intern/array_utils.cc +++ b/source/blender/blenlib/intern/array_utils.cc @@ -85,6 +85,11 @@ void invert_booleans(MutableSpan span) }); } +void invert_booleans(MutableSpan span, const IndexMask &mask) +{ + mask.foreach_index_optimized([&](const int64_t i) { span[i] = !span[i]; }); +} + BooleanMix booleans_mix_calc(const VArray &varray, const IndexRange range_to_check) { if (varray.is_empty()) { diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 1dba6454e9f..7bace9a51c8 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -846,17 +846,17 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) IndexMaskMemory memory; const IndexMask curve_selection = ed::curves::retrieve_selected_curves(curves, memory); - curve_selection.foreach_index([&](const int64_t curve_i) { - if (mode == CyclicalMode::CLOSE) { - cyclic[curve_i] = true; - } - else if (mode == CyclicalMode::OPEN) { - cyclic[curve_i] = false; - } - else if (mode == CyclicalMode::TOGGLE) { - cyclic[curve_i] ^= true; - } - }); + switch (mode) { + case CyclicalMode::CLOSE: + index_mask::masked_fill(cyclic, true, curve_selection); + break; + case CyclicalMode::OPEN: + index_mask::masked_fill(cyclic, false, curve_selection); + break; + case CyclicalMode::TOGGLE: + array_utils::invert_booleans(cyclic, curve_selection); + break; + } changed = true; }); -- 2.30.2 From a56851b9e67298c335527804c8ab48cf2da4b445 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Wed, 18 Oct 2023 19:34:06 -0700 Subject: [PATCH 11/12] Cleanup: handle non-existing attribute --- .../editors/grease_pencil/intern/grease_pencil_edit.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index f68818f9b4a..9ad5fc1f9ae 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -891,6 +891,11 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) return; } + /* Return to stop from creating unneeded attribute. */ + if (mode == CyclicalMode::OPEN && !curves.attributes().contains("cyclic")) { + return; + } + MutableSpan cyclic = curves.cyclic_for_write(); IndexMaskMemory memory; @@ -908,6 +913,11 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) break; } + /* Remove the attribute if it is empty. */ + if (!ed::curves::has_anything_selected(curves.cyclic(), curves.curves_range())) { + curves.attributes_for_write().remove("cyclic"); + } + changed = true; }); -- 2.30.2 From 8f45f2f5b76afdf6a188565dc38fed22f2cb7dc6 Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Thu, 19 Oct 2023 16:15:46 -0700 Subject: [PATCH 12/12] Cleanup: don't do unneeded checks --- .../editors/grease_pencil/intern/grease_pencil_edit.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 9ad5fc1f9ae..f964b69a9b4 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -914,7 +914,9 @@ static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op) } /* Remove the attribute if it is empty. */ - if (!ed::curves::has_anything_selected(curves.cyclic(), curves.curves_range())) { + if (mode != CyclicalMode::CLOSE && + !ed::curves::has_anything_selected(curves.cyclic(), curves.curves_range())) + { curves.attributes_for_write().remove("cyclic"); } -- 2.30.2