| |
| |
|
|
| #include "tstream.h" |
| #include "tconvert.h" |
| #include "tsystem.h" |
| #include "tcubicbezier.h" |
| #include "tundo.h" |
| |
| |
| #include "tdoublekeyframe.h" |
| #include "tdoubleparamfile.h" |
| #include "texpression.h" |
| #include "tgrammar.h" |
| #include "tparser.h" |
| #include "tunit.h" |
| |
| |
| #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); |
| } |
| |
| |
| |
| 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; |
| 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 (aValue <= 0 || bValue <= 0) return getLinearValue(k0, k1, frame); |
| |
| double t = (frame - aFrame) / deltaX; |
| |
| |
| if (bValue < aValue) { |
| t = 1 - t; |
| tswap(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; |
| } |
| 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()) { |
| |
| } 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) { |
| |
| } 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; |
| |
| } |
| |
| |
| |
| bool TDoubleParam::getValueRange(double &min, double &max, double &step) const { |
| min = m_imp->m_minValue; |
| max = m_imp->m_maxValue; |
| step = 1; |
| 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()) { |
| |
| value = m_imp->m_defaultValue; |
| } else if (keyframes.size() == 1) { |
| |
| value = keyframes[0].m_value; |
| } else { |
| |
| 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; |
| } |
| } |
| |
| |
| 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--; |
| } |
| |
| |
| 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 (TDoubleKeyframe::isKeyframeBased(a->m_type)) { |
| |
| 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(); |
| } |
| |
| 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); |
| |
| break; |
| |
| default: |
| value = 0.0; |
| } |
| value += valueOffset; |
| if (convertUnit) value = a->convertFrom(m_imp->m_measure, value); |
| } |
| |
| |
| |
| 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 (it != keyframes.end() && it->m_frame == frame) { |
| |
| 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)); |
| } |
| |
| else { |
| assert(it == keyframes.end() || it->m_frame > frame); |
| |
| |
| if (it != keyframes.end() && it > keyframes.begin() && |
| ((it - 1)->m_type == TDoubleKeyframe::Expression || |
| (it - 1)->m_type == TDoubleKeyframe::File)) |
| return false; |
| |
| |
| 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; |
| |
| 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; |
| } |
| |
| |
| |
| 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) { |
| |
| TActualDoubleKeyframe &dst = *it; |
| (TDoubleKeyframe &)dst = k; |
| dst.updateUnit(m_imp->m_measure); |
| } else { |
| it = keyframes.insert(it, TActualDoubleKeyframe(k)); |
| |
| |
| 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; |
| |
| 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; |
| |
| |
| |
| |
| m_imp->notify(TParamChange(this, 0, 0, true, false, false)); |
| } |
| } |
| |
| |
| |
| bool TDoubleParam::isDefault() const { |
| return m_imp->m_keyframes.empty() && |
| |
| 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()) { |
| |
| |
| 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; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| m_imp->m_keyframes.clear(); |
| int oldType = -1; |
| while (is.matchTag(tagName)) { |
| if (tagName == "type") { |
| |
| is >> oldType; |
| } else if (tagName == "default") { |
| is >> m_imp->m_defaultValue; |
| } else if (tagName == "cycle") { |
| string dummy; |
| is >> dummy; |
| m_imp->m_cycleEnabled = true; |
| |
| } else if (tagName == "k") { |
| |
| 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") { |
| |
| 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") { |
| |
| 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; |
| |
| |
| } else if (tagName == "keyframes") { |
| while (!is.eos()) { |
| TDoubleKeyframe kk; |
| kk.loadData(is); |
| 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()) { |
| |
| 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("default") << m_imp->m_defaultValue; |
| if (isCycleEnabled()) os.child("cycle") << std::string("enabled"); |
| |
| |
| 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; } |
| |