d575c38e9d846aa2c507145582600c0da821a556
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / floatauto.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "autos.h"
23 #include "clip.h"
24 #include "edl.h"
25 #include "filexml.h"
26 #include "floatauto.h"
27 #include "floatautos.h"
28 #include "language.h"
29 #include "localsession.h"
30 #include "transportque.inc"
31 #include "automation.inc"
32
33 FloatAuto::FloatAuto(EDL *edl, FloatAutos *autos)
34  : Auto(edl, (Autos*)autos)
35 {
36         value = 0;
37         control_in_value = 0;
38         control_out_value = 0;
39         control_in_position = 0;
40         control_out_position = 0;
41         pos_valid = -1; //"dirty"
42         curve_mode = FREE;
43 //  note: in most cases the curve_mode value is set
44 //        by the method interpolate_from() rsp. copy_from()
45 }
46
47 FloatAuto::~FloatAuto()
48 {
49         // as we are going away, the neighbouring float auto nodes
50         // need to re-adjust their ctrl point positions and curves
51         if (next)
52                 ((FloatAuto*)next)->curve_dirty();
53         if (previous)
54                 ((FloatAuto*)previous)->curve_dirty();
55 }
56
57 int FloatAuto::operator==(Auto &that)
58 {
59         return identical((FloatAuto*)&that);
60 }
61
62
63 int FloatAuto::operator==(FloatAuto &that)
64 {
65         return identical((FloatAuto*)&that);
66 }
67
68 int FloatAuto::identical(FloatAuto *src)
69 {
70         return EQUIV(value, src->value) &&
71                 EQUIV(control_in_value, src->control_in_value) &&
72                 EQUIV(control_out_value, src->control_out_value);
73                 // ctrl positions ignored, as they may depend on neighbours
74                 // curve_mode is ignored, no recalculations
75 }
76
77 // exactly equals
78 int FloatAuto::equals(FloatAuto *that)
79 {
80         return this->value == that->value &&
81                 this->control_in_value == that->control_in_value &&
82                 this->control_out_value == that->control_out_value &&
83                 this->control_in_position == that->control_in_position &&
84                 this->control_out_position == that->control_out_position &&
85                 this->curve_mode == that->curve_mode;
86 }
87
88
89 /* Note: the following is essentially display-code and has been moved to:
90  *  TrackCanvas::value_to_percentage(float auto_value, int autogrouptype)
91  *
92 float FloatAuto::value_to_percentage()
93 {
94 }
95 float FloatAuto::value_to_percentage()
96 {
97 }
98 float FloatAuto::value_to_percentage()
99 {
100 }
101 */
102
103
104 void FloatAuto::copy_from(Auto *that)
105 {
106         copy_from((FloatAuto*)that);
107 }
108
109 void FloatAuto::copy_from(FloatAuto *that)
110 {
111         Auto::copy_from(that);
112         this->value = that->value;
113         this->control_in_value = that->control_in_value;
114         this->control_out_value = that->control_out_value;
115         this->control_in_position = that->control_in_position;
116         this->control_out_position = that->control_out_position;
117         this->curve_mode = that->curve_mode;
118 // note: literate copy, no recalculations
119 }
120
121 inline
122 void FloatAuto::handle_automatic_curve_after_copy()
123 // in most cases, we don't want to use the manual curve modes
124 // of the left neighbour used as a template for interpolation.
125 // Rather, we (re)set to automatically smoothed curves. Note
126 // auto generated nodes (while tweaking values) indeed are
127 // inserted by using this "interpolation" approach, thus making
128 // this defaulting to auto-smooth curves very important.
129 {
130         if(curve_mode == FREE || curve_mode == TFREE)
131         {
132                 this->curve_mode = SMOOTH;
133         }
134 }
135
136
137 int FloatAuto::interpolate_from(Auto *a1, Auto *a2, int64_t pos, Auto *templ)
138 // bézier interpolates this->value and curves for the given position
139 // between the positions of a1 and a2. If a1 or a2 are omitted, they default
140 // to this->previous and this->next. If this FloatAuto has automatic curves,
141 // this may trigger re-adjusting of this and its neighbours in this->autos.
142 // Note while a1 and a2 need not be members of this->autos, automatic
143 // readjustments are always done to the neighbours in this->autos.
144 // If the template is given, it will be used to fill out this
145 // objects fields prior to interpolating.
146 {
147         if(!a1) a1 = previous;
148         if(!a2) a2 = next;
149         Auto::interpolate_from(a1, a2, pos, templ);
150         if( !templ ) handle_automatic_curve_after_copy();
151         if( curve_mode == SMOOTH && a1 && a2 &&
152             a1->is_floatauto() && a2->is_floatauto() &&
153             a1->position <= pos && pos <= a2->position ) {
154                 // set this->value using bézier interpolation if possible
155                 FloatAuto *left = (FloatAuto*)a1;
156                 FloatAuto *right = (FloatAuto*)a2;
157                 if( pos != position ) { // this may trigger smoothing
158                         this->adjust_to_new_coordinates(pos,
159                                 FloatAutos::calculate_bezier(left, right, pos));
160                 }
161                 float new_slope = FloatAutos::calculate_bezier_derivation(left, right, pos);
162                 this->set_control_in_value(new_slope * control_in_position);
163                 this->set_control_out_value(new_slope * control_out_position);
164                 return 1; //return true: interpolated indeed...
165         }
166
167         adjust_ctrl_positions(); // implies adjust_curves()
168         return 0; // unable to interpolate
169 }
170
171
172 void FloatAuto::change_curve_mode(t_mode new_mode)
173 {
174         if(new_mode == TFREE && !(control_in_position && control_out_position))
175                 new_mode = FREE; // only if curves on both sides...
176
177         curve_mode = new_mode;
178         adjust_curves();
179 }
180
181 void FloatAuto::toggle_curve_mode()
182 {
183         switch (curve_mode) {
184         case SMOOTH:    change_curve_mode(TFREE);  break;
185         case LINEAR:    change_curve_mode(FREE);   break;
186         case TFREE :    change_curve_mode(LINEAR); break;
187         case FREE  :    change_curve_mode(SMOOTH); break;
188         }
189 }
190
191
192 void FloatAuto::set_value(float value)
193 {
194         float float_min = ((FloatAutos*)autos)->float_min;
195         if( value < float_min ) value = float_min;
196         float float_max = ((FloatAutos*)autos)->float_max;
197         if( value > float_max ) value = float_max;
198         this->value = value;
199         this->adjust_curves();
200         if(previous) ((FloatAuto*)previous)->adjust_curves();
201         if(next)     ((FloatAuto*)next)->adjust_curves();
202 }
203
204 void FloatAuto::set_control_in_value(float newvalue)
205 {
206         switch(curve_mode) {
207         case TFREE:     control_out_value = control_out_position*newvalue / control_in_position;
208         case FREE:      control_in_value = newvalue;
209         default:        return; // otherwise calculated automatically...
210         }
211 }
212
213 void FloatAuto::set_control_out_value(float newvalue)
214 {
215         switch(curve_mode) {
216         case TFREE:     control_in_value = control_in_position*newvalue / control_out_position;
217         case FREE:      control_out_value=newvalue;
218         default:        return;
219         }
220 }
221
222
223
224 inline int sgn(float value) { return (value == 0)?  0 : (value < 0) ? -1 : 1; }
225
226 inline float weighted_mean(float v1, float v2, float w1, float w2){
227         if(0.000001 > fabs(w1 + w2))
228                 return 0;
229         else
230                 return (w1 * v1 + w2 * v2) / (w1 + w2);
231 }
232
233
234
235
236 void FloatAuto::adjust_curves()
237 // recalculates curves if current mode
238 // implies automatic adjustment of curves
239 {
240         if(!autos) return;
241
242         if(curve_mode == SMOOTH) {
243                 // normally, one would use the slope of chord between the neighbours.
244                 // but this could cause the curve to overshot extremal automation nodes.
245                 // (e.g when setting a fade node at zero, the curve could go negative)
246                 // we can interpret the slope of chord as a weighted mean value, where
247                 // the length of the interval is used as weight; we just use other
248                 // weights: intervall length /and/ reciprocal of slope. So, if the
249                 // connection to one of the neighbours has very low slope this will
250                 // dominate the calculated curve slope at this automation node.
251                 // if the slope goes beyond the zero line, e.g if left connection
252                 // has positive and right connection has negative slope, then
253                 // we force the calculated curve to be horizontal.
254                 float s, dxl, dxr, sl, sr;
255                 calculate_slope((FloatAuto*) previous, this, sl, dxl);
256                 calculate_slope(this, (FloatAuto*) next, sr, dxr);
257
258                 if(0 < sgn(sl) * sgn(sr))
259                 {
260                         float wl = fabs(dxl) * (fabs(1.0/sl) + 1);
261                         float wr = fabs(dxr) * (fabs(1.0/sr) + 1);
262                         s = weighted_mean(sl, sr, wl, wr);
263                 }
264                 else s = 0; // fixed hoizontal curve
265
266                 control_in_value = s * control_in_position;
267                 control_out_value = s * control_out_position;
268         }
269
270         else if(curve_mode == LINEAR) {
271                 float g, dx;
272                 if(previous)
273                 {
274                         calculate_slope(this, (FloatAuto*)previous, g, dx);
275                         control_in_value = g * dx / 3;
276                 }
277                 if(next)
278                 {
279                         calculate_slope(this, (FloatAuto*)next, g, dx);
280                         control_out_value = g * dx / 3;
281         }       }
282
283         else if(curve_mode == TFREE && control_in_position && control_out_position) {
284                 float gl = control_in_value / control_in_position;
285                 float gr = control_out_value / control_out_position;
286                 float wl = fabs(control_in_value);
287                 float wr = fabs(control_out_value);
288                 float g = weighted_mean(gl, gr, wl, wr);
289
290                 control_in_value = g * control_in_position;
291                 control_out_value = g * control_out_position;
292         }
293 }
294
295 inline void FloatAuto::calculate_slope(FloatAuto *left, FloatAuto *right, float &dvdx, float &dx)
296 {
297         dvdx=0; dx=0;
298         if(!left || !right) return;
299
300         dx = right->position - left->position;
301         float dv = right->value - left->value;
302         dvdx = (dx == 0) ? 0 : dv/dx;
303 }
304
305
306 void FloatAuto::adjust_ctrl_positions(FloatAuto *prev, FloatAuto *next)
307 // recalculates location of ctrl points to be
308 // always 1/3 and 2/3 of the distance to the
309 // next neighbours. The reason is: for this special
310 // distance the bézier function yields x(t) = t, i.e.
311 // we can use the y(t) as if it was a simple function y(x).
312
313 // This adjustment is done only on demand and involves
314 // updating neighbours and adjust_curves() as well.
315 {
316         if(!prev && !next)
317         {       // use current siblings
318                 prev = (FloatAuto*)this->previous;
319                 next = (FloatAuto*)this->next;
320         }
321
322         if(prev)
323         {       set_ctrl_positions(prev, this);
324                 prev->adjust_curves();
325         }
326         else // disable curve on left side
327                 control_in_position = 0;
328
329         if(next)
330         {       set_ctrl_positions(this, next);
331                 next->adjust_curves();
332         }
333         else // disable right curve
334                 control_out_position = 0;
335
336         this->adjust_curves();
337         pos_valid = position;
338 // curves up-to-date
339 }
340
341
342
343 inline void redefine_curve(int64_t &old_pos, int64_t new_pos, float &ctrl_val)
344 {
345         if(old_pos != 0)
346                 ctrl_val *= (float)new_pos / old_pos;
347         old_pos = new_pos;
348 }
349
350
351 inline void FloatAuto::set_ctrl_positions(FloatAuto *prev, FloatAuto* next)
352 {
353         int64_t distance = next->position - prev->position;
354         redefine_curve(prev->control_out_position, +distance / 3,  prev->control_out_value);
355         redefine_curve(next->control_in_position,  -distance / 3,  next->control_in_value);
356 }
357
358
359
360 void FloatAuto::adjust_to_new_coordinates(int64_t position, float value)
361 // define new position and value in one step, do necessary re-adjustments
362 {
363         float float_min = ((FloatAutos*)autos)->float_min;
364         if( value < float_min ) value = float_min;
365         float float_max = ((FloatAutos*)autos)->float_max;
366         if( value > float_max ) value = float_max;
367         this->value = value;
368         this->position = position;
369         adjust_ctrl_positions();
370 }
371
372
373
374 int FloatAuto::value_to_str(char *string, float value)
375 {
376         int j = 0, i = 0;
377         if(value > 0)
378                 sprintf(string, "+%.2f", value);
379         else
380                 sprintf(string, "%.2f", value);
381
382 // fix number
383         if(value == 0)
384         {
385                 j = 0;
386                 string[1] = 0;
387         }
388         else
389         if(value < 1 && value > -1)
390         {
391                 j = 1;
392                 string[j] = string[0];
393         }
394         else
395         {
396                 j = 0;
397                 string[3] = 0;
398         }
399
400         while(string[j] != 0) string[i++] = string[j++];
401         string[i] = 0;
402
403         return 0;
404 }
405
406 void FloatAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
407 {
408         file->tag.set_title("AUTO");
409         if(default_auto)
410                 file->tag.set_property("POSITION", 0);
411         else
412                 file->tag.set_property("POSITION", position - start);
413         file->tag.set_property("VALUE", value);
414         file->tag.set_property("CONTROL_IN_VALUE", control_in_value / 2.0); // compatibility, see below
415         file->tag.set_property("CONTROL_OUT_VALUE", control_out_value / 2.0);
416         file->tag.set_property("TANGENT_MODE", (int)curve_mode);
417         file->append_tag();
418         file->tag.set_title("/AUTO");
419         file->append_tag();
420         file->append_newline();
421 }
422
423 void FloatAuto::load(FileXML *file)
424 {
425         value = file->tag.get_property("VALUE", value);
426         float float_min = ((FloatAutos*)autos)->float_min;
427         if( value < float_min ) value = float_min;
428         float float_max = ((FloatAutos*)autos)->float_max;
429         if( value > float_max ) value = float_max;
430         control_in_value = file->tag.get_property("CONTROL_IN_VALUE", control_in_value);
431         control_out_value = file->tag.get_property("CONTROL_OUT_VALUE", control_out_value);
432         curve_mode = (t_mode)file->tag.get_property("TANGENT_MODE", (int)FREE);
433
434         // Compatibility to old session data format:
435         // Versions previous to the bezier auto patch (Jun 2006) applied a factor 2
436         // to the y-coordinates of ctrl points while calculating the bezier function.
437         // To retain compatibility, we now apply this factor while loading
438         control_in_value *= 2.0;
439         control_out_value *= 2.0;
440
441 // restore ctrl positions and adjust curves if necessary
442         adjust_ctrl_positions();
443 }
444
445 const char *FloatAuto::curve_name(int curve_mode)
446 {
447         switch( curve_mode ) {
448         case FloatAuto::SMOOTH: return _("Smooth");
449         case FloatAuto::LINEAR: return _("Linear");
450         case FloatAuto::TFREE:  return _("Tangent");
451         case FloatAuto::FREE:   return _("Disjoint");
452         }
453         return _("Error");
454 }
455