#include "toonz/doubleparamcmd.h" #include "toonz/preferences.h" #include "tdoubleparam.h" #include "tdoublekeyframe.h" #include "tundo.h" #include "tunit.h" #include #include //============================================================================= // // Keyframes Undo // //----------------------------------------------------------------------------- class KeyframesUndo final : public TUndo { TDoubleParamP m_param; typedef std::map Keyframes; Keyframes m_oldKeyframes; Keyframes m_newKeyframes; public: KeyframesUndo(TDoubleParam *param) : m_param(param) {} void addKeyframe(int kIndex) { if (m_oldKeyframes.count(kIndex) > 0) return; assert(0 <= kIndex && kIndex < m_param->getKeyframeCount()); m_oldKeyframes[kIndex] = m_param->getKeyframe(kIndex); } int createKeyframe(double frame) { TDoubleKeyframe oldKeyframe = m_param->getKeyframeAt(frame); if (oldKeyframe.m_isKeyframe) { int kIndex = m_param->getClosestKeyframe(oldKeyframe.m_frame); assert(0 <= kIndex && kIndex < m_param->getKeyframeCount()); assert(m_param->keyframeIndexToFrame(kIndex) == oldKeyframe.m_frame); return kIndex; } m_param->setKeyframe(oldKeyframe); int kIndex = m_param->getClosestKeyframe(oldKeyframe.m_frame); assert(0 <= kIndex && kIndex < m_param->getKeyframeCount()); assert(m_param->keyframeIndexToFrame(kIndex) == oldKeyframe.m_frame); assert(m_oldKeyframes.count(kIndex) == 0); m_oldKeyframes[kIndex] = oldKeyframe; return kIndex; } void onAdd() override { Keyframes::iterator it; for (it = m_oldKeyframes.begin(); it != m_oldKeyframes.end(); ++it) { int kIndex = it->first; assert(0 <= kIndex && kIndex < m_param->getKeyframeCount()); m_newKeyframes[kIndex] = m_param->getKeyframe(kIndex); } } void undo() const override { m_param->setKeyframes(m_oldKeyframes); Keyframes::const_iterator it; for (it = m_oldKeyframes.begin(); it != m_oldKeyframes.end(); ++it) if (!it->second.m_isKeyframe) m_param->deleteKeyframe(it->second.m_frame); } void redo() const override { Keyframes::const_iterator it; for (it = m_oldKeyframes.begin(); it != m_oldKeyframes.end(); ++it) if (!it->second.m_isKeyframe) m_param->setKeyframe(it->second); m_param->setKeyframes(m_newKeyframes); } int getSize() const override { return sizeof(*this) + sizeof(*m_oldKeyframes.begin()) * 2 * m_oldKeyframes.size(); } QString getHistoryString() override { return QObject::tr("Set Keyframe"); } }; //============================================================================= // // KeyframeSetter // //----------------------------------------------------------------------------- KeyframeSetter::KeyframeSetter(TDoubleParam *param, int kIndex, bool enableUndo) : m_param(param) , m_kIndex(-1) , m_extraDFrame(0) , m_enableUndo(enableUndo) , m_undo(new KeyframesUndo(param)) , m_changed(false) , m_pixelRatio(1) { if (kIndex >= 0) selectKeyframe(kIndex); } KeyframeSetter::~KeyframeSetter() { if (m_enableUndo) addUndo(); else if (m_undo) { delete m_undo; m_undo = 0; } } void KeyframeSetter::addUndo() { if (m_undo) { if (m_changed) TUndoManager::manager()->add(m_undo); else delete m_undo; m_undo = 0; } } void KeyframeSetter::selectKeyframe(int kIndex) { if (m_indices.count(kIndex) == 0) { m_indices.insert(kIndex); m_undo->addKeyframe(kIndex); } m_kIndex = kIndex; m_keyframe = m_param->getKeyframe(m_kIndex); } // set key frame at frame and returns its index int KeyframeSetter::createKeyframe(double frame) { /*--- すでにそこにキーフレームがある場合はそのIndexを返すだけ ---*/ int kIndex = m_param->getClosestKeyframe(frame); if (kIndex >= 0 && m_param->getKeyframe(kIndex).m_frame == frame) { selectKeyframe(kIndex); return kIndex; } m_changed = true; kIndex = m_undo->createKeyframe(frame); m_indices.insert(kIndex); m_kIndex = kIndex; m_keyframe = m_param->getKeyframe(m_kIndex); setStep(Preferences::instance()->getAnimationStep()); int kCount = m_param->getKeyframeCount(); if (kCount <= 1) { // a single keyframe created in a empty curve } else if (kCount == 2) { // two keyframes => a linear segment TDoubleKeyframe::Type type = TDoubleKeyframe::Type(Preferences::instance()->getKeyframeType()); setType(0, type); } else { // there are at least other two keyframes (and therefore at least a segment) if (m_kIndex == 0) { // a new segment added before the others. use the preference value setType( TDoubleKeyframe::Type(Preferences::instance()->getKeyframeType())); } else if (m_kIndex == kCount - 1) { // a new segment added after the others. use the preference value too setType(m_kIndex - 1, TDoubleKeyframe::Type( Preferences::instance()->getKeyframeType())); } else { // a new point in a segment TDoubleKeyframe ka = m_param->getKeyframe(m_kIndex - 1); TDoubleKeyframe kb = m_param->getKeyframe(m_kIndex + 1); TDoubleKeyframe::Type segmentType = ka.m_type; m_keyframe.m_type = ka.m_type; m_keyframe.m_step = ka.m_step; // An existing segment step should prevail // over the preference /*---Segment内にKeyを打った場合は、Step値も元のSegmentの値を引き継ぐようにする---*/ m_param->setKeyframe(m_kIndex, m_keyframe); if (segmentType == TDoubleKeyframe::SpeedInOut || segmentType == TDoubleKeyframe::EaseInOut || segmentType == TDoubleKeyframe::EaseInOutPercentage) { std::map keyframes; if (segmentType == TDoubleKeyframe::SpeedInOut) { splitSpeedInOutSegment(m_keyframe, ka, kb); } else if (segmentType == TDoubleKeyframe::EaseInOut) { m_keyframe.m_speedIn = m_keyframe.m_speedOut = TPointD(); if (ka.m_frame + ka.m_speedOut.x > m_keyframe.m_frame) ka.m_speedOut.x = m_keyframe.m_frame - ka.m_frame; if (kb.m_frame + kb.m_speedIn.x < m_keyframe.m_frame) ka.m_speedIn.x = m_keyframe.m_frame - kb.m_frame; } else // easeinpercentage { m_keyframe.m_speedIn = m_keyframe.m_speedOut = TPointD(); double segmentWidth = kb.m_frame - ka.m_frame; } keyframes[m_kIndex - 1] = ka; keyframes[m_kIndex] = m_keyframe; keyframes[m_kIndex + 1] = kb; m_undo->addKeyframe(m_kIndex - 1); m_undo->addKeyframe(m_kIndex + 1); m_param->setKeyframes(keyframes); } else if (segmentType == TDoubleKeyframe::Expression || segmentType == TDoubleKeyframe::SimilarShape) { std::string expressionText = ka.m_expressionText; setExpression(expressionText); setType(ka.m_type); } else setType(ka.m_type); } } return kIndex; } bool KeyframeSetter::isSpeedInOut(int segmentIndex) const { return 0 <= segmentIndex && segmentIndex + 1 < m_param->getKeyframeCount() && m_param->getKeyframe(segmentIndex).m_type == TDoubleKeyframe::SpeedInOut; } bool KeyframeSetter::isEaseInOut(int segmentIndex) const { if (0 <= segmentIndex && segmentIndex + 1 < m_param->getKeyframeCount()) { TDoubleKeyframe::Type type = m_param->getKeyframe(segmentIndex).m_type; if (type == TDoubleKeyframe::EaseInOut || type == TDoubleKeyframe::EaseInOutPercentage) return true; } return false; } // find the speedInOut handles which will change when moving keyframe kIndex // rotatingSpeeds: { } void KeyframeSetter::getRotatingSpeedHandles( std::vector> &rotatingSpeeds, TDoubleParam *param, int kIndex) const { const double epsilon = 1.0e-7; int ty[4] = {0, 0, 0, 0}; // ty[] refers to segments around the kIndex keyframe // 1 ==> linear or exponential (they can cause speed handle rotation) // 2 ==> speedinout for (int i = 0; i < 4; i++) { int k = kIndex + i - 2; if (0 <= k && k < param->getKeyframeCount()) { TDoubleKeyframe::Type type = param->getKeyframe(k).m_type; if (type == TDoubleKeyframe::Linear || type == TDoubleKeyframe::Exponential) ty[i] = 1; else if (type == TDoubleKeyframe::SpeedInOut) ty[i] = 2; } } // SpeedInOut * Linear * if ((ty[0] == 2 && ty[1] == 1) || (ty[1] == 2 && ty[2] == 1)) { int k = ty[1] == 1 ? kIndex - 1 : kIndex; double speed = getNorm(param->getSpeedIn(k)); if (speed > epsilon) rotatingSpeeds.push_back(std::make_pair(-speed, k)); } // * Linear * SpeedInOut if ((ty[1] == 1 && ty[2] == 2) || (ty[2] == 1 && ty[3] == 2)) { int k = ty[2] == 2 ? kIndex : kIndex + 1; double speed = getNorm(param->getSpeedOut(k)); if (speed > epsilon) rotatingSpeeds.push_back(std::make_pair(speed, k)); } } void KeyframeSetter::moveKeyframes(int dFrame, double dValue) { int n = m_param->getKeyframeCount(); if (n == 0) return; // to keep constant the "length" of speed handles which are modified as side // effects std::vector> rotatingSpeedHandles; if (m_indices.size() == 1) getRotatingSpeedHandles(rotatingSpeedHandles, m_param.getPointer(), *m_indices.begin()); // update the frame change (m_extraDFrame represent old moves // which has not performed, e.g. because of keyframe collisions) dFrame += m_extraDFrame; m_extraDFrame = 0; if (dFrame != 0) { // check frame constraints (keyframe collisions and segment type swaps) double preferredDFrame = dFrame; double dFrameSgn = dFrame < 0 ? -1 : 1; dFrame *= dFrameSgn; typedef std::pair TypePair; std::vector> keyframes(n); keyframes[0].second.first = TDoubleKeyframe::None; for (int i = 0; i < n; i++) { TDoubleKeyframe kf = m_param->getKeyframe(i); keyframes[i].first = kf.m_frame; if (i < n - 1) { keyframes[i].second.second = kf.m_type; keyframes[i + 1].second.first = kf.m_type; } else keyframes[i].second.second = TDoubleKeyframe::None; } while (dFrame > 0) { std::vector> mk( keyframes); // mk = moved keyframes std::set::iterator it; for (it = m_indices.begin(); it != m_indices.end(); ++it) mk[*it].first += dFrame * dFrameSgn; std::sort(mk.begin(), mk.end()); bool ok = true; int i; for (i = 0; i + 1 < n && ok; i++) { // check keyframe collisions if (fabs(mk[i + 1].first - mk[i].first) < 1.e-8) ok = false; // check segment type swaps if (mk[i].second.second != mk[i + 1].second.first && mk[i].second.second != TDoubleKeyframe::None && mk[i + 1].second.first != TDoubleKeyframe::None) ok = false; } if (ok) break; dFrame -= 1; } dFrame = dFrameSgn * std::max(0, dFrame); if (dFrame != preferredDFrame) m_extraDFrame = preferredDFrame - dFrame; // at this point dFrame (possibly ==0) is ok (no keyframe collisions, no // segment type mismatches) } std::map change; if (dFrame == 0) { // only value changes std::set::iterator it; int i = 0; m_changed = true; for (it = m_indices.begin(); it != m_indices.end(); ++it, i++) { TDoubleKeyframe keyframe = m_param->getKeyframe(*it); keyframe.m_value += dValue; change[*it] = keyframe; m_undo->addKeyframe(i); } } else { // frame+value changes int n = m_param->getKeyframeCount(); std::vector> keyframes( n); // (keyframe, index) for (int i = 0; i < n; i++) keyframes[i] = std::make_pair(m_param->getKeyframe(i), i); // change frame and value of selected keyframes std::set::iterator it; for (it = m_indices.begin(); it != m_indices.end(); ++it) { int i = *it; keyframes[i].first.m_frame += dFrame; keyframes[i].first.m_value += dValue; // keyframes[i].second = -1; // to mark keyframes involved in the move } // keyframes.back().first.m_type = TDoubleKeyframe::None; // clear selection (indices can change: we have to rebuild it) std::set oldSelection(m_indices); m_indices.clear(); // sort keyframes (according to their - updated - frames) std::sort(keyframes.begin(), keyframes.end()); for (std::set::iterator it = oldSelection.begin(); it != oldSelection.end(); ++it) m_indices.insert(keyframes[*it].second); // update segment types (because of swaps) /* for(int i=0;i+1addKeyframe(i); change[i] = keyframes[i].first; } else if (m_indices.count(i) > 0) change[i] = keyframes[i].first; } } m_param->setKeyframes(change); /* if(!rotatingSpeedHandles.empty()) { for(int i=0;i<(int)rotatingSpeedHandles.size();i++) { double speedLength = rotatingSpeedHandles[i].first; int kIndex = rotatingSpeedHandles[i].second; TDoubleKeyframe kf = m_param->getKeyframe(kIndex); TPointD speed = speedLength<0 ? m_param->getSpeedIn(kIndex) : m_param->getSpeedOut(kIndex); speed = fabs(speedLength/getNorm(speed)) * speed; if(speedLength<0) kf.m_speedIn = speed; else kf.m_speedOut = speed; m_param->setKeyframe(kf); } } */ } void KeyframeSetter::setType(int kIndex, TDoubleKeyframe::Type type) { assert(0 <= kIndex && kIndex < m_param->getKeyframeCount()); // get the current keyframe value TDoubleKeyframe keyframe = m_param->getKeyframe(kIndex); // get the next keyframe (if any) and compute the segment length TDoubleKeyframe nextKeyframe; double segmentWidth = 1; if (kIndex + 1 < m_param->getKeyframeCount()) { nextKeyframe = m_param->getKeyframe(kIndex + 1); segmentWidth = nextKeyframe.m_frame - keyframe.m_frame; } else if (kIndex + 1 > m_param->getKeyframeCount()) { // kIndex is the last keyframe. no segment is defined (therefore no segment // type) type = TDoubleKeyframe::Linear; } if (type == keyframe.m_type) return; // I'm going to change kIndex. Make sure it is selected. set the dirty flag m_undo->addKeyframe(kIndex); // m_indices.insert(kIndex); m_changed = true; double ease0 = 0, ease1 = 0; std::map keyframes; switch (type) { case TDoubleKeyframe::SpeedInOut: keyframe.m_speedOut = TPointD(segmentWidth / 3, 0); nextKeyframe.m_speedIn = TPointD(-segmentWidth / 3, 0); if (nextKeyframe.m_linkedHandles && nextKeyframe.m_speedOut.x > 0.01) nextKeyframe.m_speedIn = -nextKeyframe.m_speedOut; if (keyframe.m_linkedHandles && keyframe.m_speedIn.x < -0.01) keyframe.m_speedOut = -keyframe.m_speedIn; keyframe.m_type = type; keyframes[kIndex] = keyframe; keyframes[kIndex + 1] = nextKeyframe; m_param->setKeyframes(keyframes); break; case TDoubleKeyframe::EaseInOut: case TDoubleKeyframe::EaseInOutPercentage: if (keyframe.m_type == TDoubleKeyframe::EaseInOut) { // absolute -> percentage ease0 = keyframe.m_speedOut.x * 100.0 / segmentWidth; ease1 = -nextKeyframe.m_speedIn.x * 100.0 / segmentWidth; // rounding could break constraints. crop parameters ease0 = tcrop(ease0, 0.0, 100.0); ease1 = tcrop(ease1, 0.0, 100.0 - ease0); } else if (keyframe.m_type == TDoubleKeyframe::EaseInOutPercentage) { // percentage -> absolute ease0 = keyframe.m_speedOut.x * 0.01 * segmentWidth; ease1 = -nextKeyframe.m_speedIn.x * 0.01 * segmentWidth; // rounding could break constraints. crop parameters ease0 = tcrop(ease0, 0.0, segmentWidth); ease1 = tcrop(ease1, 0.0, segmentWidth - ease0); } else { ease1 = ease0 = segmentWidth / 3; } keyframe.m_speedOut = TPointD(ease0, 0); nextKeyframe.m_speedIn = TPointD(-ease1, 0); keyframe.m_type = type; keyframes[kIndex] = keyframe; keyframes[kIndex + 1] = nextKeyframe; m_param->setKeyframes(keyframes); break; case TDoubleKeyframe::Expression: keyframe.m_type = type; { double value = m_param->getValue(keyframe.m_frame); const TUnit *unit = 0; if (m_param->getMeasure()) unit = m_param->getMeasure()->getCurrentUnit(); if (unit) value = unit->convertTo(value); keyframe.m_expressionText = QString::number(value).toStdString(); } m_param->setKeyframe(kIndex, keyframe); break; default: keyframe.m_type = type; m_param->setKeyframe(kIndex, keyframe); } } void KeyframeSetter::setType(TDoubleKeyframe::Type type) { assert(m_kIndex >= 0 && m_indices.size() == 1); setType(m_kIndex, type); } void KeyframeSetter::setStep(int step) { assert(m_kIndex >= 0 && m_indices.size() == 1); if (m_keyframe.m_step == step) return; m_changed = true; m_keyframe.m_step = step; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setExpression(std::string expression) { assert(m_kIndex >= 0 && m_indices.size() == 1); m_changed = true; m_keyframe.m_expressionText = expression; m_keyframe.m_type = TDoubleKeyframe::Expression; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setSimilarShape(std::string expression, double offset) { assert(m_kIndex >= 0 && m_indices.size() == 1); m_changed = true; m_keyframe.m_expressionText = expression; m_keyframe.m_similarShapeOffset = offset; m_keyframe.m_type = TDoubleKeyframe::SimilarShape; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setFile(const TDoubleKeyframe::FileParams ¶ms) { assert(m_kIndex >= 0 && m_indices.size() == 1); m_changed = true; m_keyframe.m_fileParams = params; m_keyframe.m_type = TDoubleKeyframe::File; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setUnitName(std::string unitName) { assert(m_kIndex >= 0 && m_indices.size() == 1); m_changed = true; m_keyframe.m_unitName = unitName; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setValue(double value) { assert(m_kIndex >= 0 && m_indices.size() == 1); if (m_keyframe.m_value == value) return; m_changed = true; m_keyframe.m_value = value; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::linkHandles() { assert(m_kIndex >= 0 && m_indices.size() == 1); if (m_keyframe.m_linkedHandles) return; m_changed = true; m_keyframe.m_linkedHandles = true; if (isSpeedInOut(m_kIndex) && isSpeedInOut(m_kIndex - 1)) { // keyframe is between two speedin/out segments (and therefore linkHandles // makes sense) TPointD d = -m_keyframe.m_speedIn + m_keyframe.m_speedOut; const double eps = 0.1e-3; if (d.x <= eps) { m_keyframe.m_speedIn = m_keyframe.m_speedOut = TPointD(0, 0); } else { m_keyframe.m_speedIn.y = d.y * m_keyframe.m_speedIn.x / d.x; m_keyframe.m_speedOut.y = d.y * m_keyframe.m_speedOut.x / d.x; } } m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::unlinkHandles() { assert(m_kIndex >= 0 && m_indices.size() == 1); if (!m_keyframe.m_linkedHandles) return; m_changed = true; m_keyframe.m_linkedHandles = false; m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setSpeedIn(const TPointD &speedIn) { const double eps = 0.00001; assert(m_kIndex >= 0 && m_indices.size() == 1); assert(isSpeedInOut(m_kIndex - 1)); m_changed = true; m_keyframe.m_speedIn = speedIn; if (m_keyframe.m_speedIn.x > 0) m_keyframe.m_speedIn.x = 0; if (m_keyframe.m_linkedHandles && m_kIndex + 1 <= m_param->getKeyframeCount()) { double outNorm = getNorm(m_keyframe.m_speedOut); if (m_kIndex + 1 == m_param->getKeyframeCount() || isSpeedInOut(m_kIndex) || (m_keyframe.m_type == TDoubleKeyframe::Expression && m_keyframe.m_expressionText.find("cycle") != std::string::npos)) { // update next segment speed vector double inNorm = getNorm(m_keyframe.m_speedIn); if (inNorm < eps) m_keyframe.m_speedOut = TPointD(outNorm, 0); else m_keyframe.m_speedOut = -(outNorm / inNorm) * m_keyframe.m_speedIn; } else { // can't change next segment speed vector => adjust speedIn to be // collinear TPointD w = rotate90(m_keyframe.m_speedOut); double wNorm2 = norm2(w); if (wNorm2 > eps * eps) m_keyframe.m_speedIn -= (1.0 / wNorm2) * (w * m_keyframe.m_speedIn) * w; } } m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setSpeedOut(const TPointD &speedOut) { const double eps = 0.00001; assert(m_kIndex >= 0 && m_indices.size() == 1); assert(isSpeedInOut(m_kIndex)); m_changed = true; m_keyframe.m_speedOut = speedOut; if (m_keyframe.m_speedOut.x < 0) m_keyframe.m_speedOut.x = 0; if (m_keyframe.m_linkedHandles && m_kIndex > 0) { double inNorm = getNorm(m_keyframe.m_speedIn); if (isSpeedInOut(m_kIndex - 1)) { // update previous segment speed vector double outNorm = getNorm(m_keyframe.m_speedOut); if (outNorm > eps) m_keyframe.m_speedIn = -inNorm / outNorm * m_keyframe.m_speedOut; // else // m_keyframe.m_speedIn = TPointD(inNorm,0); } else { // can't change previous segment speed vector => adjust speedOut to be // collinear double h = 0.00001; double f = m_keyframe.m_frame; double v = (m_param->getValue(f) - m_param->getValue(f - h)) / h; TPointD w(-v, 1); double wNorm2 = norm2(w); if (wNorm2 > eps * eps) m_keyframe.m_speedOut -= (1.0 / wNorm2) * (w * m_keyframe.m_speedOut) * w; } } m_param->setKeyframe(m_kIndex, m_keyframe); } void KeyframeSetter::setEaseIn(double easeIn) { // easeIn <=0 assert(m_kIndex >= 1 && m_indices.size() == 1); assert(isEaseInOut(m_kIndex - 1)); m_changed = true; TDoubleKeyframe prevKeyframe = m_param->getKeyframe(m_kIndex - 1); bool isPercentage = prevKeyframe.m_type == TDoubleKeyframe::EaseInOutPercentage; if (!isPercentage) easeIn = floor(easeIn + 0.5); double segmentWidth = isPercentage ? 100.0 : m_keyframe.m_frame - prevKeyframe.m_frame; easeIn = -tcrop(-easeIn, 0.0, segmentWidth); m_keyframe.m_speedIn = TPointD(easeIn, 0); if (prevKeyframe.m_speedOut.x - easeIn > segmentWidth) { m_undo->addKeyframe(m_kIndex - 1); prevKeyframe.m_speedOut.x = segmentWidth + easeIn; std::map keyframes; keyframes[m_kIndex - 1] = prevKeyframe; keyframes[m_kIndex] = m_keyframe; m_param->setKeyframes(keyframes); } else { m_param->setKeyframe(m_kIndex, m_keyframe); } } void KeyframeSetter::setEaseOut(double easeOut) { assert(m_kIndex >= 0 && m_kIndex + 1 < m_param->getKeyframeCount() && m_indices.size() == 1); assert(isEaseInOut(m_kIndex)); m_changed = true; TDoubleKeyframe nextKeyframe = m_param->getKeyframe(m_kIndex + 1); bool isPercentage = m_keyframe.m_type == TDoubleKeyframe::EaseInOutPercentage; if (!isPercentage) easeOut = floor(easeOut + 0.5); double segmentWidth = isPercentage ? 100.0 : nextKeyframe.m_frame - m_keyframe.m_frame; easeOut = tcrop(easeOut, 0.0, segmentWidth); m_keyframe.m_speedOut = TPointD(easeOut, 0); if (-nextKeyframe.m_speedIn.x + easeOut > segmentWidth) { m_undo->addKeyframe(m_kIndex + 1); nextKeyframe.m_speedIn.x = easeOut - segmentWidth; std::map keyframes; keyframes[m_kIndex + 1] = nextKeyframe; keyframes[m_kIndex] = m_keyframe; m_param->setKeyframes(keyframes); } else { m_param->setKeyframe(m_kIndex, m_keyframe); } } //============================================================================= // set the curve params adaptively by clicking apply button void KeyframeSetter::setAllParams( int step, TDoubleKeyframe::Type comboType, const TPointD &speedIn, const TPointD &speedOut, std::string expressionText, std::string unitName, const TDoubleKeyframe::FileParams &fileParam, double similarShapeOffset) { assert(0 <= m_kIndex && m_kIndex < m_param->getKeyframeCount()); TDoubleKeyframe keyframe = m_param->getKeyframe(m_kIndex); TDoubleKeyframe nextKeyframe; // get the next key if (m_kIndex + 1 < m_param->getKeyframeCount()) nextKeyframe = m_param->getKeyframe(m_kIndex + 1); else comboType = TDoubleKeyframe::Linear; // I'm going to change kIndex. Make sure it is selected. set the dirty flag m_undo->addKeyframe(m_kIndex); m_indices.insert(m_kIndex); m_changed = true; // set step if (step < 1) step = 1; keyframe.m_step = step; // set type keyframe.m_type = comboType; // set parameters according to the type std::map keyframes; switch (comboType) { case TDoubleKeyframe::SpeedInOut: keyframe.m_speedOut = speedOut; nextKeyframe.m_speedIn = speedIn; if (keyframe.m_speedOut.x < 0) keyframe.m_speedOut.x = 0; if (nextKeyframe.m_speedIn.x > 0) nextKeyframe.m_speedIn.x = 0; break; case TDoubleKeyframe::EaseInOut: case TDoubleKeyframe::EaseInOutPercentage: keyframe.m_speedOut = speedOut; nextKeyframe.m_speedIn = speedIn; if (keyframe.m_type == TDoubleKeyframe::EaseInOut) { keyframe.m_speedOut.x = floor(speedOut.x + 0.5); nextKeyframe.m_speedIn.x = floor(speedIn.x + 0.5); } break; case TDoubleKeyframe::Expression: keyframe.m_expressionText = expressionText; keyframe.m_unitName = unitName; break; case TDoubleKeyframe::File: keyframe.m_fileParams = fileParam; keyframe.m_unitName = unitName; break; case TDoubleKeyframe::SimilarShape: keyframe.m_expressionText = expressionText; keyframe.m_similarShapeOffset = similarShapeOffset; break; default: break; } /*--- リンクされたカーブの処理 ---*/ const double eps = 0.00001; if (m_kIndex != 0 && keyframe.m_linkedHandles && keyframe.m_prevType == TDoubleKeyframe::SpeedInOut) { double inNorm = getNorm(keyframe.m_speedIn); // update previous segment speed vector double outNorm = getNorm(keyframe.m_speedOut); if (outNorm > eps) keyframe.m_speedIn = -inNorm / outNorm * keyframe.m_speedOut; } // Next curve if (m_kIndex + 2 < m_param->getKeyframeCount() && nextKeyframe.m_linkedHandles && nextKeyframe.m_type == TDoubleKeyframe::SpeedInOut) { double outNorm = getNorm(nextKeyframe.m_speedOut); // update next segment speed vector double inNorm = getNorm(nextKeyframe.m_speedIn); if (inNorm < eps) nextKeyframe.m_speedOut = TPointD(outNorm, 0); else nextKeyframe.m_speedOut = -(outNorm / inNorm) * nextKeyframe.m_speedIn; } // set modified keyframe keyframes[m_kIndex] = keyframe; if (m_kIndex + 1 < m_param->getKeyframeCount()) keyframes[m_kIndex + 1] = nextKeyframe; m_param->setKeyframes(keyframes); } //============================================================================= class RemoveKeyframeUndo final : public TUndo { TDoubleParam *m_param; TDoubleKeyframe m_keyframe; public: RemoveKeyframeUndo(TDoubleParam *param, int kIndex) : m_param(param) { m_param->addRef(); m_keyframe = m_param->getKeyframe(kIndex); } ~RemoveKeyframeUndo() { m_param->release(); } void undo() const override { m_param->setKeyframe(m_keyframe); } void redo() const override { m_param->deleteKeyframe(m_keyframe.m_frame); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { return QObject::tr("Remove Keyframe"); } }; //============================================================================= void KeyframeSetter::removeKeyframeAt(TDoubleParam *curve, double frame) { int kIndex = curve->getClosestKeyframe(frame); if (kIndex < 0 || kIndex >= curve->getKeyframeCount() || curve->keyframeIndexToFrame(kIndex) != frame) return; TUndoManager::manager()->add(new RemoveKeyframeUndo(curve, kIndex)); curve->deleteKeyframe(frame); } //============================================================================= class EnableCycleUndo final : public TUndo { TDoubleParam *m_param; public: EnableCycleUndo(TDoubleParam *param) : m_param(param) { m_param->addRef(); } ~EnableCycleUndo() { m_param->release(); } void invertCycleEnabled() const { bool isEnabled = m_param->isCycleEnabled(); m_param->enableCycle(!isEnabled); } void undo() const override { invertCycleEnabled(); } void redo() const override { invertCycleEnabled(); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { return QObject::tr("Cycle"); } }; //============================================================================= void KeyframeSetter::enableCycle(TDoubleParam *curve, bool enabled) { curve->enableCycle(enabled); TUndoManager::manager()->add(new EnableCycleUndo(curve)); }