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