UI: Changes to Graph Editor selection and transform
[blender.git] / source / blender / editors / transform / transform_convert_graph.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edtransform
22  */
23
24 #include "DNA_anim_types.h"
25 #include "DNA_space_types.h"
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_math.h"
30
31 #include "BKE_context.h"
32 #include "BKE_fcurve.h"
33 #include "BKE_nla.h"
34 #include "BKE_report.h"
35
36 #include "ED_anim_api.h"
37 #include "ED_markers.h"
38
39 #include "UI_view2d.h"
40
41 #include "transform.h"
42 #include "transform_convert.h"
43
44 typedef struct TransDataGraph {
45   float unit_scale;
46   float offset;
47 } TransDataGraph;
48
49 /* -------------------------------------------------------------------- */
50 /** \name Graph Editor Transform Creation
51  *
52  * \{ */
53
54 /* Helper function for createTransGraphEditData, which is responsible for associating
55  * source data with transform data
56  */
57 static void bezt_to_transdata(TransData *td,
58                               TransData2D *td2d,
59                               TransDataGraph *tdg,
60                               AnimData *adt,
61                               BezTriple *bezt,
62                               int bi,
63                               bool selected,
64                               bool ishandle,
65                               bool intvals,
66                               const float mtx[3][3],
67                               const float smtx[3][3],
68                               float unit_scale,
69                               float offset)
70 {
71   float *loc = bezt->vec[bi];
72   const float *cent = bezt->vec[1];
73
74   /* New location from td gets dumped onto the old-location of td2d, which then
75    * gets copied to the actual data at td2d->loc2d (bezt->vec[n])
76    *
77    * Due to NLA mapping, we apply NLA mapping to some of the verts here,
78    * and then that mapping will be undone after transform is done.
79    */
80
81   if (adt) {
82     td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP);
83     td2d->loc[1] = (loc[1] + offset) * unit_scale;
84     td2d->loc[2] = 0.0f;
85     td2d->loc2d = loc;
86
87     td->loc = td2d->loc;
88     td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_MAP);
89     td->center[1] = (cent[1] + offset) * unit_scale;
90     td->center[2] = 0.0f;
91
92     copy_v3_v3(td->iloc, td->loc);
93   }
94   else {
95     td2d->loc[0] = loc[0];
96     td2d->loc[1] = (loc[1] + offset) * unit_scale;
97     td2d->loc[2] = 0.0f;
98     td2d->loc2d = loc;
99
100     td->loc = td2d->loc;
101     copy_v3_v3(td->center, cent);
102     td->center[1] = (td->center[1] + offset) * unit_scale;
103     copy_v3_v3(td->iloc, td->loc);
104   }
105
106   if (!ishandle) {
107     td2d->h1 = bezt->vec[0];
108     td2d->h2 = bezt->vec[2];
109     copy_v2_v2(td2d->ih1, td2d->h1);
110     copy_v2_v2(td2d->ih2, td2d->h2);
111   }
112   else {
113     td2d->h1 = NULL;
114     td2d->h2 = NULL;
115   }
116
117   memset(td->axismtx, 0, sizeof(td->axismtx));
118   td->axismtx[2][2] = 1.0f;
119
120   td->ext = NULL;
121   td->val = NULL;
122
123   /* store AnimData info in td->extra, for applying mapping when flushing */
124   td->extra = adt;
125
126   if (selected) {
127     td->flag |= TD_SELECTED;
128     td->dist = 0.0f;
129   }
130   else {
131     td->dist = FLT_MAX;
132   }
133
134   if (ishandle) {
135     td->flag |= TD_NOTIMESNAP;
136   }
137   if (intvals) {
138     td->flag |= TD_INTVALUES;
139   }
140
141   /* copy space-conversion matrices for dealing with non-uniform scales */
142   copy_m3_m3(td->mtx, mtx);
143   copy_m3_m3(td->smtx, smtx);
144
145   tdg->unit_scale = unit_scale;
146   tdg->offset = offset;
147 }
148
149 static bool graph_edit_is_translation_mode(TransInfo *t)
150 {
151   return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE);
152 }
153
154 static bool graph_edit_use_local_center(TransInfo *t)
155 {
156   return ((t->around == V3D_AROUND_LOCAL_ORIGINS) && (graph_edit_is_translation_mode(t) == false));
157 }
158
159 /**
160  * Get the effective selection of a triple for transform, i.e. return if the left handle, right
161  * handle and/or the center point should be affected by transform.
162  */
163 static void graph_bezt_get_transform_selection(const TransInfo *t,
164                                                const BezTriple *bezt,
165                                                const bool use_handle,
166                                                bool *r_left_handle,
167                                                bool *r_key,
168                                                bool *r_right_handle)
169 {
170   SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
171   bool key = (bezt->f2 & SELECT) != 0;
172   bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key;
173   bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key;
174
175   if (use_handle && t->is_launch_event_tweak) {
176     if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT) {
177       key = right = false;
178     }
179     else if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT) {
180       left = key = false;
181     }
182   }
183
184   /* Whenever we move the key, we also move both handles. */
185   if (key) {
186     left = right = true;
187   }
188
189   *r_key = key;
190   *r_left_handle = left;
191   *r_right_handle = right;
192 }
193
194 static void graph_key_shortest_dist(
195     TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle)
196 {
197   int j = 0;
198   TransData *td_iter = td_start;
199   bool sel_key, sel_left, sel_right;
200
201   td->dist = FLT_MAX;
202   for (; j < fcu->totvert; j++) {
203     BezTriple *bezt = fcu->bezt + j;
204     if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
205       graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
206
207       if (sel_left || sel_key || sel_right) {
208         td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0]));
209       }
210
211       td_iter += 3;
212     }
213   }
214 }
215
216 /**
217  * It is important to note that this doesn't always act on the selection (like it's usually done),
218  * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a
219  * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the
220  * selected left or right handles accordingly.
221  * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve
222  * functions may need to be made aware of this. It's ugly that these act based on selection state
223  * anyway.
224  */
225 void createTransGraphEditData(bContext *C, TransInfo *t)
226 {
227   SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
228   Scene *scene = t->scene;
229   ARegion *ar = t->ar;
230   View2D *v2d = &ar->v2d;
231
232   TransData *td = NULL;
233   TransData2D *td2d = NULL;
234   TransDataGraph *tdg = NULL;
235
236   bAnimContext ac;
237   ListBase anim_data = {NULL, NULL};
238   bAnimListElem *ale;
239   int filter;
240
241   BezTriple *bezt;
242   int count = 0, i;
243   float mtx[3][3], smtx[3][3];
244   const bool use_handle = !(sipo->flag & SIPO_NOHANDLES);
245   const bool use_local_center = graph_edit_use_local_center(t);
246   const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
247   short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
248   bool sel_key, sel_left, sel_right;
249
250   /* determine what type of data we are operating on */
251   if (ANIM_animdata_get_context(C, &ac) == 0) {
252     return;
253   }
254
255   anim_map_flag |= ANIM_get_normalization_flags(&ac);
256
257   /* filter data */
258   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
259   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
260
261   /* which side of the current frame should be allowed */
262   // XXX we still want this mode, but how to get this using standard transform too?
263   if (t->mode == TFM_TIME_EXTEND) {
264     /* only side on which mouse is gets transformed */
265     float xmouse, ymouse;
266
267     UI_view2d_region_to_view(v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
268     t->frame_side = (xmouse > CFRA) ? 'R' : 'L';  // XXX use t->frame_side
269   }
270   else {
271     /* normal transform - both sides of current frame are considered */
272     t->frame_side = 'B';
273   }
274
275   /* Loop 1: count how many BezTriples (specifically their verts)
276    * are selected (or should be edited). */
277   for (ale = anim_data.first; ale; ale = ale->next) {
278     AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
279     FCurve *fcu = (FCurve *)ale->key_data;
280     float cfra;
281     int curvecount = 0;
282     bool selected = false;
283
284     /* F-Curve may not have any keyframes */
285     if (fcu->bezt == NULL) {
286       continue;
287     }
288
289     /* convert current-frame to action-time (slightly less accurate, especially under
290      * higher scaling ratios, but is faster than converting all points)
291      */
292     if (adt) {
293       cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
294     }
295     else {
296       cfra = (float)CFRA;
297     }
298
299     for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
300       /* Only include BezTriples whose 'keyframe'
301        * occurs on the same side of the current frame as mouse. */
302       if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
303         graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
304
305         if (is_prop_edit) {
306           curvecount += 3;
307           if (sel_key || sel_left || sel_right) {
308             selected = true;
309           }
310         }
311         else {
312           if (sel_left) {
313             count++;
314           }
315
316           if (sel_right) {
317             count++;
318           }
319
320           /* only include main vert if selected */
321           if (sel_key && !use_local_center) {
322             count++;
323           }
324         }
325       }
326     }
327
328     if (is_prop_edit) {
329       if (selected) {
330         count += curvecount;
331         ale->tag = true;
332       }
333     }
334   }
335
336   /* stop if trying to build list if nothing selected */
337   if (count == 0) {
338     /* cleanup temp list */
339     ANIM_animdata_freelist(&anim_data);
340     return;
341   }
342
343   TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
344
345   /* allocate memory for data */
346   tc->data_len = count;
347
348   tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData (Graph Editor)");
349   /* For each 2d vert a 3d vector is allocated,
350    * so that they can be treated just as if they were 3d verts. */
351   tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D (Graph Editor)");
352   tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataGraph), "TransDataGraph");
353   tc->custom.type.use_free = true;
354
355   td = tc->data;
356   td2d = tc->data_2d;
357   tdg = tc->custom.type.data;
358
359   /* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */
360   unit_m3(mtx);
361   unit_m3(smtx);
362
363   if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
364     float xscale, yscale;
365
366     /* apply scale factors to x and y axes of space-conversion matrices */
367     UI_view2d_scale_get(v2d, &xscale, &yscale);
368
369     /* mtx is data to global (i.e. view) conversion */
370     mul_v3_fl(mtx[0], xscale);
371     mul_v3_fl(mtx[1], yscale);
372
373     /* smtx is global (i.e. view) to data conversion */
374     if (IS_EQF(xscale, 0.0f) == 0) {
375       mul_v3_fl(smtx[0], 1.0f / xscale);
376     }
377     if (IS_EQF(yscale, 0.0f) == 0) {
378       mul_v3_fl(smtx[1], 1.0f / yscale);
379     }
380   }
381
382   /* loop 2: build transdata arrays */
383   for (ale = anim_data.first; ale; ale = ale->next) {
384     AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
385     FCurve *fcu = (FCurve *)ale->key_data;
386     bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0;
387     float unit_scale, offset;
388     float cfra;
389
390     /* F-Curve may not have any keyframes */
391     if (fcu->bezt == NULL || (is_prop_edit && ale->tag == 0)) {
392       continue;
393     }
394
395     /* convert current-frame to action-time (slightly less accurate, especially under
396      * higher scaling ratios, but is faster than converting all points)
397      */
398     if (adt) {
399       cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
400     }
401     else {
402       cfra = (float)CFRA;
403     }
404
405     unit_scale = ANIM_unit_mapping_get_factor(
406         ac.scene, ale->id, ale->key_data, anim_map_flag, &offset);
407
408     for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
409       /* Ensure temp flag is cleared for all triples, we use it. */
410       bezt->f1 &= ~BEZT_FLAG_TEMP_TAG;
411       bezt->f2 &= ~BEZT_FLAG_TEMP_TAG;
412       bezt->f3 &= ~BEZT_FLAG_TEMP_TAG;
413
414       /* only include BezTriples whose 'keyframe' occurs on the same side
415        * of the current frame as mouse (if applicable) */
416       if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
417         TransDataCurveHandleFlags *hdata = NULL;
418
419         graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
420
421         if (is_prop_edit) {
422           bool is_sel = (sel_key || sel_left || sel_right);
423           /* we always select all handles for proportional editing if central handle is selected */
424           initTransDataCurveHandles(td, bezt);
425           bezt_to_transdata(td++,
426                             td2d++,
427                             tdg++,
428                             adt,
429                             bezt,
430                             0,
431                             is_sel,
432                             true,
433                             intvals,
434                             mtx,
435                             smtx,
436                             unit_scale,
437                             offset);
438           initTransDataCurveHandles(td, bezt);
439           bezt_to_transdata(td++,
440                             td2d++,
441                             tdg++,
442                             adt,
443                             bezt,
444                             1,
445                             is_sel,
446                             false,
447                             intvals,
448                             mtx,
449                             smtx,
450                             unit_scale,
451                             offset);
452           initTransDataCurveHandles(td, bezt);
453           bezt_to_transdata(td++,
454                             td2d++,
455                             tdg++,
456                             adt,
457                             bezt,
458                             2,
459                             is_sel,
460                             true,
461                             intvals,
462                             mtx,
463                             smtx,
464                             unit_scale,
465                             offset);
466
467           if (is_sel) {
468             bezt->f1 |= BEZT_FLAG_TEMP_TAG;
469             bezt->f2 |= BEZT_FLAG_TEMP_TAG;
470             bezt->f3 |= BEZT_FLAG_TEMP_TAG;
471           }
472         }
473         else {
474           /* only include handles if selected, irrespective of the interpolation modes.
475            * also, only treat handles specially if the center point isn't selected.
476            */
477           if (sel_left) {
478             hdata = initTransDataCurveHandles(td, bezt);
479             bezt_to_transdata(td++,
480                               td2d++,
481                               tdg++,
482                               adt,
483                               bezt,
484                               0,
485                               sel_left,
486                               true,
487                               intvals,
488                               mtx,
489                               smtx,
490                               unit_scale,
491                               offset);
492             bezt->f1 |= BEZT_FLAG_TEMP_TAG;
493           }
494
495           if (sel_right) {
496             if (hdata == NULL) {
497               hdata = initTransDataCurveHandles(td, bezt);
498             }
499             bezt_to_transdata(td++,
500                               td2d++,
501                               tdg++,
502                               adt,
503                               bezt,
504                               2,
505                               sel_right,
506                               true,
507                               intvals,
508                               mtx,
509                               smtx,
510                               unit_scale,
511                               offset);
512             bezt->f3 |= BEZT_FLAG_TEMP_TAG;
513           }
514
515           /* only include main vert if selected */
516           if (sel_key && !use_local_center) {
517             /* move handles relative to center */
518             if (graph_edit_is_translation_mode(t)) {
519               if (sel_left) {
520                 td->flag |= TD_MOVEHANDLE1;
521               }
522               if (sel_right) {
523                 td->flag |= TD_MOVEHANDLE2;
524               }
525             }
526
527             /* if handles were not selected, store their selection status */
528             if (!(sel_left) || !(sel_right)) {
529               if (hdata == NULL) {
530                 hdata = initTransDataCurveHandles(td, bezt);
531               }
532             }
533
534             bezt_to_transdata(td++,
535                               td2d++,
536                               tdg++,
537                               adt,
538                               bezt,
539                               1,
540                               sel_key,
541                               false,
542                               intvals,
543                               mtx,
544                               smtx,
545                               unit_scale,
546                               offset);
547             bezt->f2 |= BEZT_FLAG_TEMP_TAG;
548           }
549           /* Special hack (must be done after #initTransDataCurveHandles(),
550            * as that stores handle settings to restore...):
551            *
552            * - Check if we've got entire BezTriple selected and we're scaling/rotating that point,
553            *   then check if we're using auto-handles.
554            * - If so, change them auto-handles to aligned handles so that handles get affected too
555            */
556           if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) &&
557               ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
558             if (hdata && (sel_left) && (sel_right)) {
559               bezt->h1 = HD_ALIGN;
560               bezt->h2 = HD_ALIGN;
561             }
562           }
563         }
564       }
565     }
566
567     /* Sets handles based on the selection */
568     testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
569   }
570
571   if (is_prop_edit) {
572     /* loop 2: build transdata arrays */
573     td = tc->data;
574
575     for (ale = anim_data.first; ale; ale = ale->next) {
576       AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
577       FCurve *fcu = (FCurve *)ale->key_data;
578       TransData *td_start = td;
579       float cfra;
580
581       /* F-Curve may not have any keyframes */
582       if (fcu->bezt == NULL || (ale->tag == 0)) {
583         continue;
584       }
585
586       /* convert current-frame to action-time (slightly less accurate, especially under
587        * higher scaling ratios, but is faster than converting all points)
588        */
589       if (adt) {
590         cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
591       }
592       else {
593         cfra = (float)CFRA;
594       }
595
596       for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
597         /* only include BezTriples whose 'keyframe' occurs on the
598          * same side of the current frame as mouse (if applicable) */
599         if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
600           graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
601
602           if (sel_left || sel_key) {
603             td->dist = td->rdist = 0.0f;
604           }
605           else {
606             graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
607           }
608           td++;
609
610           if (sel_key) {
611             td->dist = td->rdist = 0.0f;
612           }
613           else {
614             graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
615           }
616           td++;
617
618           if (sel_right || sel_key) {
619             td->dist = td->rdist = 0.0f;
620           }
621           else {
622             graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
623           }
624           td++;
625         }
626       }
627     }
628   }
629
630   /* cleanup temp list */
631   ANIM_animdata_freelist(&anim_data);
632 }
633
634 /** \} */
635
636 /* -------------------------------------------------------------------- */
637 /** \name Graph Editor Transform Flush
638  *
639  * \{ */
640
641 /* this function is called on recalcData to apply the transforms applied
642  * to the transdata on to the actual keyframe data
643  */
644 void flushTransGraphData(TransInfo *t)
645 {
646   SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
647   TransData *td;
648   TransData2D *td2d;
649   TransDataGraph *tdg;
650   Scene *scene = t->scene;
651   double secf = FPS;
652   int a;
653
654   TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
655
656   /* flush to 2d vector from internally used 3d vector */
657   for (a = 0, td = tc->data, td2d = tc->data_2d, tdg = tc->custom.type.data; a < tc->data_len;
658        a++, td++, td2d++, tdg++) {
659     /* pointers to relevant AnimData blocks are stored in the td->extra pointers */
660     AnimData *adt = (AnimData *)td->extra;
661
662     float inv_unit_scale = 1.0f / tdg->unit_scale;
663
664     /* Handle snapping for time values:
665      * - We should still be in NLA-mapping time-space.
666      * - Only apply to keyframes (but never to handles).
667      * - Don't do this when canceling, or else these changes won't go away.
668      */
669     if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) {
670       switch (sipo->autosnap) {
671         case SACTSNAP_FRAME: /* snap to nearest frame */
672           td2d->loc[0] = floor((double)td2d->loc[0] + 0.5);
673           break;
674
675         case SACTSNAP_SECOND: /* snap to nearest second */
676           td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
677           break;
678
679         case SACTSNAP_MARKER: /* snap to nearest marker */
680           td2d->loc[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers,
681                                                                     td2d->loc[0]);
682           break;
683       }
684     }
685
686     /* we need to unapply the nla-mapping from the time in some situations */
687     if (adt) {
688       td2d->loc2d[0] = BKE_nla_tweakedit_remap(adt, td2d->loc[0], NLATIME_CONVERT_UNMAP);
689     }
690     else {
691       td2d->loc2d[0] = td2d->loc[0];
692     }
693
694     /** Time-stepping auto-snapping modes don't get applied for Graph Editor transforms,
695      * as these use the generic transform modes which don't account for this sort of thing.
696      * These ones aren't affected by NLA mapping, so we do this after the conversion...
697      *
698      * \note We also have to apply to td->loc,
699      * as that's what the handle-adjustment step below looks to,
700      * otherwise we get "swimming handles".
701      *
702      * \note We don't do this when canceling transforms, or else these changes don't go away.
703      */
704     if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 &&
705         ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP)) {
706       switch (sipo->autosnap) {
707         case SACTSNAP_STEP: /* frame step */
708           td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5);
709           td->loc[0] = floor((double)td->loc[0] + 0.5);
710           break;
711
712         case SACTSNAP_TSTEP: /* second step */
713           /* XXX: the handle behavior in this case is still not quite right... */
714           td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
715           td->loc[0] = floor(((double)td->loc[0] / secf) + 0.5) * secf;
716           break;
717       }
718     }
719
720     /* if int-values only, truncate to integers */
721     if (td->flag & TD_INTVALUES) {
722       td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f);
723     }
724     else {
725       td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset;
726     }
727
728     if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
729       td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
730       td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
731     }
732
733     if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
734       td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
735       td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
736     }
737   }
738 }
739
740 /** \} */