[Bf-blender-cvs] [17b0c06946b] master: Animation: Equalize Handle Operator

Kevin C. Burke noreply at git.blender.org
Tue Jan 25 11:42:24 CET 2022


Commit: 17b0c06946bef39e29e3b0b1a5faaf8faca35daa
Author: Kevin C. Burke
Date:   Tue Jan 25 11:40:46 2022 +0100
Branches: master
https://developer.blender.org/rB17b0c06946bef39e29e3b0b1a5faaf8faca35daa

Animation: Equalize Handle Operator

The Equalize Handles operator allows users to make selected handle
lengths uniform: either respecting their original angle from the key
control point or by flattening their angle (removing the overshoot
sometimes produced by certain handle types).

Design: T94172

Reviewed by: sybren

Differential Revision: https://developer.blender.org/D13702

===================================================================

M	release/scripts/startup/bl_ui/space_graph.py
M	source/blender/editors/animation/keyframes_edit.c
M	source/blender/editors/include/ED_keyframes_edit.h
M	source/blender/editors/space_graph/graph_edit.c
M	source/blender/editors/space_graph/graph_intern.h
M	source/blender/editors/space_graph/graph_ops.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index a9f2b9e9a36..db0020b7846 100644
--- a/release/scripts/startup/bl_ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -335,6 +335,7 @@ class GRAPH_MT_key_snap(Menu):
         layout.operator("graph.snap", text="Selection to Nearest Second").type = 'NEAREST_SECOND'
         layout.operator("graph.snap", text="Selection to Nearest Marker").type = 'NEAREST_MARKER'
         layout.operator("graph.snap", text="Flatten Handles").type = 'HORIZONTAL'
+        layout.operator("graph.equalize_handles", text="Equalize Handles").side = 'BOTH'
         layout.separator()
         layout.operator("graph.frame_jump", text="Cursor to Selection")
         layout.operator("graph.snap_cursor_value", text="Cursor Value to Selection")
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index 145d67b7810..dfe6566df67 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -1283,6 +1283,61 @@ static short set_bezt_sine(KeyframeEditData *UNUSED(ked), BezTriple *bezt)
   return 0;
 }
 
+static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
+{
+  BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index");
+
+  add_v2_v2v2(vec[idx], vec[1], direction);
+}
+
+static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
+{
+  BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index");
+
+  float handle_direction[2];
+  sub_v2_v2v2(handle_direction, vec[idx], vec[1]);
+  normalize_v2_length(handle_direction, handle_length);
+  add_v2_v2v2(vec[idx], vec[1], handle_direction);
+}
+
+void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu,
+                                         const eEditKeyframes_Equalize mode,
+                                         const float handle_length,
+                                         const bool flatten)
+{
+  uint i;
+  BezTriple *bezt;
+  const float flat_direction_left[2] = {-handle_length, 0.f};
+  const float flat_direction_right[2] = {handle_length, 0.f};
+
+  /* Loop through an F-Curves keyframes. */
+  for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
+    if ((bezt->f2 & SELECT) == 0) {
+      continue;
+    }
+
+    /* Perform handle equalization if mode is 'Both' or 'Left'. */
+    if (mode & EQUALIZE_HANDLES_LEFT) {
+      if (flatten) {
+        handle_flatten(bezt->vec, 0, flat_direction_left);
+      }
+      else {
+        handle_set_length(bezt->vec, 0, handle_length);
+      }
+    }
+
+    /* Perform handle equalization if mode is 'Both' or 'Right'. */
+    if (mode & EQUALIZE_HANDLES_RIGHT) {
+      if (flatten) {
+        handle_flatten(bezt->vec, 2, flat_direction_right);
+      }
+      else {
+        handle_set_length(bezt->vec, 2, handle_length);
+      }
+    }
+  }
+}
+
 KeyframeEditFunc ANIM_editkeyframes_ipo(short mode)
 {
   switch (mode) {
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index c7e89030ee2..f006378658b 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -93,6 +93,13 @@ typedef enum eEditKeyframes_Snap {
   SNAP_KEYS_TIME,
 } eEditKeyframes_Snap;
 
+/* equalizing tools */
+typedef enum eEditKeyframes_Equalize {
+  EQUALIZE_HANDLES_LEFT = (1 << 0),
+  EQUALIZE_HANDLES_RIGHT = (1 << 1),
+  EQUALIZE_HANDLES_BOTH = (EQUALIZE_HANDLES_LEFT | EQUALIZE_HANDLES_RIGHT),
+} eEditKeyframes_Equalize;
+
 /* mirroring tools */
 typedef enum eEditKeyframes_Mirror {
   MIRROR_KEYS_CURFRAME = 1,
@@ -258,6 +265,18 @@ short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked,
                                  KeyframeEditFunc key_ok,
                                  KeyframeEditFunc key_cb,
                                  FcuEditFunc fcu_cb);
+/**
+ * Sets selected keyframes' bezier handles to an equal length and optionally makes
+ * the keyframes' handles horizontal.
+ * \param handle_length: Desired handle length, must be positive.
+ * \param flatten: Makes the keyframes' handles the same value as the keyframe,
+ * flattening the curve at that point.
+ */
+void ANIM_fcurve_equalize_keyframes_loop(struct FCurve *fcu,
+                                         eEditKeyframes_Equalize mode,
+                                         float handle_length,
+                                         bool flatten);
+
 /**
  * Function for working with any type (i.e. one of the known types) of animation channel.
  */
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 2afee277847..9675901ead3 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -2352,6 +2352,103 @@ void GRAPH_OT_snap(wmOperatorType *ot)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Equalize Handles Operator
+ * \{ */
+
+/* Defines for equalize handles tool. */
+static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[] = {
+    {GRAPHKEYS_EQUALIZE_LEFT, "LEFT", 0, "Left", "Equalize selected keyframes' left handles"},
+    {GRAPHKEYS_EQUALIZE_RIGHT, "RIGHT", 0, "Right", "Equalize selected keyframes' right handles"},
+    {GRAPHKEYS_EQUALIZE_BOTH, "BOTH", 0, "Both", "Equalize both of a keyframe's handles"},
+    {0, NULL, 0, NULL, NULL},
+};
+
+/* ------------------- */
+
+/* Equalize selected keyframes' bezier handles. */
+static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten)
+{
+  /* Filter data. */
+  const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
+                      ANIMFILTER_NODUPLIS);
+  ListBase anim_data = {NULL, NULL};
+  ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+
+  /* Equalize keyframes. */
+  LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
+    ANIM_fcurve_equalize_keyframes_loop(ale->key_data, mode, handle_length, flatten);
+    ale->update |= ANIM_UPDATE_DEFAULT;
+  }
+
+  ANIM_animdata_update(ac, &anim_data);
+  ANIM_animdata_freelist(&anim_data);
+}
+
+static int graphkeys_equalize_handles_exec(bContext *C, wmOperator *op)
+{
+  bAnimContext ac;
+
+  /* Get editor data. */
+  if (ANIM_animdata_get_context(C, &ac) == 0) {
+    return OPERATOR_CANCELLED;
+  }
+
+  /* Get equalize mode. */
+  int mode = RNA_enum_get(op->ptr, "side");
+  float handle_length = RNA_float_get(op->ptr, "handle_length");
+  bool flatten = RNA_boolean_get(op->ptr, "flatten");
+
+  /* Equalize graph keyframes. */
+  equalize_graph_keys(&ac, mode, handle_length, flatten);
+
+  WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+
+  return OPERATOR_FINISHED;
+}
+
+void GRAPH_OT_equalize_handles(wmOperatorType *ot)
+{
+  /* Identifiers */
+  ot->name = "Equalize Handles";
+  ot->idname = "GRAPH_OT_equalize_handles";
+  ot->description =
+      "Ensure selected keyframes' handles have equal length, optionally making them horizontal";
+
+  /* API callbacks */
+  ot->invoke = WM_menu_invoke;
+  ot->exec = graphkeys_equalize_handles_exec;
+  ot->poll = graphop_editable_keyframes_poll;
+
+  /* Flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+  /* Properties */
+  ot->prop = RNA_def_enum(ot->srna,
+                          "side",
+                          prop_graphkeys_equalize_handles_sides,
+                          0,
+                          "Side",
+                          "Side of the keyframes' bezier handles to affect");
+  RNA_def_float(ot->srna,
+                "handle_length",
+                5.0f,
+                0.1f,
+                FLT_MAX,
+                "Handle Length",
+                "Length to make selected keyframes' bezier handles",
+                1.0f,
+                50.0f);
+  RNA_def_boolean(
+      ot->srna,
+      "flatten",
+      false,
+      "Flatten",
+      "Make the values of the selected keyframes' handles the same as their respective keyframes");
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Mirror Keyframes Operator
  * \{ */
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index 243a4ee4b95..a59fb63dd22 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -144,6 +144,7 @@ void GRAPH_OT_easing_type(struct wmOperatorType *ot);
 void GRAPH_OT_frame_jump(struct wmOperatorType *ot);
 void GRAPH_OT_snap_cursor_value(struct wmOperatorType *ot);
 void GRAPH_OT_snap(struct wmOperatorType *ot);
+void GRAPH_OT_equalize_handles(struct wmOperatorType *ot);
 void GRAPH_OT_mirror(struct wmOperatorType *ot);
 
 /* defines for snap keyframes
@@ -158,6 +159,15 @@ enum eGraphKeys_Snap_Mode {
   GRAPHKEYS_SNAP_VALUE,
 };
 
+/* Defines for equalize keyframe handles.
+ * NOTE: Keep in sync with eEditKeyframes_Equalize (in ED_keyframes_edit.h).
+ */
+enum eGraphKeys_Equalize_Mode {
+  GRAPHKEYS_EQUALIZE_LEFT = 1,
+  GRAPHKEYS_EQUALIZE_RIGHT,
+  GRAPHKEYS_EQUALIZE_BOTH,
+};
+
 /* defines for mirror keyframes
  * NOTE: keep in sync with eEditKeyframes_Mirror (in ED_keyframes_edit.h)
  */
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index fe4cffcb3b8..7606dcc60cf 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -456,6 +456,7 @@ void graphedit_operatortypes(void)
 
   /* editing */
   WM_operatortype_append(GRAPH_OT_snap);
+  WM_operatortype_append(GRAPH_OT_equalize_handles);
   WM_operatortype_append(GRAPH_OT_mirror);
   WM_operatortype_append(GRAPH_OT_frame_jump);
   WM_operatortype_append(GRAPH_OT_snap_cursor_value);



More information about the Bf-blender-cvs mailing list