Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/keyframes_general.c9
-rw-r--r--source/blender/editors/curve/editcurve.c41
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c3
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h2
-rw-r--r--source/blender/editors/include/ED_numinput.h2
-rw-r--r--source/blender/editors/include/ED_view3d.h5
-rw-r--r--source/blender/editors/include/UI_interface.h2
-rw-r--r--source/blender/editors/include/UI_view2d.h2
-rw-r--r--source/blender/editors/interface/interface.cc4
-rw-r--r--source/blender/editors/interface/interface_region_menu_pie.cc2
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c5
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c8
-rw-r--r--source/blender/editors/object/object_add.cc6
-rw-r--r--source/blender/editors/render/render_preview.cc3
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc526
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh3
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc126
-rw-r--r--source/blender/editors/space_action/action_edit.c12
-rw-r--r--source/blender/editors/space_action/space_action.c5
-rw-r--r--source/blender/editors/space_file/filelist.c8
-rw-r--r--source/blender/editors/space_graph/graph_edit.c11
-rw-r--r--source/blender/editors/space_node/node_draw.cc16
-rw-r--r--source/blender/editors/space_node/node_edit.cc6
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c45
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c5
30 files changed, 703 insertions, 167 deletions
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index aec2b0f769a..00e2f221117 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -118,11 +118,13 @@ void clear_fcurve_keys(FCurve *fcu)
/* ---------------- */
-void duplicate_fcurve_keys(FCurve *fcu)
+bool duplicate_fcurve_keys(FCurve *fcu)
{
+ bool changed = false;
+
/* this can only work when there is an F-Curve, and also when there are some BezTriples */
if (ELEM(NULL, fcu, fcu->bezt)) {
- return;
+ return changed;
}
for (int i = 0; i < fcu->totvert; i++) {
@@ -135,7 +137,7 @@ void duplicate_fcurve_keys(FCurve *fcu)
memcpy(newbezt + i + 1, fcu->bezt + i, sizeof(BezTriple));
memcpy(newbezt + i + 2, fcu->bezt + i + 1, sizeof(BezTriple) * (fcu->totvert - (i + 1)));
fcu->totvert++;
-
+ changed = true;
/* reassign pointers... (free old, and add new) */
MEM_freeN(fcu->bezt);
fcu->bezt = newbezt;
@@ -148,6 +150,7 @@ void duplicate_fcurve_keys(FCurve *fcu)
BEZT_SEL_ALL(&fcu->bezt[i]);
}
}
+ return changed;
}
/* **************************************************** */
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 755e538f415..9da9845116d 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1471,7 +1471,8 @@ static int curve_split_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- int ok = -1;
+ bool changed = false;
+ int count_failed = 0;
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
@@ -1489,7 +1490,7 @@ static int curve_split_exec(bContext *C, wmOperator *op)
adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, true);
if (BLI_listbase_is_empty(&newnurb)) {
- ok = MAX2(ok, 0);
+ count_failed += 1;
continue;
}
@@ -1504,14 +1505,16 @@ static int curve_split_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
- ok = 1;
+ changed = true;
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
DEG_id_tag_update(obedit->data, 0);
}
MEM_freeN(objects);
- if (ok == 0) {
- BKE_report(op->reports, RPT_ERROR, "Cannot split current selection");
+ if (changed == false) {
+ if (count_failed != 0) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot split current selection");
+ }
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
@@ -4998,7 +5001,8 @@ static int spin_exec(bContext *C, wmOperator *op)
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = ED_view3d_context_rv3d(C);
float cent[3], axis[3], viewmat[4][4];
- int ok = -1;
+ bool changed = false;
+ int count_failed = 0;
RNA_float_get_array(op->ptr, "center", cent);
RNA_float_get_array(op->ptr, "axis", axis);
@@ -5025,11 +5029,11 @@ static int spin_exec(bContext *C, wmOperator *op)
mul_m4_v3(obedit->imat, cent);
if (!ed_editnurb_spin(viewmat, v3d, obedit, axis, cent)) {
- ok = MAX2(ok, 0);
+ count_failed += 1;
continue;
}
- ok = 1;
+ changed = true;
if (ED_curve_updateAnimPaths(bmain, cu)) {
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit);
}
@@ -5039,8 +5043,11 @@ static int spin_exec(bContext *C, wmOperator *op)
}
MEM_freeN(objects);
- if (ok == 0) {
- BKE_report(op->reports, RPT_ERROR, "Cannot spin");
+ if (changed == false) {
+ if (count_failed != 0) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot spin");
+ }
+ return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
@@ -5889,7 +5896,9 @@ static int duplicate_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- int ok = -1;
+
+ bool changed = false;
+ int count_failed = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
@@ -5906,19 +5915,21 @@ static int duplicate_exec(bContext *C, wmOperator *op)
adduplicateflagNurb(obedit, v3d, &newnurb, SELECT, false);
if (BLI_listbase_is_empty(&newnurb)) {
- ok = MAX2(ok, 0);
+ count_failed += 1;
continue;
}
- ok = 1;
+ changed = true;
BLI_movelisttolist(object_editcurve_get(obedit), &newnurb);
DEG_id_tag_update(&cu->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, &cu->id);
}
MEM_freeN(objects);
- if (ok == 0) {
- BKE_report(op->reports, RPT_ERROR, "Cannot duplicate current selection");
+ if (changed == false) {
+ if (count_failed != 0) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot duplicate current selection");
+ }
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 1f31a384787..8fca0c46c82 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -776,7 +776,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.curves.sculpt_comb
ops.curves.sculpt_cut
ops.curves.sculpt_delete
- ops.curves.sculpt_grow
+ ops.curves.sculpt_grow_shrink
ops.generic.cursor
ops.generic.select
ops.generic.select_box
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index a8fb344f366..3c8e6d6e5f5 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -994,9 +994,10 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
}
- return OPERATOR_FINISHED;
+ return OPERATOR_CANCELLED;
}
void GPENCIL_OT_duplicate(wmOperatorType *ot)
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index 3a0bb9738ae..163797d395d 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -381,7 +381,7 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con
void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc);
bool delete_fcurve_keys(struct FCurve *fcu);
void clear_fcurve_keys(struct FCurve *fcu);
-void duplicate_fcurve_keys(struct FCurve *fcu);
+bool duplicate_fcurve_keys(struct FCurve *fcu);
float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr);
typedef struct FCurveSegment {
diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h
index 12e3ffee627..a1b4e3a4552 100644
--- a/source/blender/editors/include/ED_numinput.h
+++ b/source/blender/editors/include/ED_numinput.h
@@ -43,7 +43,7 @@ typedef struct NumInput {
int str_cur;
} NumInput;
-/* NumInput.flag */
+/** #NumInput.flag */
enum {
NUM_AFFECT_ALL = (1 << 0),
/* (1 << 9) and above are reserved for internal flags! */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index c3571bb3788..3f9f26560b2 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -163,6 +163,11 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain,
const struct Scene *scene,
struct Object *camera_ob);
+bool ED_view3d_camera_to_view_selected_with_set_clipping(struct Main *bmain,
+ struct Depsgraph *depsgraph,
+ const struct Scene *scene,
+ struct Object *camera_ob);
+
/**
* Use to store the last view, before entering camera view.
*/
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 0854b8b5cfa..4649fb06374 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2948,7 +2948,7 @@ uiBlock *UI_region_block_find_mouse_over(const struct ARegion *region,
*/
struct ARegion *UI_region_searchbox_region_get(const struct ARegion *button_region);
-/* uiFontStyle.align */
+/** #uiFontStyle.align */
typedef enum eFontStyle_Align {
UI_STYLE_TEXT_LEFT = 0,
UI_STYLE_TEXT_CENTER = 1,
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 56b0bd04a71..47c554f84e0 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -447,7 +447,7 @@ typedef struct View2DEdgePanData {
struct ARegion *region;
/** View2d we're operating in. */
struct View2D *v2d;
- /* Limit maximum pannable area */
+ /** Limit maximum pannable area. */
struct rctf limit;
/** Panning should only start once being in the inside rect once (e.g. adding nodes can happen
diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc
index 9bd0498fcfb..b34d61d5192 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -6516,7 +6516,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
if (properties) {
PointerRNA *ptr = UI_but_operator_ptr_get(but);
- /* Copy idproperties. */
+ /* Copy id-properties. */
ptr->data = IDP_CopyProperty(properties);
}
@@ -6755,7 +6755,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
PointerRNA *opptr = UI_but_operator_ptr_get(but);
wmOperatorType *ot = but->optype;
- /* so the context is passed to itemf functions */
+ /* So the context is passed to `itemf` functions. */
WM_operator_properties_sanitize(opptr, false);
/* if the default property of the operator is enum and it is set,
diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc
index d30797cc4d0..b11564f09c5 100644
--- a/source/blender/editors/interface/interface_region_menu_pie.cc
+++ b/source/blender/editors/interface/interface_region_menu_pie.cc
@@ -332,7 +332,7 @@ static void ui_pie_menu_level_invoke(bContext *C, void *argN, void *arg2)
PointerRNA ptr;
WM_operator_properties_create_ptr(&ptr, lvl->ot);
- /* so the context is passed to itemf functions (some need it) */
+ /* So the context is passed to `itemf` functions (some need it). */
WM_operator_properties_sanitize(&ptr, false);
PropertyRNA *prop = RNA_struct_find_property(&ptr, lvl->propname);
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 64d10f7e2af..73716f6edba 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -4399,6 +4399,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
knifetool_exit(op);
ED_workspace_status_text(C, NULL);
+ /* Exit early to prevent undo push for empty cuts. */
+ if (kcd->totkvert == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
return OPERATOR_FINISHED;
case KNF_MODAL_UNDO:
if (BLI_stack_is_empty(kcd->undostack)) {
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 0ea807586f9..5a0a2b7a09a 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -2016,6 +2016,7 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
+ bool changed = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -2026,6 +2027,7 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op)
BMOperator bmop;
BMesh *bm = em->bm;
+ changed = true;
EDBM_op_init(em,
&bmop,
@@ -2060,16 +2062,16 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op)
}
MEM_freeN(objects);
- return OPERATOR_FINISHED;
+ return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
static int edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
WM_cursor_wait(true);
- edbm_duplicate_exec(C, op);
+ const int retval = edbm_duplicate_exec(C, op);
WM_cursor_wait(false);
- return OPERATOR_FINISHED;
+ return retval;
}
void MESH_OT_duplicate(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc
index 6a7920d4d75..ba11483722e 100644
--- a/source/blender/editors/object/object_add.cc
+++ b/source/blender/editors/object/object_add.cc
@@ -3564,6 +3564,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool linked = RNA_boolean_get(op->ptr, "linked");
const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag;
+ bool changed = false;
/* We need to handle that here ourselves, because we may duplicate several objects, in which case
* we also want to remap pointers between those... */
@@ -3582,6 +3583,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
* the list is made in advance */
ED_object_base_select(base, BA_DESELECT);
ED_object_base_select(basen, BA_SELECT);
+ changed = true;
if (basen == nullptr) {
continue;
@@ -3598,6 +3600,10 @@ static int duplicate_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
+ if (!changed) {
+ return OPERATOR_CANCELLED;
+ }
+
/* Note that this will also clear newid pointers and tags. */
copy_object_set_idnew(C);
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index 7404b3bf010..78e786a2130 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -830,7 +830,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
DEG_graph_build_from_view_layer(depsgraph);
DEG_evaluate_on_refresh(depsgraph);
- ED_view3d_camera_to_view_selected(preview_data->pr_main, depsgraph, scene, camera_object);
+ ED_view3d_camera_to_view_selected_with_set_clipping(
+ preview_data->pr_main, depsgraph, scene, camera_object);
BKE_scene_graph_update_tagged(depsgraph, preview_data->pr_main);
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index c422c8c2033..fe7683d12f5 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -31,6 +31,7 @@ set(SRC
curves_sculpt_add.cc
curves_sculpt_comb.cc
curves_sculpt_delete.cc
+ curves_sculpt_grow_shrink.cc
curves_sculpt_ops.cc
curves_sculpt_snake_hook.cc
paint_cursor.c
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
new file mode 100644
index 00000000000..d26af20799e
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
@@ -0,0 +1,526 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <algorithm>
+
+#include "curves_sculpt_intern.hh"
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_kdtree.h"
+#include "BLI_rand.hh"
+#include "BLI_vector.hh"
+
+#include "PIL_time.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_brush.h"
+#include "BKE_bvhutils.h"
+#include "BKE_context.h"
+#include "BKE_curves.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_paint.h"
+#include "BKE_spline.hh"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_curves_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "curves_sculpt_intern.hh"
+
+/**
+ * The code below uses a suffix naming convention to indicate the coordinate space:
+ * - `cu`: Local space of the curves object that is being edited.
+ * - `su`: Local space of the surface object.
+ * - `wo`: World space.
+ * - `re`: 2D coordinates within the region.
+ */
+
+namespace blender::ed::sculpt_paint {
+
+using bke::CurvesGeometry;
+
+/**
+ * Utility class to wrap different grow/shrink behaviors.
+ * It might be useful to use this for other future brushes as well, but better see if this
+ * abstraction holds up for a while before using it in more places.
+ */
+class CurvesEffect {
+ public:
+ virtual void execute(CurvesGeometry &curves,
+ Span<int> curve_indices,
+ Span<float> move_distances_cu) = 0;
+};
+
+/**
+ * Make curves smaller by trimming the end off.
+ */
+class ShrinkCurvesEffect : public CurvesEffect {
+ private:
+ Brush &brush_;
+
+ public:
+ ShrinkCurvesEffect(Brush &brush) : brush_(brush)
+ {
+ }
+
+ void execute(CurvesGeometry &curves,
+ const Span<int> curve_indices,
+ const Span<float> move_distances_cu) override
+ {
+ MutableSpan<float3> positions_cu = curves.positions();
+ threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int influence_i : range) {
+ const int curve_i = curve_indices[influence_i];
+ const float move_distance_cu = move_distances_cu[influence_i];
+ const IndexRange curve_points = curves.points_for_curve(curve_i);
+ this->shrink_curve(positions_cu, curve_points, move_distance_cu);
+ }
+ });
+ }
+
+ void shrink_curve(MutableSpan<float3> positions,
+ const IndexRange curve_points,
+ const float shrink_length) const
+ {
+ PolySpline spline;
+ spline.resize(curve_points.size());
+ MutableSpan<float3> spline_positions = spline.positions();
+ spline_positions.copy_from(positions.slice(curve_points));
+ spline.mark_cache_invalid();
+ const float min_length = brush_.curves_sculpt_settings->minimum_length;
+ const float old_length = spline.length();
+ const float new_length = std::max(min_length, old_length - shrink_length);
+ const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f);
+
+ Vector<float> old_point_lengths;
+ old_point_lengths.append(0.0f);
+ for (const int i : spline_positions.index_range().drop_back(1)) {
+ const float3 &p1 = spline_positions[i];
+ const float3 &p2 = spline_positions[i + 1];
+ const float length = math::distance(p1, p2);
+ old_point_lengths.append(old_point_lengths.last() + length);
+ }
+
+ for (const int i : spline_positions.index_range()) {
+ const float eval_length = old_point_lengths[i] * length_factor;
+ const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length);
+ const float index_factor = lookup.evaluated_index + lookup.factor;
+ float3 p;
+ spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1});
+ positions[curve_points[i]] = p;
+ }
+ }
+};
+
+/**
+ * Make the curves longer by extrapolating them linearly.
+ */
+class ExtrapolateCurvesEffect : public CurvesEffect {
+ void execute(CurvesGeometry &curves,
+ const Span<int> curve_indices,
+ const Span<float> move_distances_cu) override
+ {
+ MutableSpan<float3> positions_cu = curves.positions();
+ threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int influence_i : range) {
+ const int curve_i = curve_indices[influence_i];
+ const float move_distance_cu = move_distances_cu[influence_i];
+ const IndexRange curve_points = curves.points_for_curve(curve_i);
+
+ if (curve_points.size() <= 1) {
+ continue;
+ }
+
+ const float3 old_last_pos_cu = positions_cu[curve_points.last()];
+ /* Use some point within the curve rather than the end point to smooth out some random
+ * variation. */
+ const float3 direction_reference_point =
+ positions_cu[curve_points[curve_points.size() / 2]];
+ const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point);
+
+ const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu;
+ this->move_last_point_and_resample(positions_cu, curve_points, new_last_pos_cu);
+ }
+ });
+ }
+
+ void move_last_point_and_resample(MutableSpan<float3> positions,
+ const IndexRange curve_points,
+ const float3 &new_last_point_position) const
+ {
+ Vector<float> old_lengths;
+ old_lengths.append(0.0f);
+ /* Used to (1) normalize the segment sizes over time and (2) support making zero-length
+ * segments */
+ const float extra_length = 0.001f;
+ for (const int segment_i : IndexRange(curve_points.size() - 1)) {
+ const float3 &p1 = positions[curve_points[segment_i]];
+ const float3 &p2 = positions[curve_points[segment_i] + 1];
+ const float length = math::distance(p1, p2);
+ old_lengths.append(old_lengths.last() + length + extra_length);
+ }
+ Vector<float> point_factors;
+ for (float &old_length : old_lengths) {
+ point_factors.append(old_length / old_lengths.last());
+ }
+
+ PolySpline new_spline;
+ new_spline.resize(curve_points.size());
+ MutableSpan<float3> new_spline_positions = new_spline.positions();
+ for (const int i : IndexRange(curve_points.size() - 1)) {
+ new_spline_positions[i] = positions[curve_points[i]];
+ }
+ new_spline_positions.last() = new_last_point_position;
+ new_spline.mark_cache_invalid();
+
+ for (const int i : IndexRange(curve_points.size())) {
+ const float factor = point_factors[i];
+ const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor);
+ const float index_factor = lookup.evaluated_index + lookup.factor;
+ float3 p;
+ new_spline.sample_with_index_factors<float3>(
+ new_spline_positions, {&index_factor, 1}, {&p, 1});
+ positions[curve_points[i]] = p;
+ }
+ }
+};
+
+/**
+ * Change the length of curves by scaling them uniformly.
+ */
+class ScaleCurvesEffect : public CurvesEffect {
+ private:
+ bool scale_up_;
+ Brush &brush_;
+
+ public:
+ ScaleCurvesEffect(bool scale_up, Brush &brush) : scale_up_(scale_up), brush_(brush)
+ {
+ }
+
+ void execute(CurvesGeometry &curves,
+ const Span<int> curve_indices,
+ const Span<float> move_distances_cu) override
+ {
+ MutableSpan<float3> positions_cu = curves.positions();
+ threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int influence_i : range) {
+ const int curve_i = curve_indices[influence_i];
+ const float move_distance_cu = move_distances_cu[influence_i];
+ const IndexRange points = curves.points_for_curve(curve_i);
+
+ const float old_length = this->compute_poly_curve_length(positions_cu.slice(points));
+ const float length_diff = scale_up_ ? move_distance_cu : -move_distance_cu;
+ const float min_length = brush_.curves_sculpt_settings->minimum_length;
+ const float new_length = std::max(min_length, old_length + length_diff);
+ const float scale_factor = safe_divide(new_length, old_length);
+
+ const float3 &root_pos_cu = positions_cu[points[0]];
+ for (float3 &pos_cu : positions_cu.slice(points.drop_front(1))) {
+ pos_cu = (pos_cu - root_pos_cu) * scale_factor + root_pos_cu;
+ }
+ }
+ });
+ }
+
+ float compute_poly_curve_length(const Span<float3> positions)
+ {
+ float length = 0.0f;
+ const int segments_num = positions.size() - 1;
+ for (const int segment_i : IndexRange(segments_num)) {
+ const float3 &p1 = positions[segment_i];
+ const float3 &p2 = positions[segment_i + 1];
+ length += math::distance(p1, p2);
+ }
+ return length;
+ }
+};
+
+class CurvesEffectOperation : public CurvesSculptStrokeOperation {
+ private:
+ std::unique_ptr<CurvesEffect> effect_;
+ float2 last_mouse_position_;
+ CurvesBrush3D brush_3d_;
+
+ friend struct CurvesEffectOperationExecutor;
+
+ public:
+ CurvesEffectOperation(std::unique_ptr<CurvesEffect> effect) : effect_(std::move(effect))
+ {
+ }
+
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * Utility class that actually executes the update when the stroke is updated. That's useful
+ * because it avoids passing a very large number of parameters between functions.
+ */
+struct CurvesEffectOperationExecutor {
+ CurvesEffectOperation *self_ = nullptr;
+ Depsgraph *depsgraph_ = nullptr;
+ Scene *scene_ = nullptr;
+ Object *object_ = nullptr;
+ ARegion *region_ = nullptr;
+ View3D *v3d_ = nullptr;
+ RegionView3D *rv3d_ = nullptr;
+
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ Brush *brush_ = nullptr;
+ float brush_radius_re_;
+ float brush_radius_sq_re_;
+ float brush_strength_;
+ eBrushFalloffShape falloff_shape_;
+
+ float4x4 curves_to_world_mat_;
+ float4x4 world_to_curves_mat_;
+
+ float2 brush_pos_start_re_;
+ float2 brush_pos_end_re_;
+
+ struct Influences {
+ Vector<int> curve_indices;
+ Vector<float> move_distances_cu;
+ };
+
+ void execute(CurvesEffectOperation &self, bContext *C, const StrokeExtension &stroke_extension)
+ {
+ BLI_SCOPED_DEFER([&]() { self.last_mouse_position_ = stroke_extension.mouse_position; });
+
+ self_ = &self;
+ depsgraph_ = CTX_data_depsgraph_pointer(C);
+ scene_ = CTX_data_scene(C);
+ object_ = CTX_data_active_object(C);
+ region_ = CTX_wm_region(C);
+ v3d_ = CTX_wm_view3d(C);
+ rv3d_ = CTX_wm_region_view3d(C);
+
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+
+ CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush(&curves_sculpt.paint);
+ brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+ brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
+ brush_radius_sq_re_ = pow2f(brush_radius_re_);
+ falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape);
+
+ curves_to_world_mat_ = object_->obmat;
+ world_to_curves_mat_ = curves_to_world_mat_.inverted();
+
+ brush_pos_start_re_ = self.last_mouse_position_;
+ brush_pos_end_re_ = stroke_extension.mouse_position;
+
+ if (stroke_extension.is_first) {
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ if (std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
+ *C, *object_, stroke_extension.mouse_position, brush_radius_re_)) {
+ self.brush_3d_ = *brush_3d;
+ }
+ }
+
+ return;
+ }
+
+ /* Compute influences. */
+ threading::EnumerableThreadSpecific<Influences> influences_for_thread;
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->gather_influences_projected(influences_for_thread);
+ }
+ else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->gather_influences_spherical(influences_for_thread);
+ }
+
+ /* Execute effect. */
+ threading::parallel_for_each(influences_for_thread, [&](const Influences &influences) {
+ BLI_assert(influences.curve_indices.size() == influences.move_distances_cu.size());
+ self_->effect_->execute(*curves_, influences.curve_indices, influences.move_distances_cu);
+ });
+
+ curves_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region_);
+ }
+
+ void gather_influences_projected(
+ threading::EnumerableThreadSpecific<Influences> &influences_for_thread)
+ {
+ const Span<float3> positions_cu = curves_->positions();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ Influences &local_influences = influences_for_thread.local();
+
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ const int tot_segments = points.size() - 1;
+ float max_move_distance_cu = 0.0f;
+ for (const int segment_i : IndexRange(tot_segments)) {
+ const float3 &p1_cu = positions_cu[points[segment_i]];
+ const float3 &p2_cu = positions_cu[points[segment_i] + 1];
+
+ float2 p1_re, p2_re;
+ ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values);
+ ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values);
+
+ float2 closest_on_brush_re;
+ float2 closest_on_segment_re;
+ float lambda_on_brush;
+ float lambda_on_segment;
+ const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re,
+ closest_on_segment_re,
+ &lambda_on_brush,
+ &lambda_on_segment,
+ brush_pos_start_re_,
+ brush_pos_end_re_,
+ p1_re,
+ p2_re);
+
+ if (dist_to_brush_sq_re > brush_radius_sq_re_) {
+ continue;
+ }
+
+ const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, dist_to_brush_re, brush_radius_re_);
+ const float weight = brush_strength_ * radius_falloff;
+
+ const float3 closest_on_segment_cu = math::interpolate(p1_cu, p2_cu, lambda_on_segment);
+
+ float3 brush_start_pos_wo, brush_end_pos_wo;
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * closest_on_segment_cu,
+ brush_pos_start_re_,
+ brush_start_pos_wo);
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * closest_on_segment_cu,
+ brush_pos_end_re_,
+ brush_end_pos_wo);
+ const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo;
+ const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo;
+
+ const float move_distance_cu = weight *
+ math::distance(brush_start_pos_cu, brush_end_pos_cu);
+ max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu);
+ }
+ if (max_move_distance_cu > 0.0f) {
+ local_influences.curve_indices.append(curve_i);
+ local_influences.move_distances_cu.append(max_move_distance_cu);
+ }
+ }
+ });
+ }
+
+ void gather_influences_spherical(
+ threading::EnumerableThreadSpecific<Influences> &influences_for_thread)
+ {
+ const Span<float3> positions_cu = curves_->positions();
+
+ float3 brush_pos_start_wo, brush_pos_end_wo;
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ brush_pos_start_re_,
+ brush_pos_start_wo);
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ brush_pos_end_re_,
+ brush_pos_end_wo);
+ const float3 brush_pos_start_cu = world_to_curves_mat_ * brush_pos_start_wo;
+ const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo;
+ const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu;
+ const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu);
+ const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ Influences &local_influences = influences_for_thread.local();
+
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ const int tot_segments = points.size() - 1;
+ float max_move_distance_cu = 0.0f;
+ for (const int segment_i : IndexRange(tot_segments)) {
+ const float3 &p1_cu = positions_cu[points[segment_i]];
+ const float3 &p2_cu = positions_cu[points[segment_i] + 1];
+
+ float3 closest_on_segment_cu;
+ float3 closest_on_brush_cu;
+ isect_seg_seg_v3(p1_cu,
+ p2_cu,
+ brush_pos_start_cu,
+ brush_pos_end_cu,
+ closest_on_segment_cu,
+ closest_on_brush_cu);
+
+ const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu,
+ closest_on_brush_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+
+ const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, dist_to_brush_cu, brush_radius_cu);
+ const float weight = brush_strength_ * radius_falloff;
+
+ const float move_distance_cu = weight * brush_pos_diff_length_cu;
+ max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu);
+ }
+ if (max_move_distance_cu > 0.0f) {
+ local_influences.curve_indices.append(curve_i);
+ local_influences.move_distances_cu.append(max_move_distance_cu);
+ }
+ }
+ });
+ }
+};
+
+void CurvesEffectOperation::on_stroke_extended(bContext *C,
+ const StrokeExtension &stroke_extension)
+{
+ CurvesEffectOperationExecutor executor;
+ executor.execute(*this, C, stroke_extension);
+}
+
+std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation(
+ const BrushStrokeMode brush_mode, bContext *C)
+{
+ Scene &scene = *CTX_data_scene(C);
+ Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint);
+ const bool use_scale_uniform = brush.curves_sculpt_settings->flag &
+ BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM;
+ const bool use_grow = (brush_mode == BRUSH_STROKE_INVERT) == ((brush.flag & BRUSH_DIR_IN) != 0);
+
+ if (use_grow) {
+ if (use_scale_uniform) {
+ return std::make_unique<CurvesEffectOperation>(
+ std::make_unique<ScaleCurvesEffect>(true, brush));
+ }
+ return std::make_unique<CurvesEffectOperation>(std::make_unique<ExtrapolateCurvesEffect>());
+ }
+ if (use_scale_uniform) {
+ return std::make_unique<CurvesEffectOperation>(
+ std::make_unique<ScaleCurvesEffect>(false, brush));
+ }
+ return std::make_unique<CurvesEffectOperation>(std::make_unique<ShrinkCurvesEffect>(brush));
+}
+
+} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
index d021627921f..03413221907 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -5,6 +5,7 @@
#include <optional>
#include "curves_sculpt_intern.h"
+#include "paint_intern.h"
#include "BLI_math_vector.hh"
@@ -36,6 +37,8 @@ std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation(
+ const BrushStrokeMode brush_mode, bContext *C);
struct CurvesBrush3D {
float3 position_cu;
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index 208db92abf7..893b2640427 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -74,118 +74,6 @@ using blender::bke::CurvesGeometry;
/** \name * SCULPT_CURVES_OT_brush_stroke
* \{ */
-/**
- * Resamples the curves to a shorter length.
- */
-class ShrinkOperation : public CurvesSculptStrokeOperation {
- private:
- float2 last_mouse_position_;
-
- public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
- {
- BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; });
-
- if (stroke_extension.is_first) {
- return;
- }
-
- Scene &scene = *CTX_data_scene(C);
- Object &object = *CTX_data_active_object(C);
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
-
- CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
- const float brush_radius = BKE_brush_size_get(&scene, &brush);
- const float brush_strength = BKE_brush_alpha_get(&scene, &brush);
-
- const float4x4 ob_mat = object.obmat;
- const float4x4 ob_imat = ob_mat.inverted();
-
- float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
-
- Curves &curves_id = *static_cast<Curves *>(object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- MutableSpan<float3> positions = curves.positions();
-
- const float2 mouse_prev = last_mouse_position_;
- const float2 mouse_cur = stroke_extension.mouse_position;
- const float2 mouse_diff = mouse_cur - mouse_prev;
-
- threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
- for (const int curve_i : curves_range) {
- const IndexRange curve_points = curves.points_for_curve(curve_i);
- const int last_point_i = curve_points.last();
-
- const float3 old_tip_position = positions[last_point_i];
-
- float2 old_tip_position_screen;
- ED_view3d_project_float_v2_m4(
- region, old_tip_position, old_tip_position_screen, projection.values);
-
- const float distance_screen = math::distance(old_tip_position_screen, mouse_prev);
- if (distance_screen > brush_radius) {
- continue;
- }
-
- const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius);
- const float weight = brush_strength * radius_falloff;
-
- const float2 offset_tip_position_screen = old_tip_position_screen + weight * mouse_diff;
- float3 offset_tip_position;
- ED_view3d_win_to_3d(v3d,
- region,
- ob_mat * old_tip_position,
- offset_tip_position_screen,
- offset_tip_position);
- offset_tip_position = ob_imat * offset_tip_position;
- const float shrink_length = math::distance(offset_tip_position, old_tip_position);
-
- this->shrink_curve(positions, curve_points, shrink_length);
- }
- });
-
- curves.tag_positions_changed();
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region);
- }
-
- void shrink_curve(MutableSpan<float3> positions,
- const IndexRange curve_points,
- const float shrink_length) const
- {
- PolySpline spline;
- spline.resize(curve_points.size());
- MutableSpan<float3> spline_positions = spline.positions();
- spline_positions.copy_from(positions.slice(curve_points));
- spline.mark_cache_invalid();
- const float old_length = spline.length();
- const float new_length = std::max(0.0f, old_length - shrink_length);
- const float length_factor = new_length / old_length;
-
- Vector<float> old_point_lengths;
- old_point_lengths.append(0.0f);
- for (const int i : spline_positions.index_range().drop_back(1)) {
- const float3 &p1 = spline_positions[i];
- const float3 &p2 = spline_positions[i + 1];
- const float length = math::distance(p1, p2);
- old_point_lengths.append(old_point_lengths.last() + length);
- }
-
- for (const int i : spline_positions.index_range()) {
- const float eval_length = old_point_lengths[i] * length_factor;
- const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1});
- positions[curve_points[i]] = p;
- }
- }
-};
-
class DensityAddOperation : public CurvesSculptStrokeOperation {
private:
/** Contains the root points of the curves that existed before this operation started. */
@@ -612,8 +500,10 @@ class DensityAddOperation : public CurvesSculptStrokeOperation {
};
static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C,
- wmOperator *UNUSED(op))
+ wmOperator *op)
{
+ const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op->ptr, "mode"));
+
Scene &scene = *CTX_data_scene(C);
CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
@@ -626,10 +516,10 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte
return new_snake_hook_operation();
case CURVES_SCULPT_TOOL_ADD:
return new_add_operation();
+ case CURVES_SCULPT_TOOL_GROW_SHRINK:
+ return new_grow_shrink_operation(mode, C);
case CURVES_SCULPT_TOOL_TEST1:
return std::make_unique<DensityAddOperation>();
- case CURVES_SCULPT_TOOL_TEST2:
- return std::make_unique<ShrinkOperation>();
}
BLI_assert_unreachable();
return {};
@@ -674,7 +564,9 @@ static void stroke_update_step(bContext *C,
stroke_extension.is_first = false;
}
- op_data->operation->on_stroke_extended(C, stroke_extension);
+ if (op_data->operation) {
+ op_data->operation->on_stroke_extended(C, stroke_extension);
+ }
}
static void stroke_done(const bContext *C, PaintStroke *stroke)
@@ -796,7 +688,7 @@ static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
WM_toolsystem_update_from_context_view3d(C);
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
- return OPERATOR_CANCELLED;
+ return OPERATOR_FINISHED;
}
static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index d33cf70e117..87a271543d1 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -878,11 +878,12 @@ void ACTION_OT_keyframe_insert(wmOperatorType *ot)
/* ******************** Duplicate Keyframes Operator ************************* */
-static void duplicate_action_keys(bAnimContext *ac)
+static bool duplicate_action_keys(bAnimContext *ac)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
+ bool changed = false;
/* filter data */
if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
@@ -898,10 +899,11 @@ static void duplicate_action_keys(bAnimContext *ac)
/* loop through filtered data and delete selected keys */
for (ale = anim_data.first; ale; ale = ale->next) {
if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
- duplicate_fcurve_keys((FCurve *)ale->key_data);
+ changed |= duplicate_fcurve_keys((FCurve *)ale->key_data);
}
else if (ale->type == ANIMTYPE_GPLAYER) {
ED_gpencil_layer_frames_duplicate((bGPDlayer *)ale->data);
+ changed |= ED_gpencil_layer_frame_select_check((bGPDlayer *)ale->data);
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
ED_masklayer_frames_duplicate((MaskLayer *)ale->data);
@@ -915,6 +917,8 @@ static void duplicate_action_keys(bAnimContext *ac)
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
+
+ return changed;
}
/* ------------------- */
@@ -929,7 +933,9 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
}
/* duplicate keyframes */
- duplicate_action_keys(&ac);
+ if (!duplicate_action_keys(&ac)) {
+ return OPERATOR_CANCELLED;
+ }
/* set notifier that keyframes have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 7eba3d49616..09163842587 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -225,6 +225,9 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
/* reset view matrix */
UI_view2d_view_restore(C);
+ /* gizmos */
+ WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D);
+
/* scrubbing region */
ED_time_scrub_draw(region, scene, saction->flag & SACTION_DRAWTIME, true);
}
@@ -861,7 +864,7 @@ void ED_spacetype_action(void)
art->draw_overlay = action_main_region_draw_overlay;
art->listener = action_main_region_listener;
art->message_subscribe = saction_main_region_message_subscribe;
- art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
+ art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
BLI_addhead(&st->regiontypes, art);
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index ceac53bde6b..08f4ecc0dd9 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -329,7 +329,7 @@ typedef struct FileListEntryCache {
int previews_todo_count;
} FileListEntryCache;
-/* FileListCache.flags */
+/** #FileListCache.flags */
enum {
FLC_IS_INIT = 1 << 0,
FLC_PREVIEWS_ACTIVE = 1 << 1,
@@ -359,7 +359,7 @@ typedef struct FileListFilter {
FileAssetCatalogFilterSettingsHandle *asset_catalog_filter;
} FileListFilter;
-/* FileListFilter.flags */
+/** #FileListFilter.flags */
enum {
FLF_DO_FILTER = 1 << 0,
FLF_HIDE_DOT = 1 << 1,
@@ -421,7 +421,7 @@ typedef struct FileList {
short tags; /* FileListTags */
} FileList;
-/* FileList.flags */
+/** #FileList.flags */
enum {
FL_FORCE_RESET = 1 << 0,
/* Don't do a full reset (unless #FL_FORCE_RESET is also set), only reset files representing main
@@ -434,7 +434,7 @@ enum {
FL_SORT_INVERT = 1 << 6,
};
-/* FileList.tags */
+/** #FileList.tags */
enum FileListTags {
/** The file list has references to main data (IDs) and needs special care. */
FILELIST_TAGS_USES_MAIN_DATA = (1 << 0),
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index bc82e7e20c2..2083a26f638 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -623,11 +623,12 @@ void GRAPH_OT_paste(wmOperatorType *ot)
/** \name Duplicate Keyframes Operator
* \{ */
-static void duplicate_graph_keys(bAnimContext *ac)
+static bool duplicate_graph_keys(bAnimContext *ac)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
+ bool changed = false;
/* Filter data. */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
@@ -636,13 +637,15 @@ static void duplicate_graph_keys(bAnimContext *ac)
/* Loop through filtered data and delete selected keys. */
for (ale = anim_data.first; ale; ale = ale->next) {
- duplicate_fcurve_keys((FCurve *)ale->key_data);
+ changed |= duplicate_fcurve_keys((FCurve *)ale->key_data);
ale->update |= ANIM_UPDATE_DEFAULT;
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
+
+ return changed;
}
/* ------------------- */
@@ -657,7 +660,9 @@ static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
}
/* Duplicate keyframes. */
- duplicate_graph_keys(&ac);
+ if (!duplicate_graph_keys(&ac)) {
+ return OPERATOR_CANCELLED;
+ }
/* Set notifier that keyframes have changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 68cfd1c9ef0..88ab7617932 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -2579,8 +2579,20 @@ static void reroute_node_draw(
const int x = BLI_rctf_cent_x(&node.totr) - (width / 2);
const int y = node.totr.ymax;
- uiBut *label_but = uiDefBut(
- &block, UI_BTYPE_LABEL, 0, showname, x, y, width, (short)NODE_DY, nullptr, 0, 0, 0, 0, nullptr);
+ uiBut *label_but = uiDefBut(&block,
+ UI_BTYPE_LABEL,
+ 0,
+ showname,
+ x,
+ y,
+ width,
+ (short)NODE_DY,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
UI_but_drawflag_disable(label_but, UI_BUT_TEXT_LEFT);
}
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 956bb581ee6..9d83f977fe0 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -1268,6 +1268,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
+ bool changed = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@@ -1280,6 +1281,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
bNode *new_node = blender::bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
+ changed = true;
}
/* make sure we don't copy new nodes again! */
@@ -1288,6 +1290,10 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
}
+ if (!changed) {
+ return OPERATOR_CANCELLED;
+ }
+
/* Copy links between selected nodes. */
bNodeLink *lastlink = (bNodeLink *)ntree->links.last;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index b77f780e413..0305ad279a0 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -1687,6 +1687,7 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
SEQ_select_active_set(scene, seq);
}
seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK);
+ seq->flag |= SEQ_IGNORE_CHANNEL_LOCK;
SEQ_animation_duplicate(scene, seq, &fcurves_original_backup);
SEQ_ensure_unique_name(seq, scene);
}
@@ -2457,6 +2458,9 @@ static void sequencer_copy_animation(Scene *scene, Sequence *seq)
}
GSet *fcurves = SEQ_fcurves_by_strip_get(seq, &scene->adt->action->curves);
+ if (fcurves == NULL) {
+ return;
+ }
GSET_FOREACH_BEGIN (FCurve *, fcu, fcurves) {
BLI_addtail(&fcurves_clipboard, BKE_fcurve_copy(fcu));
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 194aa518cd7..781aa521880 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -103,6 +103,7 @@ void draw_seq_strip_thumbnail(struct View2D *v2d,
float pixely);
/* sequencer_draw_channels.c */
+
void draw_channels(const struct bContext *C, struct ARegion *region);
void channel_draw_context_init(const struct bContext *C,
struct ARegion *region,
@@ -271,6 +272,7 @@ void SEQUENCER_OT_view_selected(struct wmOperatorType *ot);
void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot);
/* sequencer_channels_edit.c */
+
void SEQUENCER_OT_rename_channel(struct wmOperatorType *ot);
/* sequencer_preview.c */
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 66df1309d54..8e5931b127a 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -1700,7 +1700,9 @@ static int sequencer_box_select_invoke(bContext *C, wmOperator *op, const wmEven
if (tweak) {
int hand_dummy;
- Sequence *seq = find_nearest_seq(scene, v2d, &hand_dummy, event->mval);
+ int mval[2];
+ WM_event_drag_start_mval(event, region, mval);
+ Sequence *seq = find_nearest_seq(scene, v2d, &hand_dummy, mval);
if (seq != NULL) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 744fc61ddcf..e6895c0f4a3 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -1440,16 +1440,19 @@ void ED_view3d_to_object(const Depsgraph *depsgraph,
BKE_object_apply_mat4_ex(ob, mat, ob_eval->parent, ob_eval->parentinv, true);
}
-bool ED_view3d_camera_to_view_selected(struct Main *bmain,
- Depsgraph *depsgraph,
- const Scene *scene,
- Object *camera_ob)
+static bool view3d_camera_to_view_selected_impl(struct Main *bmain,
+ Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *camera_ob,
+ float *r_clip_start,
+ float *r_clip_end)
{
Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
float co[3]; /* the new location to apply */
float scale; /* only for ortho cameras */
- if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, co, &scale)) {
+ if (BKE_camera_view_frame_fit_to_scene(
+ depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) {
ObjectTfmProtectedChannels obtfm;
float obmat_new[4][4];
@@ -1475,6 +1478,38 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain,
return false;
}
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *camera_ob)
+{
+ return view3d_camera_to_view_selected_impl(bmain, depsgraph, scene, camera_ob, NULL, NULL);
+}
+
+bool ED_view3d_camera_to_view_selected_with_set_clipping(struct Main *bmain,
+ Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *camera_ob)
+{
+ float clip_start;
+ float clip_end;
+ if (view3d_camera_to_view_selected_impl(
+ bmain, depsgraph, scene, camera_ob, &clip_start, &clip_end)) {
+
+ ((Camera *)camera_ob->data)->clip_start = clip_start;
+ ((Camera *)camera_ob->data)->clip_end = clip_end;
+
+ /* TODO: Support update via #ID_RECALC_PARAMETERS. */
+ Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
+ ((Camera *)camera_ob_eval->data)->clip_start = clip_start;
+ ((Camera *)camera_ob_eval->data)->clip_end = clip_end;
+
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 16a96001fd2..990d3680057 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -606,6 +606,11 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc);
SEQ_collection_expand(seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, transformed_strips) {
+ seq->flag &= ~SEQ_IGNORE_CHANNEL_LOCK;
+ }
+
if (t->state == TRANS_CANCEL) {
seq_transform_cancel(t, transformed_strips);
SEQ_collection_free(transformed_strips);