tornavis/source/blender/editors/transform/transform_convert_graph.c

1046 lines
32 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include "DNA_anim_types.h"
#include "DNA_space_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_layer.h"
#include "BKE_nla.h"
#include "BKE_report.h"
#include "ED_anim_api.h"
#include "ED_keyframes_edit.h"
#include "ED_markers.h"
#include "UI_view2d.h"
#include "transform.h"
#include "transform_convert.h"
#include "transform_mode.h"
#include "transform_snap.h"
typedef struct TransDataGraph {
float unit_scale;
float offset;
} TransDataGraph;
/* -------------------------------------------------------------------- */
/** \name Graph Editor Transform Creation
* \{ */
/* Helper function for createTransGraphEditData, which is responsible for associating
* source data with transform data
*/
static void bezt_to_transdata(TransData *td,
TransData2D *td2d,
TransDataGraph *tdg,
AnimData *adt,
BezTriple *bezt,
int bi,
bool selected,
bool ishandle,
bool intvals,
const float mtx[3][3],
const float smtx[3][3],
float unit_scale,
float offset)
{
float *loc = bezt->vec[bi];
const float *cent = bezt->vec[1];
/* New location from td gets dumped onto the old-location of td2d, which then
* gets copied to the actual data at td2d->loc2d (bezt->vec[n])
*
* Due to NLA mapping, we apply NLA mapping to some of the verts here,
* and then that mapping will be undone after transform is done.
*/
if (adt) {
td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP);
td2d->loc[1] = (loc[1] + offset) * unit_scale;
td2d->loc[2] = 0.0f;
td2d->loc2d = loc;
td->loc = td2d->loc;
td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_MAP);
td->center[1] = (cent[1] + offset) * unit_scale;
td->center[2] = 0.0f;
copy_v3_v3(td->iloc, td->loc);
}
else {
td2d->loc[0] = loc[0];
td2d->loc[1] = (loc[1] + offset) * unit_scale;
td2d->loc[2] = 0.0f;
td2d->loc2d = loc;
td->loc = td2d->loc;
copy_v3_v3(td->center, cent);
td->center[1] = (td->center[1] + offset) * unit_scale;
copy_v3_v3(td->iloc, td->loc);
}
if (!ishandle) {
td2d->h1 = bezt->vec[0];
td2d->h2 = bezt->vec[2];
copy_v2_v2(td2d->ih1, td2d->h1);
copy_v2_v2(td2d->ih2, td2d->h2);
}
else {
td2d->h1 = NULL;
td2d->h2 = NULL;
}
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
td->ext = NULL;
td->val = NULL;
/* store AnimData info in td->extra, for applying mapping when flushing */
td->extra = adt;
if (selected) {
td->flag |= TD_SELECTED;
td->dist = 0.0f;
}
else {
td->dist = FLT_MAX;
}
if (ishandle) {
td->flag |= TD_NOTIMESNAP;
}
if (intvals) {
td->flag |= TD_INTVALUES;
}
/* copy space-conversion matrices for dealing with non-uniform scales */
copy_m3_m3(td->mtx, mtx);
copy_m3_m3(td->smtx, smtx);
tdg->unit_scale = unit_scale;
tdg->offset = offset;
}
static bool graph_edit_is_translation_mode(TransInfo *t)
{
return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE);
}
static bool graph_edit_use_local_center(TransInfo *t)
{
return ((t->around == V3D_AROUND_LOCAL_ORIGINS) && (graph_edit_is_translation_mode(t) == false));
}
/**
* Get the effective selection of a triple for transform, i.e. return if the left handle, right
* handle and/or the center point should be affected by transform.
*/
static void graph_bezt_get_transform_selection(const TransInfo *t,
const BezTriple *bezt,
const bool use_handle,
bool *r_left_handle,
bool *r_key,
bool *r_right_handle)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
bool key = (bezt->f2 & SELECT) != 0;
bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key;
bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key;
if (use_handle && t->is_launch_event_drag) {
if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT) {
key = right = false;
}
else if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT) {
left = key = false;
}
}
/* Whenever we move the key, we also move both handles. */
if (key) {
left = right = true;
}
*r_key = key;
*r_left_handle = left;
*r_right_handle = right;
}
static void graph_key_shortest_dist(
TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle)
{
int j = 0;
TransData *td_iter = td_start;
bool sel_key, sel_left, sel_right;
td->dist = FLT_MAX;
for (; j < fcu->totvert; j++) {
BezTriple *bezt = fcu->bezt + j;
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
if (sel_left || sel_key || sel_right) {
td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0]));
}
td_iter += 3;
}
}
}
/**
* It is important to note that this doesn't always act on the selection (like it's usually done),
* it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a
* left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the
* selected left or right handles accordingly.
* The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve
* functions may need to be made aware of this. It's ugly that these act based on selection state
* anyway.
*/
static void createTransGraphEditData(bContext *C, TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
Scene *scene = t->scene;
ARegion *region = t->region;
View2D *v2d = &region->v2d;
TransData *td = NULL;
TransData2D *td2d = NULL;
TransDataGraph *tdg = NULL;
bAnimContext ac;
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
int filter;
BezTriple *bezt;
int count = 0, i;
float mtx[3][3], smtx[3][3];
const bool use_handle = !(sipo->flag & SIPO_NOHANDLES);
const bool use_local_center = graph_edit_use_local_center(t);
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
bool sel_key, sel_left, sel_right;
/* determine what type of data we are operating on */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return;
}
anim_map_flag |= ANIM_get_normalization_flags(&ac);
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* which side of the current frame should be allowed */
/* XXX we still want this mode, but how to get this using standard transform too? */
if (t->mode == TFM_TIME_EXTEND) {
t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra);
}
else {
/* normal transform - both sides of current frame are considered */
t->frame_side = 'B';
}
/* Loop 1: count how many BezTriples (specifically their verts)
* are selected (or should be edited). */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
FCurve *fcu = (FCurve *)ale->key_data;
float cfra;
int curvecount = 0;
bool selected = false;
/* F-Curve may not have any keyframes */
if (fcu->bezt == NULL) {
continue;
}
/* convert current-frame to action-time (slightly less accurate, especially under
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
cfra = (float)scene->r.cfra;
}
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
/* Only include BezTriples whose 'keyframe'
* occurs on the same side of the current frame as mouse. */
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
if (is_prop_edit) {
curvecount += 3;
if (sel_key || sel_left || sel_right) {
selected = true;
}
}
else {
if (sel_left) {
count++;
}
if (sel_right) {
count++;
}
/* only include main vert if selected */
if (sel_key && !use_local_center) {
count++;
}
}
}
}
if (is_prop_edit) {
if (selected) {
count += curvecount;
ale->tag = true;
}
}
}
/* stop if trying to build list if nothing selected */
if (count == 0) {
/* cleanup temp list */
ANIM_animdata_freelist(&anim_data);
return;
}
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* allocate memory for data */
tc->data_len = count;
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData (Graph Editor)");
/* For each 2d vert a 3d vector is allocated,
* so that they can be treated just as if they were 3d verts. */
tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D (Graph Editor)");
tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataGraph), "TransDataGraph");
tc->custom.type.use_free = true;
td = tc->data;
td2d = tc->data_2d;
tdg = tc->custom.type.data;
/* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */
unit_m3(mtx);
unit_m3(smtx);
if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
float xscale, yscale;
/* apply scale factors to x and y axes of space-conversion matrices */
UI_view2d_scale_get(v2d, &xscale, &yscale);
/* mtx is data to global (i.e. view) conversion */
mul_v3_fl(mtx[0], xscale);
mul_v3_fl(mtx[1], yscale);
/* smtx is global (i.e. view) to data conversion */
if (IS_EQF(xscale, 0.0f) == 0) {
mul_v3_fl(smtx[0], 1.0f / xscale);
}
if (IS_EQF(yscale, 0.0f) == 0) {
mul_v3_fl(smtx[1], 1.0f / yscale);
}
}
/* loop 2: build transdata arrays */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
FCurve *fcu = (FCurve *)ale->key_data;
bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0;
float unit_scale, offset;
float cfra;
/* F-Curve may not have any keyframes */
if (fcu->bezt == NULL || (is_prop_edit && ale->tag == 0)) {
continue;
}
/* convert current-frame to action-time (slightly less accurate, especially under
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
cfra = (float)scene->r.cfra;
}
unit_scale = ANIM_unit_mapping_get_factor(
ac.scene, ale->id, ale->key_data, anim_map_flag, &offset);
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
/* Ensure temp flag is cleared for all triples, we use it. */
bezt->f1 &= ~BEZT_FLAG_TEMP_TAG;
bezt->f2 &= ~BEZT_FLAG_TEMP_TAG;
bezt->f3 &= ~BEZT_FLAG_TEMP_TAG;
/* only include BezTriples whose 'keyframe' occurs on the same side
* of the current frame as mouse (if applicable) */
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
TransDataCurveHandleFlags *hdata = NULL;
graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
if (is_prop_edit) {
bool is_sel = (sel_key || sel_left || sel_right);
/* we always select all handles for proportional editing if central handle is selected */
initTransDataCurveHandles(td, bezt);
bezt_to_transdata(td++,
td2d++,
tdg++,
adt,
bezt,
0,
is_sel,
true,
intvals,
mtx,
smtx,
unit_scale,
offset);
initTransDataCurveHandles(td, bezt);
bezt_to_transdata(td++,
td2d++,
tdg++,
adt,
bezt,
1,
is_sel,
false,
intvals,
mtx,
smtx,
unit_scale,
offset);
initTransDataCurveHandles(td, bezt);
bezt_to_transdata(td++,
td2d++,
tdg++,
adt,
bezt,
2,
is_sel,
true,
intvals,
mtx,
smtx,
unit_scale,
offset);
if (is_sel) {
bezt->f1 |= BEZT_FLAG_TEMP_TAG;
bezt->f2 |= BEZT_FLAG_TEMP_TAG;
bezt->f3 |= BEZT_FLAG_TEMP_TAG;
}
}
else {
/* only include handles if selected, irrespective of the interpolation modes.
* also, only treat handles specially if the center point isn't selected.
*/
if (sel_left) {
hdata = initTransDataCurveHandles(td, bezt);
bezt_to_transdata(td++,
td2d++,
tdg++,
adt,
bezt,
0,
sel_left,
true,
intvals,
mtx,
smtx,
unit_scale,
offset);
bezt->f1 |= BEZT_FLAG_TEMP_TAG;
}
if (sel_right) {
if (hdata == NULL) {
hdata = initTransDataCurveHandles(td, bezt);
}
bezt_to_transdata(td++,
td2d++,
tdg++,
adt,
bezt,
2,
sel_right,
true,
intvals,
mtx,
smtx,
unit_scale,
offset);
bezt->f3 |= BEZT_FLAG_TEMP_TAG;
}
/* only include main vert if selected */
if (sel_key && !use_local_center) {
/* move handles relative to center */
if (graph_edit_is_translation_mode(t)) {
if (sel_left) {
td->flag |= TD_MOVEHANDLE1;
}
if (sel_right) {
td->flag |= TD_MOVEHANDLE2;
}
}
/* if handles were not selected, store their selection status */
if (!(sel_left) || !(sel_right)) {
if (hdata == NULL) {
hdata = initTransDataCurveHandles(td, bezt);
}
}
bezt_to_transdata(td++,
td2d++,
tdg++,
adt,
bezt,
1,
sel_key,
false,
intvals,
mtx,
smtx,
unit_scale,
offset);
bezt->f2 |= BEZT_FLAG_TEMP_TAG;
}
/* Special hack (must be done after #initTransDataCurveHandles(),
* as that stores handle settings to restore...):
*
* - Check if we've got entire BezTriple selected and we're scaling/rotating that point,
* then check if we're using auto-handles.
* - If so, change them auto-handles to aligned handles so that handles get affected too
*/
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) &&
ELEM(t->mode, TFM_ROTATION, TFM_RESIZE))
{
if (hdata && (sel_left) && (sel_right)) {
bezt->h1 = HD_ALIGN;
bezt->h2 = HD_ALIGN;
}
}
}
}
}
/* Sets handles based on the selection */
testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
}
if (is_prop_edit) {
/* loop 2: build transdata arrays */
td = tc->data;
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
FCurve *fcu = (FCurve *)ale->key_data;
TransData *td_start = td;
float cfra;
/* F-Curve may not have any keyframes */
if (fcu->bezt == NULL || (ale->tag == 0)) {
continue;
}
/* convert current-frame to action-time (slightly less accurate, especially under
* higher scaling ratios, but is faster than converting all points)
*/
if (adt) {
cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP);
}
else {
cfra = (float)scene->r.cfra;
}
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
/* only include BezTriples whose 'keyframe' occurs on the
* same side of the current frame as mouse (if applicable) */
if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
if (sel_left || sel_key) {
td->dist = td->rdist = 0.0f;
}
else {
graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
}
td++;
if (sel_key) {
td->dist = td->rdist = 0.0f;
}
else {
graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
}
td++;
if (sel_right || sel_key) {
td->dist = td->rdist = 0.0f;
}
else {
graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
}
td++;
}
}
}
}
/* cleanup temp list */
ANIM_animdata_freelist(&anim_data);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Graph Editor Transform Flush
* \{ */
static bool fcu_test_selected(FCurve *fcu)
{
BezTriple *bezt = fcu->bezt;
uint i;
if (bezt == NULL) { /* ignore baked */
return 0;
}
for (i = 0; i < fcu->totvert; i++, bezt++) {
if (BEZT_ISSEL_ANY(bezt)) {
return 1;
}
}
return 0;
}
/* This function is called on recalcData to apply the transforms applied
* to the transdata on to the actual keyframe data
*/
static void flushTransGraphData(TransInfo *t)
{
TransData *td;
TransData2D *td2d;
TransDataGraph *tdg;
int a;
const short autosnap = getAnimEdit_SnapMode(t);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* flush to 2d vector from internally used 3d vector */
for (a = 0, td = tc->data, td2d = tc->data_2d, tdg = tc->custom.type.data; a < tc->data_len;
a++, td++, td2d++, tdg++)
{
/* pointers to relevant AnimData blocks are stored in the td->extra pointers */
AnimData *adt = (AnimData *)td->extra;
float inv_unit_scale = 1.0f / tdg->unit_scale;
/* Handle snapping for time values:
* - We should still be in NLA-mapping time-space.
* - Only apply to keyframes (but never to handles).
* - Don't do this when canceling, or else these changes won't go away.
*/
if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
transform_snap_anim_flush_data(t, td, autosnap, td->loc);
}
/* we need to unapply the nla-mapping from the time in some situations */
if (adt) {
td2d->loc2d[0] = BKE_nla_tweakedit_remap(adt, td2d->loc[0], NLATIME_CONVERT_UNMAP);
}
else {
td2d->loc2d[0] = td2d->loc[0];
}
/* if int-values only, truncate to integers */
if (td->flag & TD_INTVALUES) {
td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f);
}
else {
td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset;
}
transform_convert_flush_handle2D(td, td2d, inv_unit_scale);
}
}
/* struct for use in re-sorting BezTriples during Graph Editor transform */
typedef struct BeztMap {
BezTriple *bezt;
uint oldIndex; /* index of bezt in fcu->bezt array before sorting */
uint newIndex; /* index of bezt in fcu->bezt array after sorting */
short swapHs; /* swap order of handles (-1=clear; 0=not checked, 1=swap) */
char pipo, cipo; /* interpolation of current and next segments */
} BeztMap;
/* This function converts an FCurve's BezTriple array to a BeztMap array
* NOTE: this allocates memory that will need to get freed later
*/
static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert)
{
BezTriple *bezt = bezts;
BezTriple *prevbezt = NULL;
BeztMap *bezm, *bezms;
int i;
/* allocate memory for this array */
if (totvert == 0 || bezts == NULL) {
return NULL;
}
bezm = bezms = MEM_callocN(sizeof(BeztMap) * totvert, "BeztMaps");
/* assign beztriples to beztmaps */
for (i = 0; i < totvert; i++, bezm++, prevbezt = bezt, bezt++) {
bezm->bezt = bezt;
bezm->oldIndex = i;
bezm->newIndex = i;
bezm->pipo = (prevbezt) ? prevbezt->ipo : bezt->ipo;
bezm->cipo = bezt->ipo;
}
return bezms;
}
/* This function copies the code of sort_time_ipocurve, but acts on BeztMap structs instead */
static void sort_time_beztmaps(BeztMap *bezms, int totvert)
{
BeztMap *bezm;
int i, ok = 1;
/* keep repeating the process until nothing is out of place anymore */
while (ok) {
ok = 0;
bezm = bezms;
i = totvert;
while (i--) {
/* is current bezm out of order (i.e. occurs later than next)? */
if (i > 0) {
if (bezm->bezt->vec[1][0] > (bezm + 1)->bezt->vec[1][0]) {
bezm->newIndex++;
(bezm + 1)->newIndex--;
SWAP(BeztMap, *bezm, *(bezm + 1));
ok = 1;
}
}
/* do we need to check if the handles need to be swapped?
* optimization: this only needs to be performed in the first loop
*/
if (bezm->swapHs == 0) {
if ((bezm->bezt->vec[0][0] > bezm->bezt->vec[1][0]) &&
(bezm->bezt->vec[2][0] < bezm->bezt->vec[1][0]))
{
/* handles need to be swapped */
bezm->swapHs = 1;
}
else {
/* handles need to be cleared */
bezm->swapHs = -1;
}
}
bezm++;
}
}
}
/* This function firstly adjusts the pointers that the transdata has to each BezTriple */
static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totvert)
{
BezTriple *bezts = fcu->bezt;
BeztMap *bezm;
TransData2D *td2d;
TransData *td;
int i, j;
char *adjusted;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* dynamically allocate an array of chars to mark whether an TransData's
* pointers have been fixed already, so that we don't override ones that are
* already done
*/
adjusted = MEM_callocN(tc->data_len, "beztmap_adjusted_map");
/* for each beztmap item, find if it is used anywhere */
bezm = bezms;
for (i = 0; i < totvert; i++, bezm++) {
/* loop through transdata, testing if we have a hit
* for the handles (vec[0]/vec[2]), we must also check if they need to be swapped...
*/
td2d = tc->data_2d;
td = tc->data;
for (j = 0; j < tc->data_len; j++, td2d++, td++) {
/* skip item if already marked */
if (adjusted[j] != 0) {
continue;
}
/* update all transdata pointers, no need to check for selections etc,
* since only points that are really needed were created as transdata
*/
if (td2d->loc2d == bezm->bezt->vec[0]) {
if (bezm->swapHs == 1) {
td2d->loc2d = (bezts + bezm->newIndex)->vec[2];
}
else {
td2d->loc2d = (bezts + bezm->newIndex)->vec[0];
}
adjusted[j] = 1;
}
else if (td2d->loc2d == bezm->bezt->vec[2]) {
if (bezm->swapHs == 1) {
td2d->loc2d = (bezts + bezm->newIndex)->vec[0];
}
else {
td2d->loc2d = (bezts + bezm->newIndex)->vec[2];
}
adjusted[j] = 1;
}
else if (td2d->loc2d == bezm->bezt->vec[1]) {
td2d->loc2d = (bezts + bezm->newIndex)->vec[1];
/* if only control point is selected, the handle pointers need to be updated as well */
if (td2d->h1) {
td2d->h1 = (bezts + bezm->newIndex)->vec[0];
}
if (td2d->h2) {
td2d->h2 = (bezts + bezm->newIndex)->vec[2];
}
adjusted[j] = 1;
}
/* the handle type pointer has to be updated too */
if (adjusted[j] && td->flag & TD_BEZTRIPLE && td->hdata) {
if (bezm->swapHs == 1) {
td->hdata->h1 = &(bezts + bezm->newIndex)->h2;
td->hdata->h2 = &(bezts + bezm->newIndex)->h1;
}
else {
td->hdata->h1 = &(bezts + bezm->newIndex)->h1;
td->hdata->h2 = &(bezts + bezm->newIndex)->h2;
}
}
}
}
/* free temp memory used for 'adjusted' array */
MEM_freeN(adjusted);
}
/* This function is called by recalcData during the Transform loop to recalculate
* the handles of curves and sort the keyframes so that the curves draw correctly.
* It is only called if some keyframes have moved out of order.
*
* anim_data is the list of channels (F-Curves) retrieved already containing the
* channels to work on. It should not be freed here as it may still need to be used.
*/
static void remake_graph_transdata(TransInfo *t, ListBase *anim_data)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
bAnimListElem *ale;
const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
/* sort and reassign verts */
for (ale = anim_data->first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
if (fcu->bezt) {
BeztMap *bezm;
/* Adjust transform-data pointers. */
/* NOTE: none of these functions use 'use_handle', it could be removed. */
bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert);
sort_time_beztmaps(bezm, fcu->totvert);
beztmap_to_data(t, fcu, bezm, fcu->totvert);
/* free mapping stuff */
MEM_freeN(bezm);
/* re-sort actual beztriples (perhaps this could be done using the beztmaps to save time?) */
sort_time_fcurve(fcu);
/* make sure handles are all set correctly */
testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
}
}
}
static void recalcData_graphedit(TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
ViewLayer *view_layer = t->view_layer;
ListBase anim_data = {NULL, NULL};
bAnimContext ac = {NULL};
int filter;
bAnimListElem *ale;
int dosort = 0;
BKE_view_layer_synced_ensure(t->scene, t->view_layer);
/* initialize relevant anim-context 'context' data from TransInfo data */
/* NOTE: sync this with the code in ANIM_animdata_get_context() */
ac.bmain = CTX_data_main(t->context);
ac.scene = t->scene;
ac.view_layer = t->view_layer;
ac.obact = BKE_view_layer_active_object_get(view_layer);
ac.area = t->area;
ac.region = t->region;
ac.sl = (t->area) ? t->area->spacedata.first : NULL;
ac.spacetype = (t->area) ? t->area->spacetype : 0;
ac.regiontype = (t->region) ? t->region->regiontype : 0;
ANIM_animdata_context_getdata(&ac);
/* do the flush first */
flushTransGraphData(t);
/* get curves to check if a re-sort is needed */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* now test if there is a need to re-sort */
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
/* ignore FC-Curves without any selected verts */
if (!fcu_test_selected(fcu)) {
continue;
}
/* watch it: if the time is wrong: do not correct handles yet */
if (test_time_fcurve(fcu)) {
dosort++;
}
else {
BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_TEMP_TAG);
}
/* set refresh tags for objects using this animation,
* BUT only if realtime updates are enabled
*/
if ((sipo->flag & SIPO_NOREALTIMEUPDATES) == 0) {
ANIM_list_elem_update(CTX_data_main(t->context), t->scene, ale);
}
}
/* do resort and other updates? */
if (dosort) {
remake_graph_transdata(t, &anim_data);
}
/* now free temp channels */
ANIM_animdata_freelist(&anim_data);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Special After Transform Graph
* \{ */
static void special_aftertrans_update__graph(bContext *C, TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
bAnimContext ac;
const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
const bool canceled = (t->state == TRANS_CANCEL);
const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
/* initialize relevant anim-context 'context' data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return;
}
if (ac.datatype) {
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE |
ANIMFILTER_FCURVESONLY);
/* get channels to work on */
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
FCurve *fcu = (FCurve *)ale->key_data;
/* 3 cases here for curve cleanups:
* 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done
* 2) canceled == 0 -> user confirmed the transform,
* so duplicates should be removed
* 3) canceled + duplicate -> user canceled the transform,
* but we made duplicates, so get rid of these
*/
if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
BKE_fcurve_merge_duplicate_keys(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
}
else {
BKE_fcurve_merge_duplicate_keys(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
}
}
}
/* free temp memory */
ANIM_animdata_freelist(&anim_data);
}
/* Make sure all F-Curves are set correctly, but not if transform was
* canceled, since then curves were already restored to initial state.
* NOTE: if the refresh is really needed after cancel then some way
* has to be added to not update handle types (see bug 22289).
*/
if (!canceled) {
ANIM_editkeyframes_refresh(&ac);
}
}
/** \} */
TransConvertTypeInfo TransConvertType_Graph = {
/*flags*/ (T_POINTS | T_2D_EDIT),
/*createTransData*/ createTransGraphEditData,
/*recalcData*/ recalcData_graphedit,
/*special_aftertrans_update*/ special_aftertrans_update__graph,
};