// TnzCore includes
#include "tstream.h"
#include "tconvert.h"
#include "tsystem.h"
#include "tcubicbezier.h"
#include "tundo.h"
// TnzBase includes
#include "tdoublekeyframe.h"
#include "tdoubleparamfile.h"
#include "texpression.h"
#include "tgrammar.h"
#include "tparser.h"
#include "tunit.h"
// STD includes
#include <set>
#include "tdoubleparam.h"
using namespace std;
//===============================
class TActualDoubleKeyframe final : public TDoubleKeyframe {
public:
mutable TExpression m_expression;
mutable TDoubleParamFileData m_fileData;
TActualDoubleKeyframe(double frame = 0, double value = 0)
: TDoubleKeyframe(frame, value), m_unit(0) {}
explicit TActualDoubleKeyframe(const TDoubleKeyframe &src) : m_unit(0) {
TDoubleKeyframe::operator=(src);
if (m_type == Expression || m_type == SimilarShape)
m_expression.setText(m_expressionText);
else if (m_type == File)
m_fileData.setParams(m_fileParams);
}
TActualDoubleKeyframe &operator=(const TDoubleKeyframe &src) {
TDoubleKeyframe::operator=(src);
m_unit = 0;
if (m_type == Expression || m_type == SimilarShape) {
m_expression.setText(m_expressionText);
} else if (m_type == File) {
m_fileData.setParams(m_fileParams);
}
return *this;
}
const TUnit *updateUnit(const TMeasure *measure) {
if (!measure) {
m_unit = 0;
m_unitName = "";
} else {
if (m_unitName != "")
m_unit = measure->getUnit(::to_wstring(m_unitName));
else
m_unit = 0;
if (!m_unit) {
m_unit = measure->getCurrentUnit();
if (m_unit) {
QString app = QString::fromStdWString(m_unit->getDefaultExtension());
m_unitName = app.toStdString();
}
}
}
assert(measure || m_unit == 0 && m_unitName == "");
assert((m_unit == 0) == (m_unitName == ""));
QString app = QString::fromStdString(m_unitName);
assert(m_unit == 0 || m_unit->isExtension(app.toStdWString()));
return m_unit;
}
double convertFrom(TMeasure *measure, double value) const {
if (!m_unit) const_cast<TActualDoubleKeyframe *>(this)->updateUnit(measure);
if (m_unit) value = m_unit->convertFrom(value);
return value;
}
private:
mutable const TUnit *m_unit;
};
typedef vector<TActualDoubleKeyframe> DoubleKeyframeVector;
//===================================================================
inline double getConstantValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1, double f) {
return (f == k1.m_frame) ? k1.m_value : k0.m_value;
}
//---------------------------------------------------------
inline double getLinearValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1, double f) {
return k0.m_value + (f - k0.m_frame) * (k1.m_value - k0.m_value) /
(k1.m_frame - k0.m_frame);
}
//---------------------------------------------------------
static void truncateSpeeds(double aFrame, double bFrame, TPointD &aSpeedTrunc,
TPointD &bSpeedTrunc) {
double deltaX = bFrame - aFrame;
if (aSpeedTrunc.x < 0) aSpeedTrunc.x = 0;
if (bSpeedTrunc.x > 0) bSpeedTrunc.x = 0;
if (aFrame + aSpeedTrunc.x > bFrame) {
if (aSpeedTrunc.x != 0) {
aSpeedTrunc = aSpeedTrunc * (deltaX / aSpeedTrunc.x);
}
}
if (bFrame + bSpeedTrunc.x < aFrame) {
if (bSpeedTrunc.x != 0) {
bSpeedTrunc = -bSpeedTrunc * (deltaX / bSpeedTrunc.x);
}
}
}
//---------------------------------------------------------
inline double getSpeedInOutValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1,
const TPointD &speed0, const TPointD &speed1,
double frame) {
double aFrame = k0.m_frame;
double bFrame = k1.m_frame;
double aValue = k0.m_value;
double bValue = k1.m_value;
if (frame <= aFrame)
return aValue;
else if (frame >= bFrame)
return bValue;
TPointD aSpeedTrunc = speed0;
TPointD bSpeedTrunc = speed1;
truncateSpeeds(aFrame, bFrame, aSpeedTrunc, bSpeedTrunc);
return getCubicBezierY(frame, TPointD(aFrame, aValue), aSpeedTrunc,
bSpeedTrunc, TPointD(bFrame, bValue));
}
//---------------------------------------------------------
DV_EXPORT_API void splitSpeedInOutSegment(TDoubleKeyframe &k,
TDoubleKeyframe &k0,
TDoubleKeyframe &k1) {
if (k.m_frame <= k0.m_frame) {
k = k0;
return;
} else if (k.m_frame >= k1.m_frame) {
k = k1;
return;
}
TPointD aSpeed = k0.m_speedOut;
TPointD bSpeed = k1.m_speedIn;
truncateSpeeds(k0.m_frame, k1.m_frame, aSpeed, bSpeed);
TPointD p0(k0.m_frame, k0.m_value);
TPointD p3(k1.m_frame, k1.m_value);
TPointD p1 = p0 + aSpeed;
TPointD p2 = p3 + bSpeed;
double t = invCubicBezierX(k.m_frame, p0, aSpeed, bSpeed, p3);
t = tcrop(t, 0.0, 1.0);
TPointD p01 = (1 - t) * p0 + t * p1;
TPointD p12 = (1 - t) * p1 + t * p2;
TPointD p23 = (1 - t) * p2 + t * p3;
TPointD p012 = (1 - t) * p01 + t * p12;
TPointD p123 = (1 - t) * p12 + t * p23;
TPointD p = (1 - t) * p012 + t * p123;
assert(fabs(p.x - k.m_frame) < 0.1e-3);
k.m_value = p.y;
k0.m_speedOut = p01 - p0;
k.m_speedIn = p012 - p;
k.m_speedOut = p123 - p;
k1.m_speedIn = p23 - p3;
}
//---------------------------------------------------------
inline double getEaseInOutValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1, double frame,
bool percentage) {
double x3 = k1.m_frame - k0.m_frame;
if (x3 <= 0.0) return k0.m_value;
double x = frame - k0.m_frame;
if (x <= 0)
return k0.m_value;
else if (x >= x3)
return k1.m_value;
double e0 = std::max(k0.m_speedOut.x, 0.0);
double e1 = std::max(-k1.m_speedIn.x, 0.0);
if (percentage) {
e0 *= x3 * 0.01;
e1 *= x3 * 0.01;
}
if (e0 + e1 >= x3) {
double x = tcrop((e0 + x3 - e1) / 2, 0.0, x3);
e0 = x;
e1 = x3 - x;
}
double x1 = e0, x2 = x3 - e1;
if (0 < x1 - x2 && x1 - x2 < 0.1e-5)
x1 = x2 = (x1 + x2) * 0.5; // against rounding problems
assert(0 <= x1 && x1 <= x2 && x2 <= x3);
double v = 2 / (x3 + x2 - x1);
double value = 0;
if (x < x1) {
double a = v / e0;
value = 0.5 * a * x * x;
} else if (x > x2) {
double a = v / e1;
value = 1 - 0.5 * a * (x3 - x) * (x3 - x);
} else {
double c = -0.5 * v * e0;
value = x * v + c;
}
return (1 - value) * k0.m_value + value * k1.m_value;
}
//---------------------------------------------------------
inline double getExponentialValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1,
double frame) {
double aFrame = k0.m_frame;
double bFrame = k1.m_frame;
double deltaX = bFrame - aFrame;
double aValue = k0.m_value;
double bValue = k1.m_value;
// if min(aValue,bValue)<=0 => error => linear
if (aValue <= 0 || bValue <= 0) return getLinearValue(k0, k1, frame);
double t = (frame - aFrame) / deltaX;
// if aValue<bValue then v = aValue * exp(t * log(bValue/aValue))
// if bValue<aValue then v = bValue * exp((1-t) * log(aValue/bValue))
if (bValue < aValue) {
t = 1 - t;
std::swap(aValue, bValue);
}
return aValue * exp(t * log(bValue / aValue));
}
//---------------------------------------------------------
inline double getExpressionValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1, double frame,
const TMeasure *measure) {
double t = 0, rframe = frame - k0.m_frame;
if (k1.m_frame > k0.m_frame) t = rframe / (k1.m_frame - k0.m_frame);
TSyntax::Calculator *calculator = k0.m_expression.getCalculator();
if (calculator) {
calculator->setUnit(
(const_cast<TActualDoubleKeyframe &>(k0)).updateUnit(measure));
return calculator->compute(t, frame + 1, rframe + 1);
} else if (measure)
return measure->getDefaultValue();
else
return 0;
}
//---------------------------------------------------------
inline double getSimilarShapeValue(const TActualDoubleKeyframe &k0,
const TActualDoubleKeyframe &k1,
double frame, const TMeasure *measure) {
double offset = k0.m_similarShapeOffset;
double rv0 = getExpressionValue(k0, k1, k0.m_frame + offset, measure);
double rv1 = getExpressionValue(k0, k1, k1.m_frame + offset, measure);
double rv = getExpressionValue(k0, k1, frame + offset, measure);
double v0 = k0.m_value;
double v1 = k1.m_value;
if (rv1 != rv0)
return v0 + (v1 - v0) * (rv - rv0) / (rv1 - rv0);
else if (measure)
return measure->getDefaultValue();
else
return 0;
}
//===================================================================
class TDoubleParam::Imp {
public:
const TSyntax::Grammar *m_grammar;
string m_measureName;
TMeasure *m_measure;
double m_defaultValue, m_minValue, m_maxValue;
DoubleKeyframeVector m_keyframes;
bool m_cycleEnabled;
std::set<TParamObserver *> m_observers;
Imp(double v = 0.0)
: m_grammar(0)
, m_measureName()
, m_measure(0)
, m_defaultValue(v)
, m_minValue(-(std::numeric_limits<double>::max)())
, m_maxValue((std::numeric_limits<double>::max)())
, m_cycleEnabled(false) {}
~Imp() {}
void copy(std::unique_ptr<Imp> const &src) {
m_grammar = src->m_grammar;
m_measureName = src->m_measureName;
m_measure = src->m_measure;
m_defaultValue = src->m_defaultValue;
m_minValue = src->m_minValue;
m_maxValue = src->m_maxValue;
m_keyframes = src->m_keyframes;
m_cycleEnabled = src->m_cycleEnabled;
}
void notify(const TParamChange &change) {
std::set<TParamObserver *>::iterator it = m_observers.begin();
for (; it != m_observers.end(); ++it) (*it)->onChange(change);
}
double getValue(int segmentIndex, double frame);
double getSpeed(int segmentIndex, double frame);
TPointD getSpeedIn(int kIndex);
TPointD getSpeedOut(int kIndex);
};
//---------------------------------------------------------
double TDoubleParam::Imp::getValue(int segmentIndex, double frame) {
assert(0 <= segmentIndex && segmentIndex + 1 < (int)m_keyframes.size());
const TActualDoubleKeyframe &k0 = m_keyframes[segmentIndex];
const TActualDoubleKeyframe &k1 = m_keyframes[segmentIndex + 1];
double value = m_defaultValue;
bool convertUnit = false;
switch (k0.m_type) {
case TDoubleKeyframe::Constant:
value = getConstantValue(k0, k1, frame);
break;
case TDoubleKeyframe::Linear:
value = getLinearValue(k0, k1, frame);
break;
case TDoubleKeyframe::SpeedInOut:
value = getSpeedInOutValue(k0, k1, getSpeedOut(segmentIndex),
getSpeedIn(segmentIndex + 1), frame);
break;
case TDoubleKeyframe::EaseInOut:
value = getEaseInOutValue(k0, k1, frame, false);
break;
case TDoubleKeyframe::EaseInOutPercentage:
value = getEaseInOutValue(k0, k1, frame, true);
break;
case TDoubleKeyframe::Exponential:
value = getExponentialValue(k0, k1, frame);
break;
case TDoubleKeyframe::Expression:
value = getExpressionValue(k0, k1, frame, m_measure);
convertUnit = true;
break;
case TDoubleKeyframe::File:
value = k0.m_fileData.getValue(frame, m_defaultValue);
convertUnit = true;
break;
case TDoubleKeyframe::SimilarShape:
value = getSimilarShapeValue(k0, k1, frame, m_measure);
break;
default:
break;
}
if (convertUnit) value = k0.convertFrom(m_measure, value);
return value;
}
//---------------------------------------------------------
double TDoubleParam::Imp::getSpeed(int segmentIndex, double frame) {
const double h = 0.00001;
return (getValue(segmentIndex, frame + h) -
getValue(segmentIndex, frame - h)) /
(2 * h);
}
//---------------------------------------------------------
TPointD TDoubleParam::Imp::getSpeedIn(int kIndex) {
assert(1 <= kIndex && kIndex < (int)m_keyframes.size());
const TActualDoubleKeyframe &kf0 = m_keyframes[kIndex - 1];
const TActualDoubleKeyframe &kf1 = m_keyframes[kIndex];
assert(kf0.m_type == TDoubleKeyframe::SpeedInOut);
if (!kf1.m_linkedHandles) return kf1.m_speedIn;
TPointD speedIn = kf1.m_speedIn;
if (kIndex + 1 >= (int)m_keyframes.size()) {
// speedIn.y = 0;
} else {
if (kf1.m_type != TDoubleKeyframe::SpeedInOut &&
(kf1.m_type != TDoubleKeyframe::Expression ||
!kf1.m_expression.isCycling())) {
double speed = getSpeed(kIndex, kf1.m_frame);
speedIn.y = speedIn.x * speed;
}
}
return speedIn;
}
//---------------------------------------------------------
TPointD TDoubleParam::Imp::getSpeedOut(int kIndex) {
assert(0 <= kIndex && kIndex < (int)m_keyframes.size());
const TDoubleKeyframe &kf1 = m_keyframes[kIndex];
assert(kf1.m_type == TDoubleKeyframe::SpeedInOut);
if (!kf1.m_linkedHandles) return kf1.m_speedOut;
TPointD speedOut = kf1.m_speedOut;
if (kIndex == 0) {
// speedOut.y = 0;
} else {
const TDoubleKeyframe &kf0 = m_keyframes[kIndex - 1];
if (kf0.m_type != TDoubleKeyframe::SpeedInOut) {
double speed = getSpeed(kIndex - 1, kf1.m_frame);
speedOut.y = speedOut.x * speed;
}
}
return speedOut;
}
//---------------------------------------------------------
TPointD TDoubleParam::getSpeedIn(int kIndex) const {
return m_imp->getSpeedIn(kIndex);
}
TPointD TDoubleParam::getSpeedOut(int kIndex) const {
return m_imp->getSpeedOut(kIndex);
}
//=========================================================
PERSIST_IDENTIFIER(TDoubleParam, "doubleParam")
//---------------------------------------------------------
TDoubleParam::TDoubleParam(double v) : m_imp(new TDoubleParam::Imp(v)) {}
//---------------------------------------------------------
TDoubleParam::TDoubleParam(const TDoubleParam &src)
: TParam(src.getName()), m_imp(new TDoubleParam::Imp()) {
m_imp->copy(src.m_imp);
}
//---------------------------------------------------------
TDoubleParam::~TDoubleParam() {}
//---------------------------------------------------------
TDoubleParam &TDoubleParam::operator=(const TDoubleParam &dp) {
setName(dp.getName());
m_imp->copy(dp.m_imp);
return *this;
}
//---------------------------------------------------------
void TDoubleParam::copy(TParam *src) {
TDoubleParam *p = dynamic_cast<TDoubleParam *>(src);
if (!p) throw TException("invalid source for copy");
setName(src->getName());
m_imp->copy(p->m_imp);
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
//---------------------------------------------------------
void TDoubleParam::setValueRange(double min, double max, double step) {
if (min > max) min = max;
m_imp->m_minValue = min;
m_imp->m_maxValue = max;
// m_imp->m_valueStep = step;
}
//---------------------------------------------------------
bool TDoubleParam::getValueRange(double &min, double &max, double &step) const {
min = m_imp->m_minValue;
max = m_imp->m_maxValue;
step = 1; // m_imp->m_valueStep;
return min < max;
}
//---------------------------------------------------------
double TDoubleParam::getDefaultValue() const {
assert(m_imp);
return m_imp->m_defaultValue;
}
//---------------------------------------------------------
void TDoubleParam::enableCycle(bool enabled) {
m_imp->m_cycleEnabled = enabled;
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
//---------------------------------------------------------
bool TDoubleParam::isCycleEnabled() const { return m_imp->m_cycleEnabled; }
//=========================================================
//=========================================================
double TDoubleParam::getValue(double frame, bool leftmost) const {
double value = 0;
assert(m_imp);
const DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
if (keyframes.empty()) {
// no keyframes: return the default value
value = m_imp->m_defaultValue;
} else if (keyframes.size() == 1) {
// a single keyframe. Type must be keyframe based (no expression/file)
value = keyframes[0].m_value;
} else {
// keyframes range is [f0,f1]
double f0 = keyframes.begin()->m_frame;
double f1 = keyframes.back().m_frame;
if (frame < f0)
frame = f0;
else if (frame > f1 && !m_imp->m_cycleEnabled)
frame = f1;
double valueOffset = 0;
if (m_imp->m_cycleEnabled) {
double dist = (f1 - f0);
double dvalue = keyframes.back().m_value - keyframes.begin()->m_value;
while (frame >= f1) {
if (frame != f1 || !leftmost) {
frame -= dist;
valueOffset += dvalue;
} else
break;
}
}
// frame is in [f0,f1]
assert(f0 <= frame && frame <= f1);
DoubleKeyframeVector::const_iterator b;
b = std::lower_bound(keyframes.begin(), keyframes.end(),
TDoubleKeyframe(frame));
assert(b != keyframes.end());
DoubleKeyframeVector::const_iterator a;
if (b->m_frame == frame && (b + 1) != keyframes.end()) {
a = b;
b++;
} else {
assert(b != keyframes.begin());
a = b - 1;
}
if (leftmost && frame - a->m_frame < 0.00001 && a != keyframes.begin()) {
a--;
b--;
}
// segment (a,b) contains frame
assert(a != keyframes.end());
assert(b != keyframes.end());
assert(a->m_frame <= frame);
assert(b->m_frame >= frame);
int kIndex = std::distance(keyframes.begin(), a);
vector<TActualDoubleKeyframe> tmpKeyframe(3);
// if segment is keyframe based ....
if (TDoubleKeyframe::isKeyframeBased(a->m_type)) {
// .. and next segment is not then update the b value
if ((b + 1) != keyframes.end() &&
!TDoubleKeyframe::isKeyframeBased(b->m_type)) {
tmpKeyframe[0] = *b;
if (b->m_type != TDoubleKeyframe::Expression ||
!b->m_expression.isCycling())
tmpKeyframe[0].m_value = getValue(b->m_frame);
b = tmpKeyframe.begin();
}
// .. and/or if prev segment is not then update the a value
if (a != keyframes.begin() &&
!TDoubleKeyframe::isKeyframeBased(a[-1].m_type)) {
tmpKeyframe[1] = *a;
tmpKeyframe[1].m_value = getValue(a->m_frame, true);
a = tmpKeyframe.begin() + 1;
}
}
if (a->m_step > 1) {
tmpKeyframe[2] = *b;
b = tmpKeyframe.begin() + 2;
int relPos = tfloor(b->m_frame - a->m_frame),
step = std::min(a->m_step, relPos);
tmpKeyframe[2].m_frame = a->m_frame + tfloor(relPos, step);
if (frame > b->m_frame) frame = b->m_frame;
frame = a->m_frame + tfloor(tfloor(frame - a->m_frame), step);
}
assert(0 <= kIndex && kIndex + 1 < (int)m_imp->m_keyframes.size());
value = m_imp->m_defaultValue;
bool convertUnit = false;
switch (a->m_type) {
case TDoubleKeyframe::Constant:
value = getConstantValue(*a, *b, frame);
break;
case TDoubleKeyframe::Linear:
value = getLinearValue(*a, *b, frame);
break;
case TDoubleKeyframe::SpeedInOut:
value = getSpeedInOutValue(*a, *b, getSpeedOut(kIndex),
getSpeedIn(kIndex + 1), frame);
break;
case TDoubleKeyframe::EaseInOut:
value = getEaseInOutValue(*a, *b, frame, false);
break;
case TDoubleKeyframe::EaseInOutPercentage:
value = getEaseInOutValue(*a, *b, frame, true);
break;
case TDoubleKeyframe::Exponential:
value = getExponentialValue(*a, *b, frame);
break;
case TDoubleKeyframe::Expression:
value = getExpressionValue(*a, *b, frame, m_imp->m_measure);
convertUnit = true;
break;
case TDoubleKeyframe::File:
value = a->m_fileData.getValue(frame, m_imp->m_defaultValue);
convertUnit = true;
break;
case TDoubleKeyframe::SimilarShape:
value = getSimilarShapeValue(*a, *b, frame, m_imp->m_measure);
// convertUnit = true;
break;
default:
value = 0.0;
}
value += valueOffset;
if (convertUnit) value = a->convertFrom(m_imp->m_measure, value);
}
// if (cropped)
// value = tcrop(value, m_imp->m_minValue, m_imp->m_maxValue);
return value;
}
//---------------------------------------------------------
bool TDoubleParam::setValue(double frame, double value) {
assert(m_imp);
DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
DoubleKeyframeVector::iterator it;
TActualDoubleKeyframe k(frame);
it = std::lower_bound(keyframes.begin(), keyframes.end(), k);
int index = 0;
bool created = false;
/*-- If a keyframe is found --*/
if (it != keyframes.end() && it->m_frame == frame) {
// changing a keyframe value
index = std::distance(keyframes.begin(), it);
TActualDoubleKeyframe oldKeyframe = *it;
if (oldKeyframe.m_type == TDoubleKeyframe::Expression ||
oldKeyframe.m_type == TDoubleKeyframe::File)
return false;
it->m_value = value;
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
/*-- It is a segment, so create a new keyframe. --*/
else {
assert(it == keyframes.end() || it->m_frame > frame);
// can't change value in a file/expression segment
if (it != keyframes.end() && it > keyframes.begin() &&
((it - 1)->m_type == TDoubleKeyframe::Expression ||
(it - 1)->m_type == TDoubleKeyframe::File))
return false;
// inserting a new keyframe
k.m_value = value;
k.m_isKeyframe = true;
k.m_expression.setGrammar(m_imp->m_grammar);
k.m_expression.setOwnerParameter(this);
it = keyframes.insert(it, k);
if (it == keyframes.begin())
it->m_prevType = TDoubleKeyframe::None;
else {
it->m_prevType = it[-1].m_type;
/*-- If you create Key in Segment in FxGui, the Step value is also
* inherited. --*/
it->m_step = it[-1].m_step;
}
if (it + 1 != keyframes.end()) it[1].m_prevType = it->m_type;
index = std::distance(keyframes.begin(), it);
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
created = true;
}
assert(0 == index || keyframes[index - 1].m_frame < keyframes[index].m_frame);
assert(getKeyframeCount() - 1 == index ||
keyframes[index + 1].m_frame > keyframes[index].m_frame);
return created;
}
//---------------------------------------------------------
void TDoubleParam::setKeyframe(int index, const TDoubleKeyframe &k) {
DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
assert(0 <= index && index < (int)keyframes.size());
TActualDoubleKeyframe &dst = keyframes[index];
TActualDoubleKeyframe oldKeyframe = dst;
(TDoubleKeyframe &)dst = k;
dst.updateUnit(m_imp->m_measure);
if (dst.m_type == TDoubleKeyframe::Expression ||
dst.m_type == TDoubleKeyframe::SimilarShape)
dst.m_expression.setText(dst.m_expressionText);
if (dst.m_type == TDoubleKeyframe::File)
dst.m_fileData.setParams(dst.m_fileParams);
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
assert(0 == index || keyframes[index - 1].m_frame < keyframes[index].m_frame);
assert(getKeyframeCount() - 1 == index ||
keyframes[index + 1].m_frame > keyframes[index].m_frame);
if (index == 0)
dst.m_prevType = TDoubleKeyframe::None;
else
dst.m_prevType = keyframes[index - 1].m_type;
if (getKeyframeCount() - 1 != index)
keyframes[index + 1].m_prevType = dst.m_type;
}
//---------------------------------------------------------
void TDoubleParam::setKeyframes(const std::map<int, TDoubleKeyframe> &ks) {
DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
std::map<int, TDoubleKeyframe>::const_iterator it;
for (it = ks.begin(); it != ks.end(); ++it) {
int index = it->first;
assert(0 <= index && index < (int)keyframes.size());
TActualDoubleKeyframe oldKeyframe = keyframes[index];
TActualDoubleKeyframe &dst = keyframes[index];
(TDoubleKeyframe &)dst = it->second;
dst.updateUnit(m_imp->m_measure);
if (dst.m_type == TDoubleKeyframe::Expression ||
dst.m_type == TDoubleKeyframe::SimilarShape)
dst.m_expression.setText(dst.m_expressionText);
if (dst.m_type == TDoubleKeyframe::File)
dst.m_fileData.setParams(dst.m_fileParams);
}
if (!keyframes.empty()) {
keyframes[0].m_prevType = TDoubleKeyframe::None;
for (int i = 1; i < (int)keyframes.size(); i++)
keyframes[i].m_prevType = keyframes[i - 1].m_type;
}
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
#ifndef NDEBUG
for (int i = 0; i + 1 < (int)keyframes.size(); i++) {
assert(keyframes[i].m_frame <= keyframes[i + 1].m_frame);
}
#endif
}
//---------------------------------------------------------
void TDoubleParam::setKeyframe(const TDoubleKeyframe &k) {
DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
DoubleKeyframeVector::iterator it;
it = std::lower_bound(keyframes.begin(), keyframes.end(), k);
if (it != keyframes.end() && it->m_frame == k.m_frame) {
// int index = std::distance(keyframes.begin(), it);
TActualDoubleKeyframe &dst = *it;
(TDoubleKeyframe &)dst = k;
dst.updateUnit(m_imp->m_measure);
} else {
it = keyframes.insert(it, TActualDoubleKeyframe(k));
// int index = std::distance(keyframes.begin(), it);
// TDoubleKeyframe oldKeyframe = *it;
it->m_expression.setGrammar(m_imp->m_grammar);
it->m_expression.setOwnerParameter(this);
it->updateUnit(m_imp->m_measure);
}
it->m_isKeyframe = true;
if (it->m_type == TDoubleKeyframe::Expression)
it->m_expression.setText(it->m_expressionText);
if (it->m_type == TDoubleKeyframe::File)
it->m_fileData.setParams(it->m_fileParams);
if (it == keyframes.begin())
it->m_prevType = TDoubleKeyframe::None;
else
it->m_prevType = it[-1].m_type;
if (it + 1 != keyframes.end()) it[1].m_prevType = it->m_type;
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
assert(it == keyframes.begin() || (it - 1)->m_frame < it->m_frame);
assert(it + 1 == keyframes.end() || (it + 1)->m_frame > it->m_frame);
}
//---------------------------------------------------------
void TDoubleParam::setDefaultValue(double value) {
assert(m_imp);
if (m_imp->m_defaultValue != value) {
m_imp->m_defaultValue = value;
// gmt, 19-6-2010; needed to get a notification in the FxParamsGraphicEditor
// (Camera Stand)
// when a param (without keyframes) is changed
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
}
//---------------------------------------------------------
bool TDoubleParam::isDefault() const {
return m_imp->m_keyframes.empty() &&
// m_imp->m_type == Keyframes &&
m_imp->m_defaultValue == 0;
}
//---------------------------------------------------------
int TDoubleParam::getKeyframeCount() const { return m_imp->m_keyframes.size(); }
//---------------------------------------------------------
void TDoubleParam::getKeyframes(std::set<double> &frames) const {
for (DoubleKeyframeVector::iterator it = m_imp->m_keyframes.begin();
it != m_imp->m_keyframes.end(); ++it)
frames.insert(it->m_frame);
}
//---------------------------------------------------------
bool TDoubleParam::hasKeyframes() const { return !m_imp->m_keyframes.empty(); }
//---------------------------------------------------------
const TDoubleKeyframe &TDoubleParam::getKeyframe(int index) const {
assert(0 <= index && index < (int)m_imp->m_keyframes.size());
return m_imp->m_keyframes[index];
}
//---------------------------------------------------------
const TDoubleKeyframe &TDoubleParam::getKeyframeAt(double frame) const {
static TDoubleKeyframe k;
k = TDoubleKeyframe();
int i = 0;
for (i = 0; i < (int)m_imp->m_keyframes.size(); i++)
if (m_imp->m_keyframes[i].m_frame >= frame) break;
if (i < (int)m_imp->m_keyframes.size() &&
m_imp->m_keyframes[i].m_frame == frame) {
k = m_imp->m_keyframes[i];
return k;
}
k.m_frame = frame;
k.m_value = getValue(frame);
k.m_isKeyframe = false;
return k;
}
//---------------------------------------------------------
bool TDoubleParam::isKeyframe(double frame) const {
return std::binary_search(m_imp->m_keyframes.begin(),
m_imp->m_keyframes.end(), TDoubleKeyframe(frame));
}
//---------------------------------------------------------
void TDoubleParam::deleteKeyframe(double frame) {
DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
DoubleKeyframeVector::iterator it;
it = std::lower_bound(keyframes.begin(), keyframes.end(),
TDoubleKeyframe(frame));
if (it == keyframes.end() || it->m_frame != frame) return;
TDoubleKeyframe::Type type = it->m_prevType;
it = m_imp->m_keyframes.erase(it);
if (it != keyframes.end()) it->m_prevType = type;
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
//---------------------------------------------------------
void TDoubleParam::clearKeyframes() {
m_imp->m_keyframes.clear();
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
//---------------------------------------------------------
void TDoubleParam::assignKeyframe(double frame, const TParamP &src,
double srcFrame, bool changedOnly) {
TDoubleParamP dp = src;
if (!dp) return;
double value = dp->getValue(srcFrame);
if (!changedOnly || getValue(frame) != value) setValue(frame, value);
}
//---------------------------------------------------------
int TDoubleParam::getClosestKeyframe(double frame) const {
DoubleKeyframeVector &keyframes = m_imp->m_keyframes;
typedef DoubleKeyframeVector::iterator Iterator;
Iterator it = std::lower_bound(keyframes.begin(), keyframes.end(),
TDoubleKeyframe(frame));
if (it == keyframes.end()) {
// frame > k.m_frame per qualsiasi k.
// ritorna l'indice dell'ultimo keyframe (o -1 se l'array e' vuoto)
return keyframes.size() - 1;
} else {
int index = std::distance<Iterator>(keyframes.begin(), it);
if (it->m_frame == frame || index == 0)
return index;
else {
double nextFrame = it->m_frame;
double prevFrame = keyframes[index - 1].m_frame;
assert(prevFrame < frame && frame < nextFrame);
return (nextFrame - frame < frame - prevFrame) ? index : index - 1;
}
}
}
//---------------------------------------------------------
int TDoubleParam::getNextKeyframe(double frame) const {
TDoubleKeyframe k(frame);
typedef DoubleKeyframeVector::const_iterator Iterator;
Iterator it =
std::upper_bound(m_imp->m_keyframes.begin(), m_imp->m_keyframes.end(), k);
if (it == m_imp->m_keyframes.end()) return -1;
int index = std::distance<Iterator>(m_imp->m_keyframes.begin(), it);
if (it->m_frame == frame) {
++index;
return index < getKeyframeCount() ? index : -1;
}
return index;
}
//---------------------------------------------------------
int TDoubleParam::getPrevKeyframe(double frame) const {
TDoubleKeyframe k(frame);
typedef DoubleKeyframeVector::const_iterator Iterator;
Iterator it =
std::lower_bound(m_imp->m_keyframes.begin(), m_imp->m_keyframes.end(), k);
if (it == m_imp->m_keyframes.end()) return m_imp->m_keyframes.size() - 1;
int index = std::distance<Iterator>(m_imp->m_keyframes.begin(), it);
return index - 1;
}
//---------------------------------------------------------
double TDoubleParam::keyframeIndexToFrame(int index) const {
assert(0 <= index && index < getKeyframeCount());
return getKeyframe(index).m_frame;
}
//---------------------------------------------------------
void TDoubleParam::addObserver(TParamObserver *observer) {
m_imp->m_observers.insert(observer);
}
//---------------------------------------------------------
void TDoubleParam::removeObserver(TParamObserver *observer) {
m_imp->m_observers.erase(observer);
}
//---------------------------------------------------------
const std::set<TParamObserver *> &TDoubleParam::observers() const {
return m_imp->m_observers;
}
//---------------------------------------------------------
void TDoubleParam::loadData(TIStream &is) {
string tagName;
/*
if(is.matchTag(tagName))
{
// nuovo formato (29 agosto 2005)
if(tagName!="type") throw TException("expected <type>");
int type = 0;
is >> type;
// m_imp->m_type = TDoubleParam::Type(type);
if(!is.matchEndTag()) throw TException(tagName + " : missing endtag");
//if(!is.matchTag(tagName) || tagName != "default") throw TException("expected
<default>");
//is >> m_imp->m_defaultValue;
//if(!is.matchEndTag()) throw TException(tagName + " : missing endtag");
//if(!is.matchTag(tagName) || tagName != "default") throw TException("expected
<default>");
}
else
{
// vecchio formato
is >> m_imp->m_defaultValue;
}
*/
m_imp->m_keyframes.clear();
int oldType = -1;
while (is.matchTag(tagName)) {
if (tagName == "type") {
// (old) format 5.2. Since 6.0 param type is no used anymore
is >> oldType;
} else if (tagName == "default") {
is >> m_imp->m_defaultValue;
} else if (tagName == "cycle") {
string dummy;
is >> dummy;
m_imp->m_cycleEnabled = true;
// setExtrapolationAfter(Loop);
} else if (tagName == "k") {
// vecchio formato
if (oldType != 0) continue;
int linkStatus = 0;
TActualDoubleKeyframe k;
is >> k.m_frame >> k.m_value >> k.m_speedIn.x >> k.m_speedIn.y >>
k.m_speedOut.x >> k.m_speedOut.y >> linkStatus;
k.m_isKeyframe = true;
k.m_linkedHandles = (linkStatus & 1) != 0;
if ((linkStatus & 1) != 0)
k.m_type = TDoubleKeyframe::SpeedInOut;
else if ((linkStatus & 2) != 0 || (linkStatus & 4) != 0)
k.m_type = TDoubleKeyframe::EaseInOut;
else
k.m_type = TDoubleKeyframe::Linear;
k.m_expression.setGrammar(m_imp->m_grammar);
k.m_expression.setOwnerParameter(this);
k.m_prevType = m_imp->m_keyframes.empty()
? TDoubleKeyframe::None
: m_imp->m_keyframes.back().m_type;
m_imp->m_keyframes.push_back(k);
} else if (tagName == "expr") {
// vecchio formato
if (oldType != 1) continue;
string text = is.getTagAttribute("text");
string enabled = is.getTagAttribute("enabled");
TDoubleKeyframe kk1, kk2;
kk1.m_frame = -1000;
kk2.m_frame = 1000;
kk1.m_isKeyframe = true;
kk2.m_isKeyframe = true;
kk1.m_expressionText = text;
kk2.m_expressionText = text;
kk1.m_type = TDoubleKeyframe::Expression;
kk2.m_type = TDoubleKeyframe::Expression;
TActualDoubleKeyframe k1(kk1);
TActualDoubleKeyframe k2(kk2);
k1.m_expression.setGrammar(m_imp->m_grammar);
k1.m_expression.setOwnerParameter(this);
k1.m_prevType = m_imp->m_keyframes.empty()
? TDoubleKeyframe::None
: m_imp->m_keyframes.back().m_type;
k2.m_expression.setGrammar(m_imp->m_grammar);
k2.m_expression.setOwnerParameter(this);
k2.m_prevType = m_imp->m_keyframes.empty()
? TDoubleKeyframe::None
: m_imp->m_keyframes.back().m_type;
m_imp->m_keyframes.push_back(k1);
m_imp->m_keyframes.push_back(k2);
continue;
} else if (tagName == "file") {
// vecchio formato
if (oldType != 2) continue;
TDoubleKeyframe::FileParams params;
params.m_path = TFilePath(is.getTagAttribute("path"));
params.m_fieldIndex = std::stoi(is.getTagAttribute("index"));
TActualDoubleKeyframe k1, k2;
k1.m_frame = -1000;
k2.m_frame = 1000;
k1.m_isKeyframe = true;
k2.m_isKeyframe = true;
k1.m_fileData.setParams(params);
k2.m_fileData.setParams(params);
k1.m_type = TDoubleKeyframe::File;
k2.m_type = TDoubleKeyframe::File;
k1.m_expression.setGrammar(m_imp->m_grammar);
k1.m_expression.setOwnerParameter(this);
k1.m_prevType = m_imp->m_keyframes.empty()
? TDoubleKeyframe::None
: m_imp->m_keyframes.back().m_type;
k2.m_expression.setGrammar(m_imp->m_grammar);
k2.m_expression.setOwnerParameter(this);
k2.m_prevType = m_imp->m_keyframes.empty()
? TDoubleKeyframe::None
: m_imp->m_keyframes.back().m_type;
m_imp->m_keyframes.push_back(k1);
m_imp->m_keyframes.push_back(k2);
continue;
} else if (tagName == "step") {
int step = 0;
is >> step;
// if(step>1)
// m_imp->m_frameStep = step;
} else if (tagName == "keyframes") {
while (!is.eos()) {
TDoubleKeyframe kk;
kk.loadData(is);
// Throw out invalid interpolation types
if (kk.m_type == TDoubleKeyframe::None) continue;
TActualDoubleKeyframe k(kk);
k.m_expression.setGrammar(m_imp->m_grammar);
k.m_expression.setOwnerParameter(this);
k.m_prevType = m_imp->m_keyframes.empty()
? TDoubleKeyframe::None
: m_imp->m_keyframes.back().m_type;
m_imp->m_keyframes.push_back(k);
}
} else {
throw TException(tagName + " : unexpected tag");
}
if (!is.matchEndTag()) throw TException(tagName + " : missing endtag");
}
if (m_imp->m_keyframes.empty() && !is.eos()) {
// vecchio sistema (prima 16/1/2003)
while (!is.eos()) {
double t, v;
is >> t >> v;
m_imp->m_keyframes.push_back(TActualDoubleKeyframe(t, v));
}
if (!m_imp->m_keyframes.empty()) {
m_imp->m_keyframes[0].m_prevType = TDoubleKeyframe::None;
for (int i = 1; i < (int)m_imp->m_keyframes.size(); i++)
m_imp->m_keyframes[i].m_prevType = m_imp->m_keyframes[i - 1].m_type;
}
}
m_imp->notify(TParamChange(this, 0, 0, true, false, false));
}
//---------------------------------------------------------
void TDoubleParam::saveData(TOStream &os) {
// os.child("type") << (int)m_imp->m_type;
os.child("default") << m_imp->m_defaultValue;
if (isCycleEnabled()) os.child("cycle") << std::string("enabled");
// if(getExtrapolationAfter() == Loop)
// os.child("cycle") << string("loop");
if (!m_imp->m_keyframes.empty()) {
os.openChild("keyframes");
DoubleKeyframeVector::const_iterator it;
for (it = m_imp->m_keyframes.begin(); it != m_imp->m_keyframes.end(); ++it)
it->saveData(os);
os.closeChild();
}
}
//---------------------------------------------------------
string TDoubleParam::getStreamTag() const { return "doubleParam"; }
//-------------------------------------------------------------------
string TDoubleParam::getValueAlias(double frame, int precision) {
return ::to_string(getValue(frame), precision);
}
//-------------------------------------------------------------------
void TDoubleParam::setGrammar(const TSyntax::Grammar *grammar) {
m_imp->m_grammar = grammar;
for (int i = 0; i < (int)m_imp->m_keyframes.size(); i++)
m_imp->m_keyframes[i].m_expression.setGrammar(grammar);
}
//-------------------------------------------------------------------
const TSyntax::Grammar *TDoubleParam::getGrammar() const {
return m_imp->m_grammar;
}
//-------------------------------------------------------------------
void TDoubleParam::accept(TSyntax::CalculatorNodeVisitor &visitor) {
for (int i = 0; i < (int)m_imp->m_keyframes.size(); i++)
if (m_imp->m_keyframes[i].m_type == TDoubleKeyframe::Expression ||
m_imp->m_keyframes[i].m_type == TDoubleKeyframe::SimilarShape)
m_imp->m_keyframes[i].m_expression.accept(visitor);
}
//-------------------------------------------------------------------
string TDoubleParam::getMeasureName() const { return m_imp->m_measureName; }
//-------------------------------------------------------------------
void TDoubleParam::setMeasureName(string name) {
m_imp->m_measureName = name;
m_imp->m_measure = TMeasureManager::instance()->get(name);
}
//-------------------------------------------------------------------
TMeasure *TDoubleParam::getMeasure() const { return m_imp->m_measure; }