Anim: add low-level function for simple FCurve key deduplication #107089
@ -571,4 +571,8 @@ class KeyframesCo:
|
|||||||
keyframe_points.foreach_set("co", co_buffer)
|
keyframe_points.foreach_set("co", co_buffer)
|
||||||
keyframe_points.foreach_set("interpolation", ipo_buffer)
|
keyframe_points.foreach_set("interpolation", ipo_buffer)
|
||||||
|
|
||||||
fcurve.update()
|
# TODO: in Blender 4.0 the next lines can be replaced with one call to `fcurve.update()`.
|
||||||
|
# See https://projects.blender.org/blender/blender/issues/107126 for more info.
|
||||||
|
keyframe_points.sort()
|
||||||
|
keyframe_points.deduplicate()
|
||||||
|
keyframe_points.handles_recalc()
|
||||||
|
@ -416,6 +416,7 @@ int BKE_fcurve_active_keyframe_index(const struct FCurve *fcu);
|
|||||||
* Move the indexed keyframe to the given value,
|
* Move the indexed keyframe to the given value,
|
||||||
* and move the handles with it to ensure the slope remains the same.
|
* and move the handles with it to ensure the slope remains the same.
|
||||||
*/
|
*/
|
||||||
|
void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time);
|
||||||
void BKE_fcurve_keyframe_move_value_with_handles(struct BezTriple *keyframe, float new_value);
|
void BKE_fcurve_keyframe_move_value_with_handles(struct BezTriple *keyframe, float new_value);
|
||||||
|
|
||||||
/* .............. */
|
/* .............. */
|
||||||
@ -472,6 +473,14 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
|
|||||||
struct BezTriple *next,
|
struct BezTriple *next,
|
||||||
float *r_pdelta);
|
float *r_pdelta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the FCurve 'bezt' array to fit the given length.
|
||||||
|
*
|
||||||
|
* \param new_totvert new number of elements in the FCurve's `bezt` array.
|
||||||
|
* Constraint: `0 <= new_totvert <= fcu->totvert`
|
||||||
|
*/
|
||||||
|
void BKE_fcurve_bezt_shrink(struct FCurve *fcu, int new_totvert);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a keyframe from an F-curve at a specific index.
|
* Delete a keyframe from an F-curve at a specific index.
|
||||||
*/
|
*/
|
||||||
@ -499,6 +508,21 @@ void BKE_fcurve_merge_duplicate_keys(struct FCurve *fcu,
|
|||||||
const int sel_flag,
|
const int sel_flag,
|
||||||
const bool use_handle);
|
const bool use_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the FCurve is a proper function, such that every X-coordinate of the
|
||||||
|
* timeline has only one value of the FCurve. In other words, removes duplicate
|
||||||
|
* keyframes.
|
||||||
|
*
|
||||||
|
* Contrary to #BKE_fcurve_merge_duplicate_keys, which is intended for
|
||||||
|
* interactive use, and where selection matters, this is a simpler deduplication
|
||||||
|
* where the last duplicate "wins".
|
||||||
|
*
|
||||||
|
* Assumes the keys are sorted (see #sort_time_fcurve).
|
||||||
|
*
|
||||||
|
* After deduplication, call `BKE_fcurve_handles_recalc(fcu);`
|
||||||
|
*/
|
||||||
|
void BKE_fcurve_deduplicate_keys(struct FCurve *fcu);
|
||||||
|
|
||||||
/* -------- Curve Sanity -------- */
|
/* -------- Curve Sanity -------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -883,6 +883,14 @@ int BKE_fcurve_active_keyframe_index(const FCurve *fcu)
|
|||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
|
void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time)
|
||||||
|
{
|
||||||
|
const float time_delta = new_time - keyframe->vec[1][0];
|
||||||
|
keyframe->vec[0][0] += time_delta;
|
||||||
|
keyframe->vec[1][0] = new_time;
|
||||||
|
keyframe->vec[2][0] += time_delta;
|
||||||
|
}
|
||||||
|
|
||||||
void BKE_fcurve_keyframe_move_value_with_handles(struct BezTriple *keyframe, const float new_value)
|
void BKE_fcurve_keyframe_move_value_with_handles(struct BezTriple *keyframe, const float new_value)
|
||||||
{
|
{
|
||||||
const float value_delta = new_value - keyframe->vec[1][1];
|
const float value_delta = new_value - keyframe->vec[1][1];
|
||||||
@ -1659,6 +1667,24 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BKE_fcurve_bezt_shrink(struct FCurve *fcu, const int new_totvert)
|
||||||
|
{
|
||||||
|
BLI_assert(new_totvert >= 0);
|
||||||
|
BLI_assert(new_totvert <= fcu->totvert);
|
||||||
|
|
||||||
|
/* No early return when new_totvert == fcu->totvert. There is no way to know the intention of the
|
||||||
|
* caller, nor the history of the FCurve so far, so `fcu->bezt` may actually have allocated space
|
||||||
|
* for more than `fcu->totvert` keys. */
|
||||||
|
|
||||||
|
if (new_totvert == 0) {
|
||||||
|
fcurve_bezt_free(fcu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fcu->bezt = MEM_reallocN(fcu->bezt, new_totvert * sizeof(*(fcu->bezt)));
|
||||||
|
fcu->totvert = new_totvert;
|
||||||
|
}
|
||||||
|
|
||||||
void BKE_fcurve_delete_key(FCurve *fcu, int index)
|
void BKE_fcurve_delete_key(FCurve *fcu, int index)
|
||||||
{
|
{
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
@ -1849,6 +1875,52 @@ void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool
|
|||||||
BLI_freelistN(&retained_keys);
|
BLI_freelistN(&retained_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BKE_fcurve_deduplicate_keys(FCurve *fcu)
|
||||||
|
{
|
||||||
|
BLI_assert_msg(fcu->bezt, "this function only works with regular (non-sampled) FCurves");
|
||||||
|
if (fcu->totvert < 2 || fcu->bezt == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prev_bezt_index = 0;
|
||||||
|
for (int i = 1; i < fcu->totvert; i++) {
|
||||||
|
BezTriple *bezt = &fcu->bezt[i];
|
||||||
|
BezTriple *prev_bezt = &fcu->bezt[prev_bezt_index];
|
||||||
|
|
||||||
|
const float bezt_x = bezt->vec[1][0];
|
||||||
|
const float prev_x = prev_bezt->vec[1][0];
|
||||||
|
|
||||||
|
if (bezt_x - prev_x <= BEZT_BINARYSEARCH_THRESH) {
|
||||||
|
/* Replace 'prev_bezt', as it has the same X-coord as 'bezt' and the last one wins. */
|
||||||
|
*prev_bezt = *bezt;
|
||||||
|
|
||||||
|
if (floor(bezt_x) == bezt_x) {
|
||||||
|
/* Keep the 'bezt_x' coordinate, as being on a frame is more desirable
|
||||||
|
* than being ever so slightly off. */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Move the retained key to the old X-coordinate, to 'anchor' the X-coordinate used for
|
||||||
|
* subsequente comparisons. Without this, the reference X-coordinate would keep moving
|
||||||
|
* forward in time, potentially merging in more keys than desired. */
|
||||||
|
BKE_fcurve_keyframe_move_time_with_handles(prev_bezt, prev_x);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Next iteration should look at the current element. However, because of the deletions, that
|
||||||
|
* may not be at index 'i'; after this increment, `prev_bezt_index` points at where the current
|
||||||
|
* element should go. */
|
||||||
|
prev_bezt_index++;
|
||||||
|
|
||||||
|
if (prev_bezt_index != i) {
|
||||||
|
/* This bezt should be kept, so copy it to its new location in the array. */
|
||||||
|
fcu->bezt[prev_bezt_index] = *bezt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_fcurve_bezt_shrink(fcu, prev_bezt_index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "DNA_anim_types.h"
|
#include "DNA_anim_types.h"
|
||||||
|
|
||||||
|
#include "BLI_math_vector_types.hh"
|
||||||
|
|
||||||
namespace blender::bke::tests {
|
namespace blender::bke::tests {
|
||||||
|
|
||||||
/* Epsilon for floating point comparisons. */
|
/* Epsilon for floating point comparisons. */
|
||||||
@ -346,6 +348,37 @@ TEST(BKE_fcurve, BKE_fcurve_keyframe_move_value_with_handles)
|
|||||||
BKE_fcurve_free(fcu);
|
BKE_fcurve_free(fcu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BKE_fcurve, BKE_fcurve_keyframe_move_time_with_handles)
|
||||||
|
{
|
||||||
|
FCurve *fcu = BKE_fcurve_create();
|
||||||
|
|
||||||
|
insert_vert_fcurve(fcu, 1.0f, 7.5f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
|
||||||
|
insert_vert_fcurve(fcu, 8.0f, 15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
|
||||||
|
insert_vert_fcurve(fcu, 14.0f, 8.2f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][0], 5.2671194f);
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][1], 15.0f);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], 8.0f);
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], 15.0f);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][0], 10.342469f);
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][1], 15.0f);
|
||||||
|
|
||||||
|
BKE_fcurve_keyframe_move_time_with_handles(&fcu->bezt[1], 47.0f);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][0], 44.2671194f) << "Left handle time should be updated";
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][1], 15.0f) << "Left handle should not move in value";
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], 47.0f) << "Frame time should have been updated";
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], 15.0f) << "Frame should not move in value";
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][0], 49.342469f) << "Right handle time should be updated";
|
||||||
|
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][1], 15.0f) << "Right handle should not move in value";
|
||||||
|
|
||||||
|
BKE_fcurve_free(fcu);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BKE_fcurve, BKE_fcurve_calc_range)
|
TEST(BKE_fcurve, BKE_fcurve_calc_range)
|
||||||
{
|
{
|
||||||
FCurve *fcu = BKE_fcurve_create();
|
FCurve *fcu = BKE_fcurve_create();
|
||||||
@ -546,4 +579,127 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||||||
BKE_fcurve_free(fcu);
|
BKE_fcurve_free(fcu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_key(FCurve *fcu, const int index, const float x, const float y)
|
||||||
|
{
|
||||||
|
fcu->bezt[index].vec[0][0] = x - 0.5f;
|
||||||
|
fcu->bezt[index].vec[1][0] = x;
|
||||||
|
fcu->bezt[index].vec[2][0] = x + 0.5f;
|
||||||
|
|
||||||
|
fcu->bezt[index].vec[0][1] = y;
|
||||||
|
fcu->bezt[index].vec[1][1] = y;
|
||||||
|
fcu->bezt[index].vec[2][1] = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FCurve *testcurve_with_duplicates()
|
||||||
|
{
|
||||||
|
/* Create a curve with some duplicate keys. The first ones are all with Y=1, the later repeats
|
||||||
|
* increase Y-coordinates on every repeat. */
|
||||||
|
FCurve *fcu = BKE_fcurve_create();
|
||||||
|
ED_keyframes_add(fcu, 10); /* Avoid `insert_vert_fcurve`, that deduplicates the keys. */
|
||||||
|
set_key(fcu, 0, 1.0f, 1.0f);
|
||||||
|
set_key(fcu, 1, 327.16f, 1.0f);
|
||||||
|
set_key(fcu, 2, 7.0f, 1.0f);
|
||||||
|
set_key(fcu, 3, 47.0f, 1.0f);
|
||||||
|
set_key(fcu, 4, 7.0f, 2.0f);
|
||||||
|
set_key(fcu, 5, 47.0f, 2.0f);
|
||||||
|
set_key(fcu, 6, 47.0f + BEZT_BINARYSEARCH_THRESH, 3.0f);
|
||||||
|
set_key(fcu, 7, 7.0f, 3.0f);
|
||||||
|
set_key(fcu, 8, 3.0f, 1.0f);
|
||||||
|
set_key(fcu, 9, 2.0f, 1.0f);
|
||||||
|
return fcu;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BKE_fcurve, sort_time_fcurve_stability)
|
||||||
|
{
|
||||||
|
FCurve *fcu = testcurve_with_duplicates();
|
||||||
|
ASSERT_EQ(fcu->totvert, 10);
|
||||||
|
|
||||||
|
sort_time_fcurve(fcu);
|
||||||
|
|
||||||
|
/* The sorting should be stable, i.e. retain the original order when the
|
||||||
|
* X-coordinates are identical. */
|
||||||
|
ASSERT_EQ(fcu->totvert, 10) << "sorting should not influence number of keys";
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(2.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(3.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(7.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[4].vec[1], float2(7.0f, 2.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[5].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[6].vec[1], float2(47.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[7].vec[1], float2(47.0f, 2.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[8].vec[1], float2(47.0f + BEZT_BINARYSEARCH_THRESH, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[9].vec[1], float2(327.16f, 1.0f), 1e-3);
|
||||||
|
|
||||||
|
BKE_fcurve_free(fcu);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BKE_fcurve, BKE_fcurve_deduplicate_keys)
|
||||||
|
{
|
||||||
|
FCurve *fcu = testcurve_with_duplicates();
|
||||||
|
ASSERT_EQ(fcu->totvert, 10);
|
||||||
|
sort_time_fcurve(fcu);
|
||||||
|
|
||||||
|
BKE_fcurve_deduplicate_keys(fcu);
|
||||||
|
ASSERT_GE(fcu->totvert, 6); /* Protect against out-of-bounds access. */
|
||||||
|
EXPECT_EQ(fcu->totvert, 6); /* The actual expected value. */
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(2.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(3.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[4].vec[1], float2(47.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[5].vec[1], float2(327.16f, 1.0f), 1e-3);
|
||||||
|
|
||||||
|
BKE_fcurve_free(fcu);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BKE_fcurve, BKE_fcurve_deduplicate_keys_edge_cases)
|
||||||
|
{
|
||||||
|
FCurve *fcu = testcurve_with_duplicates();
|
||||||
|
ASSERT_EQ(fcu->totvert, 10);
|
||||||
|
|
||||||
|
/* Update the 2nd and 2nd-to-last keys to test the edge cases. */
|
||||||
|
set_key(fcu, 0, 1, 1);
|
||||||
|
set_key(fcu, 1, 1, 2);
|
||||||
|
set_key(fcu, 8, 327.16f, 1);
|
||||||
|
set_key(fcu, 9, 327.16f, 2);
|
||||||
|
|
||||||
|
sort_time_fcurve(fcu);
|
||||||
|
|
||||||
|
BKE_fcurve_deduplicate_keys(fcu);
|
||||||
|
ASSERT_EQ(fcu->totvert, 4);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 2.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(47.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(327.16f, 2.0f), 1e-3);
|
||||||
|
|
||||||
|
BKE_fcurve_free(fcu);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BKE_fcurve, BKE_fcurve_deduplicate_keys_prefer_whole_frames)
|
||||||
|
{
|
||||||
|
FCurve *fcu = testcurve_with_duplicates();
|
||||||
|
ASSERT_EQ(fcu->totvert, 10);
|
||||||
|
|
||||||
|
/* Update the first key around 47.0 to be slightly before the frame. This gives us three keys on
|
||||||
|
* 47-epsilon, 47, and 47+epsilon. The keys at index 5 and 6 already have this value, so the
|
||||||
|
* `set_key` calls are unnecessary, but this way this test has a more local overview of the
|
||||||
|
* situation under test. */
|
||||||
|
set_key(fcu, 3, 47.0f - BEZT_BINARYSEARCH_THRESH, 1.0f);
|
||||||
|
set_key(fcu, 5, 47.0f, 2.0f);
|
||||||
|
set_key(fcu, 6, 47.0f + BEZT_BINARYSEARCH_THRESH, 3.0f);
|
||||||
|
|
||||||
|
sort_time_fcurve(fcu);
|
||||||
|
|
||||||
|
BKE_fcurve_deduplicate_keys(fcu);
|
||||||
|
ASSERT_EQ(fcu->totvert, 6);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(2.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(3.0f, 1.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[4].vec[1], float2(47.0f, 3.0f), 1e-3);
|
||||||
|
EXPECT_V2_NEAR(fcu->bezt[5].vec[1], float2(327.16f, 1.0f), 1e-3);
|
||||||
|
|
||||||
|
BKE_fcurve_free(fcu);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blender::bke::tests
|
} // namespace blender::bke::tests
|
||||||
|
@ -1659,6 +1659,29 @@ int insert_keyframe(Main *bmain,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ED_keyframes_add(FCurve *fcu, int num_keys_to_add)
|
||||||
|
{
|
||||||
|
BLI_assert_msg(num_keys_to_add >= 0, "cannot remove keyframes with this function");
|
||||||
|
|
||||||
|
if (num_keys_to_add == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fcu->bezt = MEM_recallocN(fcu->bezt, sizeof(BezTriple) * (fcu->totvert + num_keys_to_add));
|
||||||
|
BezTriple *bezt = fcu->bezt + fcu->totvert; /* Pointer to the first new one. '*/
|
||||||
|
|
||||||
|
fcu->totvert += num_keys_to_add;
|
||||||
|
|
||||||
|
/* Iterate over the new keys to update their settings. */
|
||||||
|
while (num_keys_to_add--) {
|
||||||
|
/* Defaults, ignoring user-preference gives predictable results for API. */
|
||||||
|
bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
|
||||||
|
bezt->ipo = BEZT_IPO_BEZ;
|
||||||
|
bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
|
||||||
|
bezt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************** */
|
/* ************************************************** */
|
||||||
/* KEYFRAME DELETION */
|
/* KEYFRAME DELETION */
|
||||||
|
|
||||||
|
@ -123,6 +123,17 @@ int insert_vert_fcurve(struct FCurve *fcu,
|
|||||||
eBezTriple_KeyframeType keyframe_type,
|
eBezTriple_KeyframeType keyframe_type,
|
||||||
eInsertKeyFlags flag);
|
eInsertKeyFlags flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given number of keyframes to the FCurve. Their coordinates are
|
||||||
|
* uninitialized, so the curve should not be used without further attention.
|
||||||
|
*
|
||||||
|
* The newly created keys are selected, existing keys are not touched.
|
||||||
|
*
|
||||||
|
* This can be used to allocate all the keys at once, and then update them
|
||||||
|
* afterwards.
|
||||||
|
*/
|
||||||
|
void ED_keyframes_add(struct FCurve *fcu, int num_keys_to_add);
|
||||||
|
|
||||||
/* -------- */
|
/* -------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -692,6 +692,7 @@ static void rna_tag_animation_update(Main *bmain, ID *id)
|
|||||||
static void rna_FCurve_update_data_ex(ID *id, FCurve *fcu, Main *bmain)
|
static void rna_FCurve_update_data_ex(ID *id, FCurve *fcu, Main *bmain)
|
||||||
{
|
{
|
||||||
sort_time_fcurve(fcu);
|
sort_time_fcurve(fcu);
|
||||||
|
/* TODO: Blender 4.0 should call BKE_fcurve_deduplicate_keys(fcu) here. */
|
||||||
BKE_fcurve_handles_recalc(fcu);
|
BKE_fcurve_handles_recalc(fcu);
|
||||||
|
|
||||||
rna_tag_animation_update(bmain, id);
|
rna_tag_animation_update(bmain, id);
|
||||||
@ -1071,25 +1072,13 @@ static BezTriple *rna_FKeyframe_points_insert(
|
|||||||
|
|
||||||
static void rna_FKeyframe_points_add(ID *id, FCurve *fcu, Main *bmain, int tot)
|
static void rna_FKeyframe_points_add(ID *id, FCurve *fcu, Main *bmain, int tot)
|
||||||
{
|
{
|
||||||
if (tot > 0) {
|
if (tot <= 0) {
|
||||||
BezTriple *bezt;
|
return;
|
||||||
|
|
||||||
fcu->bezt = MEM_recallocN(fcu->bezt, sizeof(BezTriple) * (fcu->totvert + tot));
|
|
||||||
|
|
||||||
bezt = fcu->bezt + fcu->totvert;
|
|
||||||
fcu->totvert += tot;
|
|
||||||
|
|
||||||
while (tot--) {
|
|
||||||
/* Defaults, ignoring user-preference gives predictable results for API. */
|
|
||||||
bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
|
|
||||||
bezt->ipo = BEZT_IPO_BEZ;
|
|
||||||
bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
|
|
||||||
bezt++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ED_keyframes_add(fcu, tot);
|
||||||
rna_tag_animation_update(bmain, id);
|
rna_tag_animation_update(bmain, id);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void rna_FKeyframe_points_remove(
|
static void rna_FKeyframe_points_remove(
|
||||||
ID *id, FCurve *fcu, Main *bmain, ReportList *reports, PointerRNA *bezt_ptr, bool do_fast)
|
ID *id, FCurve *fcu, Main *bmain, ReportList *reports, PointerRNA *bezt_ptr, bool do_fast)
|
||||||
@ -1118,6 +1107,24 @@ static void rna_FKeyframe_points_clear(ID *id, FCurve *fcu, Main *bmain)
|
|||||||
rna_tag_animation_update(bmain, id);
|
rna_tag_animation_update(bmain, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_FKeyframe_points_sort(ID *id, FCurve *fcu, Main *bmain)
|
||||||
|
{
|
||||||
|
sort_time_fcurve(fcu);
|
||||||
|
rna_tag_animation_update(bmain, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_FKeyframe_points_deduplicate(ID *id, FCurve *fcu, Main *bmain)
|
||||||
|
{
|
||||||
|
BKE_fcurve_deduplicate_keys(fcu);
|
||||||
|
rna_tag_animation_update(bmain, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_FKeyframe_points_handles_recalc(ID *id, FCurve *fcu, Main *bmain)
|
||||||
|
{
|
||||||
|
BKE_fcurve_handles_recalc(fcu);
|
||||||
|
rna_tag_animation_update(bmain, id);
|
||||||
|
}
|
||||||
|
|
||||||
static FCM_EnvelopeData *rna_FModifierEnvelope_points_add(
|
static FCM_EnvelopeData *rna_FModifierEnvelope_points_add(
|
||||||
ID *id, FModifier *fmod, Main *bmain, ReportList *reports, float frame)
|
ID *id, FModifier *fmod, Main *bmain, ReportList *reports, float frame)
|
||||||
{
|
{
|
||||||
@ -2414,6 +2421,22 @@ static void rna_def_fcurve_keyframe_points(BlenderRNA *brna, PropertyRNA *cprop)
|
|||||||
func = RNA_def_function(srna, "clear", "rna_FKeyframe_points_clear");
|
func = RNA_def_function(srna, "clear", "rna_FKeyframe_points_clear");
|
||||||
RNA_def_function_ui_description(func, "Remove all keyframes from an F-Curve");
|
RNA_def_function_ui_description(func, "Remove all keyframes from an F-Curve");
|
||||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "sort", "rna_FKeyframe_points_sort");
|
||||||
|
RNA_def_function_ui_description(func, "Ensure all keyframe points are chronologically sorted");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "deduplicate", "rna_FKeyframe_points_deduplicate");
|
||||||
|
RNA_def_function_ui_description(
|
||||||
|
func,
|
||||||
|
"Ensure there are no duplicate keys. Assumes that the points have already been sorted");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "handles_recalc", "rna_FKeyframe_points_handles_recalc");
|
||||||
|
RNA_def_function_ui_description(func,
|
||||||
|
"Update handles after modifications to the keyframe points, to "
|
||||||
|
"update things like auto-clamping");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_fcurve(BlenderRNA *brna)
|
static void rna_def_fcurve(BlenderRNA *brna)
|
||||||
|
Loading…
Reference in New Issue
Block a user