4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5 * Copyright (C) 2003-2016 Cinelerra CV contributors
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "floatauto.h"
28 #include "floatautos.h"
30 #include "localsession.h"
31 #include "transportque.inc"
32 #include "automation.inc"
34 FloatAuto::FloatAuto(EDL *edl, FloatAutos *autos)
35 : Auto(edl, (Autos*)autos)
40 control_out_value = 0;
41 control_in_position = 0;
42 control_out_position = 0;
43 pos_valid = -1; //"dirty"
45 // note: in most cases the curve_mode value is set
46 // by the method interpolate_from() rsp. copy_from()
49 FloatAuto::~FloatAuto()
51 // as we are going away, the neighbouring float auto nodes
52 // need to re-adjust their ctrl point positions and curves
54 ((FloatAuto*)next)->curve_dirty();
56 ((FloatAuto*)previous)->curve_dirty();
59 int FloatAuto::operator==(Auto &that)
61 return identical((FloatAuto*)&that);
65 int FloatAuto::operator==(FloatAuto &that)
67 return identical((FloatAuto*)&that);
70 int FloatAuto::identical(FloatAuto *src)
72 return EQUIV(value, src->value) &&
73 (curve_mode != BUMP || EQUIV(value1, src->value1)) &&
74 EQUIV(control_in_value, src->control_in_value) &&
75 EQUIV(control_out_value, src->control_out_value);
76 // ctrl positions ignored, as they may depend on neighbours
77 // curve_mode is ignored, no recalculations
81 int FloatAuto::equals(FloatAuto *that)
83 return this->value == that->value &&
84 (this->curve_mode != BUMP || this->value1 == that->value1) &&
85 this->control_in_value == that->control_in_value &&
86 this->control_out_value == that->control_out_value &&
87 this->control_in_position == that->control_in_position &&
88 this->control_out_position == that->control_out_position &&
89 this->curve_mode == that->curve_mode;
93 void FloatAuto::copy_from(Auto *that)
95 copy_from((FloatAuto*)that);
98 void FloatAuto::copy_from(FloatAuto *that)
100 Auto::copy_from(that);
101 this->value = that->value;
102 this->value1 = that->value1;
103 this->control_in_value = that->control_in_value;
104 this->control_out_value = that->control_out_value;
105 this->control_in_position = that->control_in_position;
106 this->control_out_position = that->control_out_position;
107 this->curve_mode = that->curve_mode;
108 // note: literate copy, no recalculations
112 void FloatAuto::handle_automatic_curve_after_copy()
113 // in most cases, we don't want to use the manual curve modes
114 // of the left neighbour used as a template for interpolation.
115 // Rather, we (re)set to automatically smoothed curves. Note
116 // auto generated nodes (while tweaking values) indeed are
117 // inserted by using this "interpolation" approach, thus making
118 // this defaulting to auto-smooth curves very important.
120 if(curve_mode == FREE || curve_mode == TFREE)
122 this->curve_mode = SMOOTH;
127 int FloatAuto::interpolate_from(Auto *a1, Auto *a2, int64_t pos, Auto *templ)
128 // bézier interpolates this->value and curves for the given position
129 // between the positions of a1 and a2. If a1 or a2 are omitted, they default
130 // to this->previous and this->next. If this FloatAuto has automatic curves,
131 // this may trigger re-adjusting of this and its neighbours in this->autos.
132 // Note while a1 and a2 need not be members of this->autos, automatic
133 // readjustments are always done to the neighbours in this->autos.
134 // If the template is given, it will be used to fill out this
135 // objects fields prior to interpolating.
137 if(!a1) a1 = previous;
139 Auto::interpolate_from(a1, a2, pos, templ);
140 if( !templ ) handle_automatic_curve_after_copy();
141 if( curve_mode == SMOOTH && a1 && a2 &&
142 a1->is_floatauto() && a2->is_floatauto() &&
143 a1->position <= pos && pos <= a2->position ) {
144 // set this->value using bézier interpolation if possible
145 FloatAuto *left = (FloatAuto*)a1;
146 FloatAuto *right = (FloatAuto*)a2;
147 if( pos != position ) { // this may trigger smoothing
148 this->adjust_to_new_coordinates(pos,
149 FloatAutos::calculate_bezier(left, right, pos));
151 float new_slope = FloatAutos::calculate_bezier_derivation(left, right, pos);
152 this->set_control_in_value(new_slope * control_in_position);
153 this->set_control_out_value(new_slope * control_out_position);
154 return 1; //return true: interpolated indeed...
157 adjust_ctrl_positions(); // implies adjust_curves()
158 return 0; // unable to interpolate
162 void FloatAuto::change_curve_mode(t_mode new_mode, int adjust)
164 if(new_mode == TFREE && !(control_in_position && control_out_position))
165 new_mode = FREE; // only if curves on both sides...
166 else if(new_mode == BUMP )
167 value1 = value; // continuous
169 curve_mode = new_mode;
174 void FloatAuto::toggle_curve_mode()
176 switch (curve_mode) {
177 case SMOOTH: change_curve_mode(LINEAR); break;
178 case LINEAR: change_curve_mode(TFREE); break;
179 case TFREE : change_curve_mode(FREE); break;
180 case FREE : change_curve_mode(BUMP); break;
181 case BUMP : change_curve_mode(SMOOTH); break;
185 // edge=0:left edge, 1:right edge, -1:both edges
186 void FloatAuto::set_value(float value, int edge)
188 float float_min = ((FloatAutos*)autos)->float_min;
189 float float_max = ((FloatAutos*)autos)->float_max;
190 bclamp(value, float_min, float_max);
191 if( curve_mode != BUMP || edge <= 0 ) this->value = value;
192 if( curve_mode == BUMP && edge != 0 ) this->value1 = value;
193 this->adjust_curves();
194 if(previous) ((FloatAuto*)previous)->adjust_curves();
195 if(next) ((FloatAuto*)next)->adjust_curves();
198 void FloatAuto::set_control_in_value(float newvalue)
201 case TFREE: control_out_value = control_out_position*newvalue / control_in_position;
203 case BUMP: control_in_value = newvalue;
204 default: return; // otherwise calculated automatically...
208 void FloatAuto::set_control_out_value(float newvalue)
211 case TFREE: control_in_value = control_in_position*newvalue / control_out_position;
213 case BUMP: control_out_value = newvalue;
220 inline int sgn(float value) { return (value == 0)? 0 : (value < 0) ? -1 : 1; }
222 inline float weighted_mean(float v1, float v2, float w1, float w2){
223 if(0.000001 > fabs(w1 + w2))
226 return (w1 * v1 + w2 * v2) / (w1 + w2);
232 void FloatAuto::adjust_curves()
233 // recalculates curves if current mode
234 // implies automatic adjustment of curves
238 if(curve_mode == SMOOTH) {
239 // normally, one would use the slope of chord between the neighbours.
240 // but this could cause the curve to overshot extremal automation nodes.
241 // (e.g when setting a fade node at zero, the curve could go negative)
242 // we can interpret the slope of chord as a weighted mean value, where
243 // the length of the interval is used as weight; we just use other
244 // weights: intervall length /and/ reciprocal of slope. So, if the
245 // connection to one of the neighbours has very low slope this will
246 // dominate the calculated curve slope at this automation node.
247 // if the slope goes beyond the zero line, e.g if left connection
248 // has positive and right connection has negative slope, then
249 // we force the calculated curve to be horizontal.
250 float s, dxl, dxr, sl, sr;
251 calculate_slope((FloatAuto*) previous, this, sl, dxl);
252 calculate_slope(this, (FloatAuto*) next, sr, dxr);
254 if(0 < sgn(sl) * sgn(sr))
256 float wl = fabs(dxl) * (fabs(1.0/sl) + 1);
257 float wr = fabs(dxr) * (fabs(1.0/sr) + 1);
258 s = weighted_mean(sl, sr, wl, wr);
260 else s = 0; // fixed hoizontal curve
262 control_in_value = s * control_in_position;
263 control_out_value = s * control_out_position;
266 else if(curve_mode == LINEAR) {
270 calculate_slope(this, (FloatAuto*)previous, g, dx);
271 control_in_value = g * dx / 3;
275 calculate_slope(this, (FloatAuto*)next, g, dx);
276 control_out_value = g * dx / 3;
279 else if(curve_mode == TFREE && control_in_position && control_out_position) {
280 float gl = control_in_value / control_in_position;
281 float gr = control_out_value / control_out_position;
282 float wl = fabs(control_in_value);
283 float wr = fabs(control_out_value);
284 float g = weighted_mean(gl, gr, wl, wr);
286 control_in_value = g * control_in_position;
287 control_out_value = g * control_out_position;
291 inline void FloatAuto::calculate_slope(FloatAuto *left, FloatAuto *right, float &dvdx, float &dx)
294 if(!left || !right) return;
296 dx = right->position - left->position;
297 float lval = left->get_value(0);
298 float rval = right->get_value(1);
299 float dv = rval - lval;
300 dvdx = (dx == 0) ? 0 : dv/dx;
304 void FloatAuto::adjust_ctrl_positions(FloatAuto *prev, FloatAuto *next)
305 // recalculates location of ctrl points to be
306 // always 1/3 and 2/3 of the distance to the
307 // next neighbours. The reason is: for this special
308 // distance the bézier function yields x(t) = t, i.e.
309 // we can use the y(t) as if it was a simple function y(x).
311 // This adjustment is done only on demand and involves
312 // updating neighbours and adjust_curves() as well.
315 { // use current siblings
316 prev = (FloatAuto*)this->previous;
317 next = (FloatAuto*)this->next;
321 { set_ctrl_positions(prev, this);
322 prev->adjust_curves();
324 else // disable curve on left side
325 control_in_position = 0;
328 { set_ctrl_positions(this, next);
329 next->adjust_curves();
331 else // disable right curve
332 control_out_position = 0;
334 this->adjust_curves();
335 pos_valid = position;
341 inline void redefine_curve(int64_t &old_pos, int64_t new_pos, float &ctrl_val)
344 ctrl_val *= (float)new_pos / old_pos;
349 inline void FloatAuto::set_ctrl_positions(FloatAuto *prev, FloatAuto* next)
351 int64_t distance = next->position - prev->position;
352 redefine_curve(prev->control_out_position, +distance / 3, prev->control_out_value);
353 redefine_curve(next->control_in_position, -distance / 3, next->control_in_value);
358 void FloatAuto::adjust_to_new_coordinates(int64_t position, float value)
359 // define new position and value in one step, do necessary re-adjustments
361 float float_min = ((FloatAutos*)autos)->float_min;
362 float float_max = ((FloatAutos*)autos)->float_max;
363 bclamp(value, float_min, float_max);
365 this->position = position;
366 adjust_ctrl_positions();
371 int FloatAuto::value_to_str(char *string, float value)
375 sprintf(string, "+%.2f", value);
377 sprintf(string, "%.2f", value);
386 if(value < 1 && value > -1)
389 string[j] = string[0];
397 while(string[j] != 0) string[i++] = string[j++];
403 void FloatAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
405 file->tag.set_title("AUTO");
407 file->tag.set_property("POSITION", 0);
409 file->tag.set_property("POSITION", position - start);
410 file->tag.set_property("VALUE", value);
411 file->tag.set_property("VALUE1", value1);
412 file->tag.set_property("CONTROL_IN_VALUE", control_in_value / 2.0); // compatibility, see below
413 file->tag.set_property("CONTROL_OUT_VALUE", control_out_value / 2.0);
414 file->tag.set_property("TANGENT_MODE", (int)curve_mode);
416 file->tag.set_title("/AUTO");
418 file->append_newline();
421 void FloatAuto::load(FileXML *file)
423 float float_min = ((FloatAutos*)autos)->float_min;
424 float float_max = ((FloatAutos*)autos)->float_max;
425 value = file->tag.get_property("VALUE", value);
426 bclamp(value, float_min, float_max);
427 value1 = file->tag.get_property("VALUE1", value1);
428 bclamp(value, float_min, float_max);
429 control_in_value = file->tag.get_property("CONTROL_IN_VALUE", control_in_value);
430 control_out_value = file->tag.get_property("CONTROL_OUT_VALUE", control_out_value);
431 curve_mode = (t_mode)file->tag.get_property("TANGENT_MODE", (int)FREE);
433 // Compatibility to old session data format:
434 // Versions previous to the bezier auto patch (Jun 2006) applied a factor 2
435 // to the y-coordinates of ctrl points while calculating the bezier function.
436 // To retain compatibility, we now apply this factor while loading
437 control_in_value *= 2.0;
438 control_out_value *= 2.0;
440 // restore ctrl positions and adjust curves if necessary
441 adjust_ctrl_positions();
444 const char *FloatAuto::curve_name(int curve_mode)
446 switch( curve_mode ) {
447 case FloatAuto::SMOOTH: return _("Smooth");
448 case FloatAuto::LINEAR: return _("Linear");
449 case FloatAuto::TFREE: return _("Tangent");
450 case FloatAuto::FREE: return _("Disjoint");
451 case FloatAuto::BUMP: return _("Bump");
456 void FloatAuto::bump_update(int64_t pos, float dv, int edge, int span)
458 float *mins = autos->edl->local_session->automation_mins;
459 float *maxs = autos->edl->local_session->automation_maxs;
460 int group = autos->autogrouptype;
461 float min = mins[group], max = maxs[group];
462 int last_edge = edge;
463 FloatAuto *fauto = this;
464 fauto->position = pos;
465 if( fauto->is_bump() && span ) {
467 float v = fauto->get_value(edge) + dv;
469 fauto->set_value(v, edge);
470 fauto = (FloatAuto*)(!edge ? fauto->next : fauto->previous);
471 } while( fauto && !fauto->is_bump() );
472 last_edge = !edge ? 1 : 0;
475 float v = fauto->get_value(last_edge) + dv;
477 fauto->set_value(v, last_edge);
481 void FloatAuto::bump_value(float v, int edge, int span)
483 float dv = v - get_value(edge);
484 bump_update(position, dv, edge, span);