From 46cd7afcda3c58b27243f28a1d7f9be93fea4970 Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 9 Dec 2024 11:04:16 +0100 Subject: [PATCH] Grease Pencil: "Set Start Point" operator Adds back the operator to change the start point on cyclical strokes. Behaves the same way as in 4.2. Pull Request: https://projects.blender.org/blender/blender/pulls/128540 --- scripts/startup/bl_ui/space_view3d.py | 2 + .../intern/grease_pencil_edit.cc | 112 ++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index c0e1c9921df..5c16db34c35 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -5733,6 +5733,7 @@ class VIEW3D_MT_edit_greasepencil_stroke(Menu): layout.operator("grease_pencil.cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' layout.operator_menu_enum("grease_pencil.caps_set", text="Set Caps", property="type") layout.operator("grease_pencil.stroke_switch_direction") + layout.operator("grease_pencil.set_start_point", text="Set Start Point") layout.separator() @@ -8102,6 +8103,7 @@ class VIEW3D_MT_greasepencil_edit_context_menu(Menu): col.operator("transform.push_pull", text="Push/Pull") col.operator("transform.transform", text="Shrink/Fatten").mode = 'CURVE_SHRINKFATTEN' col.operator("grease_pencil.stroke_smooth", text="Smooth Points") + col.operator("grease_pencil.set_start_point", text="Set Start Point") col.separator() 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 5f462823020..11b1db265b9 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -1175,6 +1175,117 @@ static void GREASE_PENCIL_OT_stroke_switch_direction(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ +/* -------------------------------------------------------------------- */ +/** \name Set Start Point Operator + * \{ */ +static bke::CurvesGeometry set_start_point(const bke::CurvesGeometry &curves, + const IndexMask &mask) +{ + + const OffsetIndices points_by_curve = curves.points_by_curve(); + const VArray src_cyclic = curves.cyclic(); + + /* Early-return if no cyclic curves. */ + if (array_utils::booleans_mix_calc(src_cyclic) == array_utils::BooleanMix::AllFalse) { + return curves; + } + + Array start_set_points(curves.points_num()); + mask.to_bools(start_set_points.as_mutable_span()); + + Array dst_to_src_point(curves.points_num()); + + threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + const Span curve_i_selected_points = start_set_points.as_span().slice(points); + const int first_selected = curve_i_selected_points.first_index_try(true); + + MutableSpan dst_to_src_slice = dst_to_src_point.as_mutable_span().slice(points); + + array_utils::fill_index_range(dst_to_src_slice, points.start()); + + if (first_selected == -1 || src_cyclic[curve_i] == false) { + continue; + } + + std::rotate(dst_to_src_slice.begin(), + dst_to_src_slice.begin() + first_selected, + dst_to_src_slice.end()); + } + }); + + /* New CurvesGeometry to copy to. */ + bke::CurvesGeometry dst_curves(curves.points_num(), curves.curves_num()); + BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names); + + /* Copy offsets. */ + array_utils::copy(curves.offsets(), dst_curves.offsets_for_write()); + + /* Attribute accessors for copying. */ + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + const bke::AttributeAccessor src_attributes = curves.attributes(); + + /* Copy curve attrs. */ + bke::copy_attributes( + src_attributes, bke::AttrDomain::Curve, bke::AttrDomain::Curve, {}, dst_attributes); + array_utils::copy(src_cyclic, dst_curves.cyclic_for_write()); + + /* Copy point attrs */ + gather_attributes(src_attributes, + bke::AttrDomain::Point, + bke::AttrDomain::Point, + {}, + dst_to_src_point, + dst_attributes); + + dst_curves.update_curve_types(); + return dst_curves; +} + +static int grease_pencil_set_start_point_exec(bContext *C, wmOperator *) +{ + using namespace bke::greasepencil; + const Scene *scene = CTX_data_scene(C); + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + std::atomic changed = false; + const Vector drawings = retrieve_editable_drawings(*scene, grease_pencil); + threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) { + IndexMaskMemory memory; + const IndexMask selection = retrieve_editable_and_selected_points( + *object, info.drawing, info.layer_index, memory); + if (selection.is_empty()) { + return; + } + + info.drawing.strokes_for_write() = set_start_point(info.drawing.strokes(), selection); + + info.drawing.tag_topology_changed(); + 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_set_start_point(wmOperatorType *ot) +{ + /* Identifiers */ + ot->name = "Set Start Point"; + ot->idname = "GREASE_PENCIL_OT_set_start_point"; + ot->description = "Select which point is the beginning of the curve"; + + /* Callbacks */ + ot->exec = grease_pencil_set_start_point_exec; + ot->poll = editable_grease_pencil_point_selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} /** \} */ /* -------------------------------------------------------------------- */ @@ -3827,6 +3938,7 @@ void ED_operatortypes_grease_pencil_edit() WM_operatortype_append(GREASE_PENCIL_OT_cyclical_set); WM_operatortype_append(GREASE_PENCIL_OT_set_active_material); WM_operatortype_append(GREASE_PENCIL_OT_stroke_switch_direction); + WM_operatortype_append(GREASE_PENCIL_OT_set_start_point); WM_operatortype_append(GREASE_PENCIL_OT_set_uniform_thickness); WM_operatortype_append(GREASE_PENCIL_OT_set_uniform_opacity); WM_operatortype_append(GREASE_PENCIL_OT_caps_set);