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