Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "tundo.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzExt includes
Toshihiro Shimizu 890ddd
#include "ext/plasticdeformerstorage.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzLib includes
Toshihiro Shimizu 890ddd
#include "toonz/txshcell.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshsimplelevel.h"
Toshihiro Shimizu 890ddd
#include "toonz/txsheethandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/stage.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// tcg includes
Toshihiro Shimizu 890ddd
#include "tcg/tcg_point_ops.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_algorithm.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_function_types.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_iterator_ops.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "plastictool.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace PlasticToolLocals;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    Local namespace  stuff
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TPointD closestMeshVertexPos(const TPointD &pos, double *distance = 0) {
Shinya Kitaoka 120a6e
  const TXshCell &imageCell = TTool::getImageCell();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TXshSimpleLevel *sl = imageCell.getSimpleLevel();
Shinya Kitaoka 120a6e
  assert(sl);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TMeshImageP mi(TTool::getImage(false));
Shinya Kitaoka 120a6e
  assert(mi);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Retrieve *level* dpi
Shinya Kitaoka 120a6e
  // NOTE: This is different than current IMAGE's dpi. An image is actually
Shinya Kitaoka 120a6e
  // displayed with
Shinya Kitaoka 120a6e
  //       its level owner's dpi, RATHER than its own.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TPointD &dpi = sl->getDpi(imageCell.getFrameId());
Shinya Kitaoka 120a6e
  assert(dpi.x > 0.0 && dpi.y > 0.0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Cast pos to image coordinates
Shinya Kitaoka 120a6e
  const TPointD pos_mesh(pos.x * (dpi.x / Stage::inch),
Shinya Kitaoka 120a6e
                         pos.y * (dpi.y / Stage::inch));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Retrieve the closest vertex to pos_mesh
Shinya Kitaoka 120a6e
  const std::pair<double, plastictool::meshindex=""> &closest =</double,>
Shinya Kitaoka 120a6e
      PlasticToolLocals::closestVertex(*mi, pos_mesh);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TPointD &vxPos_mesh =
Shinya Kitaoka 120a6e
      mi->meshes()[closest.second.m_meshIdx]->vertex(closest.second.m_idx).P();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (distance)
Shinya Kitaoka 120a6e
    *distance =
Shinya Kitaoka 120a6e
        std::min(Stage::inch / dpi.x, Stage::inch / dpi.y) * closest.first;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Cast it back to world coordinates
Shinya Kitaoka 120a6e
  return TPointD(vxPos_mesh.x * (Stage::inch / dpi.x),
Shinya Kitaoka 120a6e
                 vxPos_mesh.y * (Stage::inch / dpi.y));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TPointD closestSkeletonVertexPos(const TPointD &pos) {
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static inline double dist2(const TPointD &pos,
Shinya Kitaoka 120a6e
                               const PlasticSkeletonVertex &vx) {
Shinya Kitaoka 120a6e
      return tcg::point_ops::dist2(pos, vx.P());
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  };
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = l_plasticTool.skeleton();
Shinya Kitaoka 120a6e
  if (!skeleton || skeleton->empty()) return TConsts::napd;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const PlasticSkeleton::vertices_container &vertices = skeleton->vertices();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return tcg::min_transform(vertices.begin(), vertices.end(),
Shinya Kitaoka 120a6e
                            tcg::bind1st(&locals::dist2, pos))
Shinya Kitaoka 120a6e
      ->P();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    Undo  definitions
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class VertexUndo : public TUndo {
Toshihiro Shimizu 890ddd
protected:
Shinya Kitaoka 120a6e
  int m_row, m_col;  //!< Xsheet coordinates
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int m_v, m_vParent;          //!< Indices of the added vertex and its parent
Shinya Kitaoka 120a6e
  PlasticSkeletonVertex m_vx;  //!< Added vertex
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<int> m_children;  //!< Children of the vertex to insert</int>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  VertexUndo() : m_row(::row()), m_col(::column()), m_v(-1), m_vParent(-1) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int getSize() const { return sizeof(*this); }  // sizeof this is roughly ok
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void storeChildren(const PlasticSkeleton &skeleton,
Shinya Kitaoka 120a6e
                     const PlasticSkeletonVertex &vx) {
Shinya Kitaoka 120a6e
    m_children.clear();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Traverse vx's edges and build its children table
Shinya Kitaoka 120a6e
    PlasticSkeletonVertex::edges_const_iterator et, eEnd(vx.edgesEnd());
Shinya Kitaoka 120a6e
    for (et = vx.edgesBegin(); et != eEnd; ++et) {
Shinya Kitaoka 120a6e
      int vChild = skeleton.edge(*et).vertex(1);
Shinya Kitaoka 120a6e
      if (vChild == vx.getIndex()) continue;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_children.push_back(vChild);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void addVertex() {
Shinya Kitaoka 120a6e
    assert(m_vx.edges().empty());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Load the right skeleton by moving to the stored xsheet pos
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const PlasticSkeletonP &skeleton = l_plasticTool.skeleton();
Shinya Kitaoka 120a6e
    TCG_ASSERT(skeleton || m_vParent < 0, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Perform addition
Shinya Kitaoka 120a6e
    l_plasticTool.setSkeletonSelection(m_vParent);
Shinya Kitaoka 120a6e
    l_plasticTool.addVertex(m_vx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Store data to invert the operation
Shinya Kitaoka 120a6e
    assert(l_plasticTool.skeletonVertexSelection().hasSingleObject());
Shinya Kitaoka 120a6e
    m_v = l_plasticTool.skeletonVertexSelection();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void insertVertex() {
Shinya Kitaoka 120a6e
    if (m_children.empty()) {
Shinya Kitaoka 120a6e
      addVertex();
Shinya Kitaoka 120a6e
      return;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    assert(m_vx.edges().empty());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TCG_ASSERT(m_vParent >= 0, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const PlasticSkeletonP &skeleton = l_plasticTool.skeleton();
Shinya Kitaoka 120a6e
    TCG_ASSERT(skeleton, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Perform insertion
Shinya Kitaoka 120a6e
    l_plasticTool.insertVertex(m_vx, m_vParent, m_children);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Store data to invert the operation
Shinya Kitaoka 120a6e
    assert(l_plasticTool.skeletonVertexSelection().hasSingleObject());
Shinya Kitaoka 120a6e
    m_v = l_plasticTool.skeletonVertexSelection();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void removeVertex() {
Shinya Kitaoka 120a6e
    TCG_ASSERT(m_v >= 0, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const PlasticSkeletonP &skeleton = l_plasticTool.skeleton();
Shinya Kitaoka 120a6e
    TCG_ASSERT(skeleton, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Store data to invert the operation
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      const PlasticSkeletonVertex &vx = skeleton->vertex(m_v);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_vParent = vx.parent();
Shinya Kitaoka 120a6e
      m_vx      = PlasticSkeletonVertex(vx.P());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      storeChildren(*skeleton, vx);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Perform removal
Shinya Kitaoka 120a6e
    if (m_v > 0) {
Shinya Kitaoka 120a6e
      l_plasticTool.setSkeletonSelection(m_v);
Shinya Kitaoka 120a6e
      l_plasticTool.removeVertex();
Shinya Kitaoka 120a6e
    } else
Shinya Kitaoka 120a6e
      l_plasticTool.removeSkeleton(::skeletonId());
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class AddVertexUndo : public VertexUndo {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  AddVertexUndo(int vParent, const PlasticSkeletonVertex &vx) {
Shinya Kitaoka 120a6e
    m_vParent = vParent, m_vx = vx;
Shinya Kitaoka 120a6e
    assert(m_vx.edges().empty());
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    const_cast<addvertexundo &="">(*this).VertexUndo::addVertex();</addvertexundo>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    const_cast<addvertexundo &="">(*this).VertexUndo::removeVertex();</addvertexundo>
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class RemoveVertexUndo : public VertexUndo {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  RemoveVertexUndo(int v) {
Shinya Kitaoka 120a6e
    assert(v >= 0);
Shinya Kitaoka 120a6e
    m_v = v;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    const_cast<removevertexundo &="">(*this).VertexUndo::removeVertex();</removevertexundo>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    const_cast<removevertexundo &="">(*this).VertexUndo::insertVertex();</removevertexundo>
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//========================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class InsertVertexUndo : public VertexUndo {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  InsertVertexUndo(int e, const PlasticSkeletonVertex &vx) {
Shinya Kitaoka 120a6e
    const PlasticSkeleton &skeleton      = *l_plasticTool.skeleton();
Shinya Kitaoka 120a6e
    const PlasticSkeleton::edge_type &ed = skeleton.edge(e);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_vParent = ed.vertex(0), m_vx = vx;
Shinya Kitaoka 120a6e
    std::vector<int>(1, ed.vertex(1)).swap(m_children);</int>
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    const_cast<insertvertexundo &="">(*this).VertexUndo::insertVertex();</insertvertexundo>
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    TCG_ASSERT(!m_children.empty(), return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const_cast<insertvertexundo &="">(*this).VertexUndo::removeVertex();</insertvertexundo>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
//========================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class AddSkeletonUndo : public TUndo {
Shinya Kitaoka 120a6e
protected:
Shinya Kitaoka 120a6e
  int m_row, m_col;  //!< Xsheet coordinates
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int m_skelId;                 //!< The added skeleton's id
Shinya Kitaoka 120a6e
  PlasticSkeletonP m_skeleton;  //!< A COPY of the added skeleton
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
public:
Shinya Kitaoka 120a6e
  AddSkeletonUndo(int skelId, const PlasticSkeletonP &skeletonCopy)
Shinya Kitaoka 120a6e
      : m_row(::row())
Shinya Kitaoka 120a6e
      , m_col(::column())
Shinya Kitaoka 120a6e
      , m_skelId(skelId)
Shinya Kitaoka 120a6e
      , m_skeleton(skeletonCopy) {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // This is not correct... it's tough to build the right one! We're like
Shinya Kitaoka 120a6e
  // storing the whole
Shinya Kitaoka 120a6e
  // cleared deformation! So, I guess 1 MB (100 of these in the standard undos
Shinya Kitaoka 120a6e
  // pool)
Shinya Kitaoka 120a6e
  // is a reasonable estimate...
Shinya Kitaoka 120a6e
  int getSize() const { return 1 << 20; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.addSkeleton(m_skelId, new PlasticSkeleton(*m_skeleton));
Shinya Kitaoka 120a6e
    ::invalidateXsheet();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.removeSkeleton(m_skelId);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//========================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class RemoveSkeletonUndo : public AddSkeletonUndo {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  RemoveSkeletonUndo(int skelId)
Shinya Kitaoka 120a6e
      : AddSkeletonUndo(skelId, l_plasticTool.skeleton()) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void redo() const { AddSkeletonUndo::undo(); }
Shinya Kitaoka 120a6e
  void undo() const { AddSkeletonUndo::redo(); }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class RemoveSkeletonUndo_WithKeyframes : public RemoveSkeletonUndo {
Shinya Kitaoka 120a6e
  mutable std::vector<tdoublekeyframe></tdoublekeyframe>
Shinya Kitaoka 120a6e
      m_skelIdsKeyframes;  //!< Skeleton Ids param curve keyframes
Shinya Kitaoka 120a6e
                           //!< for m_skelId
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  RemoveSkeletonUndo_WithKeyframes(int skelId) : RemoveSkeletonUndo(skelId) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    // Erase all keyframes corresponding to m_skelId from sd's skeleton ids
Shinya Kitaoka 120a6e
    // curve
Shinya Kitaoka 120a6e
    const SkDP &sd = l_plasticTool.deformation();
Shinya Kitaoka 120a6e
    TCG_ASSERT(sd, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TDoubleParamP &skelIdsParam = sd->skeletonIdsParam();
Shinya Kitaoka 120a6e
    if (skelIdsParam->getKeyframeCount() > 0) {
Shinya Kitaoka 120a6e
      double frame;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      for (int k = 0; k >= 0; k = skelIdsParam->getNextKeyframe(frame)) {
Shinya Kitaoka 120a6e
        const TDoubleKeyframe &kf = skelIdsParam->getKeyframe(k);
Shinya Kitaoka 120a6e
        frame                     = kf.m_frame;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        if (m_skelId == (int)kf.m_value) {
Shinya Kitaoka 120a6e
          m_skelIdsKeyframes.push_back(kf);
Shinya Kitaoka 120a6e
          skelIdsParam->deleteKeyframe(frame);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    RemoveSkeletonUndo::redo();  // Invalidates the xsheet
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    l_plasticTool
Shinya Kitaoka 120a6e
        .touchDeformation();  // Skeleton removal could have destroyed the sd
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore saved keyframes to sd's skelIdsParam curve
Shinya Kitaoka 120a6e
    const SkDP &sd = l_plasticTool.deformation();
Shinya Kitaoka 120a6e
    assert(sd);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TDoubleParamP &skelIdsParam = sd->skeletonIdsParam();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    std::vector<tdoublekeyframe>::iterator kt, kEnd(m_skelIdsKeyframes.end());</tdoublekeyframe>
Shinya Kitaoka 120a6e
    for (kt = m_skelIdsKeyframes.begin(); kt != kEnd; ++kt)
Shinya Kitaoka 120a6e
      skelIdsParam->setKeyframe(*kt);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_skelIdsKeyframes.clear();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    RemoveSkeletonUndo::undo();  // Invalidates the xsheet
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//========================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class SetSkeletonIdUndo : public TUndo {
Shinya Kitaoka 120a6e
  int m_row, m_col;  //!< Xsheet coordinates
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int m_skelId;  //!< The new skeleton id value
Shinya Kitaoka 120a6e
  mutable TDoubleKeyframe
Shinya Kitaoka 120a6e
      m_oldKf;  //!< Old keyframe values for skelIds parameter
Shinya Kitaoka 120a6e
  mutable bool m_added1stKeyframe;  //!< Whether the redo() added the first
Shinya Kitaoka 120a6e
                                    //!skelIds keyframe
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  SetSkeletonIdUndo(int skelId)
Shinya Kitaoka 120a6e
      : m_row(::row()), m_col(::column()), m_skelId(skelId) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int getSize() const { return sizeof(*this); }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const SkDP &sd = l_plasticTool.deformation();
Shinya Kitaoka 120a6e
    TCG_ASSERT(sd, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TDoubleParamP &skelIdsParam = sd->skeletonIdsParam();
Shinya Kitaoka 120a6e
    double frame                      = ::frame();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_oldKf            = skelIdsParam->getKeyframeAt(frame);
Shinya Kitaoka 120a6e
    m_added1stKeyframe = false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (frame > 0.0 && (skelIdsParam->getKeyframeCount() == 0 ||
Shinya Kitaoka 120a6e
                        skelIdsParam->getKeyframe(0).m_frame >= frame)) {
Shinya Kitaoka 120a6e
      // Put a keyframe at the previous cell to preserve values before current
Shinya Kitaoka 120a6e
      // frame
Shinya Kitaoka 120a6e
      TDoubleKeyframe kf(frame - 1.0, skelIdsParam->getDefaultValue());
Shinya Kitaoka 120a6e
      kf.m_type = TDoubleKeyframe::Constant;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      skelIdsParam->setKeyframe(kf);
Shinya Kitaoka 120a6e
      m_added1stKeyframe = true;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TDoubleKeyframe kf(frame, m_skelId);
Shinya Kitaoka 120a6e
    kf.m_type = TDoubleKeyframe::Constant;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    skelIdsParam->setKeyframe(kf);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // No need to invoke PlasticTool::storeSkeletonId() - automatic through
Shinya Kitaoka 120a6e
    // onChange()
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const SkDP &sd = l_plasticTool.deformation();
Shinya Kitaoka 120a6e
    TCG_ASSERT(sd, return );
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TDoubleParamP &skelIdsParam = sd->skeletonIdsParam();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (m_oldKf.m_isKeyframe)
Shinya Kitaoka 120a6e
      skelIdsParam->setKeyframe(m_oldKf);
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      skelIdsParam->deleteKeyframe(m_oldKf.m_frame);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (m_added1stKeyframe) {
Shinya Kitaoka 120a6e
      const TDoubleKeyframe &kf = skelIdsParam->getKeyframe(0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      assert(kf.m_value == skelIdsParam->getDefaultValue());
Shinya Kitaoka 120a6e
      if (kf.m_value == skelIdsParam->getDefaultValue())
Shinya Kitaoka 120a6e
        skelIdsParam->deleteKeyframe(kf.m_frame);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//========================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class MoveVertexUndo_Build : public TUndo {
Shinya Kitaoka 120a6e
  int m_row, m_col;  //!< Xsheet coordinates
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<int> m_vIdxs;           //!< Moved vertices</int>
Shinya Kitaoka 120a6e
  std::vector<tpointd> m_origVxsPos;  //!< Original vertex positions</tpointd>
Shinya Kitaoka 120a6e
  TPointD m_posShift;                 //!< Vertex positions shift
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  MoveVertexUndo_Build(const std::vector<int> &vIdxs,</int>
Shinya Kitaoka 120a6e
                       const std::vector<tpointd> &origVxsPos,</tpointd>
Shinya Kitaoka 120a6e
                       const TPointD &posShift)
Shinya Kitaoka 120a6e
      : m_row(::row())
Shinya Kitaoka 120a6e
      , m_col(::column())
Shinya Kitaoka 120a6e
      , m_vIdxs(vIdxs)
Shinya Kitaoka 120a6e
      , m_origVxsPos(origVxsPos)
Shinya Kitaoka 120a6e
      , m_posShift(posShift) {
Shinya Kitaoka 120a6e
    assert(m_vIdxs.size() == m_origVxsPos.size());
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int getSize() const {
Shinya Kitaoka 120a6e
    return int(sizeof(*this) +
Shinya Kitaoka 120a6e
               m_vIdxs.size() * (sizeof(int) + 2 * sizeof(TPointD)));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void redo() const {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.setSkeletonSelection(m_vIdxs);
Shinya Kitaoka 120a6e
    l_plasticTool.moveVertex_build(m_origVxsPos, m_posShift);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::stageObject()
Shinya Kitaoka 120a6e
        ->invalidate();  // Should be a TStageObject's implementation detail ...
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void undo() const {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.setSkeletonSelection(m_vIdxs);
Shinya Kitaoka 120a6e
    l_plasticTool.moveVertex_build(m_origVxsPos, TPointD());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::stageObject()
Shinya Kitaoka 120a6e
        ->invalidate();  // Should be a TStageObject's implementation detail ...
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    PlasticTool  functions
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::mouseMove_build(const TPointD &pos, const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  m_pos = pos;  // Needs to be done now - ensures m_pos is valid
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_svHigh = m_seHigh = -1;  // Reset highlighted primitives
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double d, highlightRadius = getPixelSize() * HIGHLIGHT_DISTANCE;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = this->skeleton();
Shinya Kitaoka 120a6e
  if (skeleton) {
Shinya Kitaoka 120a6e
    // Search nearest skeleton entities
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Look for nearest vertex
Shinya Kitaoka 120a6e
    int v = skeleton->closestVertex(pos, &d);
Shinya Kitaoka 120a6e
    if (v >= 0 && d < highlightRadius)
Shinya Kitaoka 120a6e
      m_svHigh = v;
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      // Look for nearest edge
Shinya Kitaoka 120a6e
      int e = skeleton->closestEdge(pos, &d);
Shinya Kitaoka 120a6e
      if (e >= 0 && d < highlightRadius) m_seHigh = e;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_svHigh < 0 && m_seHigh < 0) {
Shinya Kitaoka 120a6e
    // No highlighted skeleton primitive. Mouse position may be
Shinya Kitaoka 120a6e
    // a candidate for vertex addition - snap to mesh if required
Shinya Kitaoka 120a6e
    if (m_snapToMesh.getValue()) {
Shinya Kitaoka 120a6e
      const TPointD &mvPos = ::closestMeshVertexPos(
Shinya Kitaoka 120a6e
          pos, &d);  // No need to check against closest skeleton vertex,
Shinya Kitaoka 120a6e
                     // since vertex highlighting kicks in at the same time
Shinya Kitaoka 120a6e
      if (d < highlightRadius)  // the snapping would take place.
Shinya Kitaoka 120a6e
        m_pos = mvPos;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::leftButtonDown_build(const TPointD &pos,
Shinya Kitaoka 120a6e
                                       const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  m_pressedPos = m_pos = pos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Update selections
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    const PlasticSkeletonP &skel = skeleton();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (m_svHigh >= 0) {
Shinya Kitaoka 120a6e
      PlasticVertexSelection vSel(
Shinya Kitaoka 120a6e
          me.isShiftPressed()
Shinya Kitaoka 120a6e
              ? PlasticVertexSelection(branchSelection(m_svHigh))
Shinya Kitaoka 120a6e
              : PlasticVertexSelection(m_svHigh));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (me.isCtrlPressed())
Shinya Kitaoka 120a6e
        toggleSkeletonSelection(vSel);
Shinya Kitaoka 120a6e
      else if (!m_svSel.contains(vSel))
Shinya Kitaoka 120a6e
        setSkeletonSelection(vSel);
Shinya Kitaoka 120a6e
    } else if (m_seHigh >= 0) {
Shinya Kitaoka 120a6e
      // Insert a vertex in the edge
Shinya Kitaoka 120a6e
      TUndo *op = new InsertVertexUndo(
Shinya Kitaoka 120a6e
          m_seHigh, PlasticSkeletonVertex(projection(*skel, m_seHigh, m_pos)));
Shinya Kitaoka 120a6e
      TUndoManager::manager()->add(op);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      op->redo();
Shinya Kitaoka 120a6e
    } else if (!skel || skel->empty() || m_svSel.hasSingleObject()) {
Shinya Kitaoka 120a6e
      // Snap to mesh if required
Shinya Kitaoka 120a6e
      if (m_snapToMesh.getValue()) {
Shinya Kitaoka 120a6e
        double d, highlightRadius = getPixelSize() * HIGHLIGHT_DISTANCE;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        const TPointD &mvPos = ::closestMeshVertexPos(
Shinya Kitaoka 120a6e
            pos, &d);  // Again, no need to check against closest
Shinya Kitaoka 120a6e
                       // skeleton vertex.
Shinya Kitaoka 120a6e
        if (d < highlightRadius) m_pos = mvPos;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Add a new vertex
Shinya Kitaoka 120a6e
      TUndo *op = new AddVertexUndo(m_svSel, PlasticSkeletonVertex(m_pos));
Shinya Kitaoka 120a6e
      TUndoManager::manager()->add(op);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      op->redo();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      assert(skeleton());
Shinya Kitaoka 120a6e
    } else
Shinya Kitaoka 120a6e
      setSkeletonSelection(-1);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Start move vertex operation
Shinya Kitaoka 120a6e
  if (!m_svSel.isEmpty()) {
Shinya Kitaoka 120a6e
    struct locals {
Shinya Kitaoka 120a6e
      static TPointD vertexPos(const PlasticSkeleton &skel, int v) {
Shinya Kitaoka 120a6e
        return skel.vertex(v).P();
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    };
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    const PlasticSkeletonP &skel = skeleton();
Shinya Kitaoka 120a6e
    assert(skel);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Adjust mouse press to the selected skeleton vertex, if necessary
Shinya Kitaoka 120a6e
    if (m_svSel.hasSingleObject()) m_pressedPos = skel->vertex(m_svSel).P();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Store original vertex positions
Shinya Kitaoka 120a6e
    m_pressedVxsPos = std::vector<tpointd>(</tpointd>
Shinya Kitaoka 120a6e
        tcg::make_cast_it(m_svSel.objects().begin(),
Shinya Kitaoka 120a6e
                          tcg::bind1st(&locals::vertexPos, *skel)),
Shinya Kitaoka 120a6e
        tcg::make_cast_it(m_svSel.objects().end(),
Shinya Kitaoka 120a6e
                          tcg::bind1st(&locals::vertexPos, *skel)));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::leftButtonDrag_build(const TPointD &pos,
Shinya Kitaoka 120a6e
                                       const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  if (m_snapToMesh.getValue()) {
Shinya Kitaoka 120a6e
    const TPointD &mvPos = ::closestMeshVertexPos(pos),
Shinya Kitaoka 120a6e
                  &svPos = ::closestSkeletonVertexPos(mvPos);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (tcg::point_ops::dist(mvPos, svPos) >
Shinya Kitaoka 120a6e
        getPixelSize())  // 1) If said distance is sub-pixel, the user
Shinya Kitaoka 120a6e
      m_pos = mvPos;  //    just cannot see the assignment - so it's not safe.
Shinya Kitaoka 120a6e
  }                   // 2) The moveVertex_build() below manipulates m_pos
Shinya Kitaoka 120a6e
  else                //    (not how m_pressedPos is subtracted), so !=
Shinya Kitaoka 120a6e
    m_pos = pos;      //    is not a choice.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  moveVertex_build(m_pressedVxsPos, m_pos - m_pressedPos);
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::leftButtonUp_build(const TPointD &pos,
Shinya Kitaoka 120a6e
                                     const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  if (m_snapToMesh.getValue()) {
Shinya Kitaoka 120a6e
    const TPointD &mvPos = ::closestMeshVertexPos(pos),
Shinya Kitaoka 120a6e
                  &svPos = ::closestSkeletonVertexPos(mvPos);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (tcg::point_ops::dist(mvPos, svPos) > getPixelSize())  // Same as above
Shinya Kitaoka 120a6e
      m_pos = mvPos;
Shinya Kitaoka 120a6e
  } else
Shinya Kitaoka 120a6e
    m_pos = pos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!m_svSel.isEmpty() && m_dragged) {
Shinya Kitaoka 120a6e
    TUndoManager::manager()->add(new MoveVertexUndo_Build(
Shinya Kitaoka 120a6e
        m_svSel.objects(), m_pressedVxsPos, m_pos - m_pressedPos));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::stageObject()
Shinya Kitaoka 120a6e
        ->invalidate();  // Should be a TStageObject's implementation detail ...
Shinya Kitaoka 120a6e
    invalidate();        // .. it's that it caches placement data and we must
Shinya Kitaoka 120a6e
  }                      // invalidate it. Gross. Can't we do anything about it?
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::addContextMenuActions_build(QMenu *menu) {
Shinya Kitaoka 120a6e
  bool ret = true;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!m_svSel.isEmpty()) {
Shinya Kitaoka 120a6e
    QAction *deleteVertex = menu->addAction(tr("Delete Vertex"));
Shinya Kitaoka 120a6e
    ret = ret && connect(deleteVertex, SIGNAL(triggered()), &l_plasticTool,
Shinya Kitaoka 120a6e
                         SLOT(deleteSelectedVertex_undo()));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    menu->addSeparator();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(ret);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::moveVertex_build(const std::vector<tpointd> &origVxsPos,</tpointd>
Shinya Kitaoka 120a6e
                                   const TPointD &posShift) {
Shinya Kitaoka 120a6e
  if (!m_svSel.isEmpty()) {
Shinya Kitaoka 120a6e
    const PlasticSkeletonP &skeleton = this->skeleton();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Move selected vertices
Shinya Kitaoka 120a6e
    int v, vCount = int(m_svSel.objects().size());
Shinya Kitaoka 120a6e
    for (v = 0; v != vCount; ++v)
Shinya Kitaoka 120a6e
      skeleton->moveVertex(m_svSel.objects()[v], origVxsPos[v] + posShift);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Deformation must be recompiled
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->invalidateSkeleton(
Shinya Kitaoka 120a6e
        m_sd.getPointer(), ::skeletonId(), PlasticDeformerStorage::ALL);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (m_mode.getIndex() == ANIMATE_IDX)
Shinya Kitaoka 120a6e
      storeDeformation();  // Rebuild deformed skeleton - default values have
Shinya Kitaoka 120a6e
                           // changed
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::addVertex(const PlasticSkeletonVertex &vx) {
Shinya Kitaoka 120a6e
  touchSkeleton();
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = this->skeleton();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  l_suspendParamsObservation =
Shinya Kitaoka 120a6e
      true;  // Some vertex parameters change during insert
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(
Shinya Kitaoka 120a6e
      m_svSel.isEmpty() ||
Shinya Kitaoka 120a6e
      m_svSel
Shinya Kitaoka 120a6e
          .hasSingleObject());  // Could there be no parent (inserting the root)
Shinya Kitaoka 120a6e
  setSkeletonSelection(skeleton->addVertex(vx, m_svSel));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  l_suspendParamsObservation = false;
Shinya Kitaoka 120a6e
  onChange();  // Update once after add
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // NOTE: Root addition does NOT currently add channels, so the above
Shinya Kitaoka 120a6e
  // onChange() is
Shinya Kitaoka 120a6e
  // quite necessary to cover that case too!
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Xsheet change notification is necessary to inform the Function Editor that
Shinya Kitaoka 120a6e
  // new
Shinya Kitaoka 120a6e
  // channels (the vertex deformation ones) have been introduced
Shinya Kitaoka 120a6e
  TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
Shinya Kitaoka 120a6e
  PlasticDeformerStorage::instance()->invalidateSkeleton(
Shinya Kitaoka 120a6e
      m_sd.getPointer(), ::skeletonId(), PlasticDeformerStorage::ALL);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::insertVertex(const PlasticSkeletonVertex &vx, int parent,
Shinya Kitaoka 120a6e
                               const std::vector<int> &children) {</int>
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = this->skeleton();
Shinya Kitaoka 120a6e
  assert(skeleton);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  l_suspendParamsObservation =
Shinya Kitaoka 120a6e
      true;  // Some vertex parameters change during insert.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  setSkeletonSelection(skeleton->insertVertex(vx, parent, children));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  l_suspendParamsObservation = false;
Shinya Kitaoka 120a6e
  onChange();  // Update once after insertion
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
Shinya Kitaoka 120a6e
  PlasticDeformerStorage::instance()->invalidateSkeleton(
Shinya Kitaoka 120a6e
      m_sd.getPointer(), ::skeletonId(), PlasticDeformerStorage::ALL);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::insertVertex(const PlasticSkeletonVertex &vx, int e) {
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = this->skeleton();
Shinya Kitaoka 120a6e
  assert(skeleton);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const PlasticSkeleton::edge_type &ed = skeleton->edge(e);
Shinya Kitaoka 120a6e
  insertVertex(vx, ed.vertex(0),
Shinya Kitaoka 120a6e
               std::vector<int>(1, skeleton->edge(e).vertex(1)));</int>
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::removeVertex() {
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = this->skeleton();
Shinya Kitaoka 120a6e
  assert(skeleton && m_svSel.hasSingleObject() && m_svSel > 0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  l_suspendParamsObservation =
Shinya Kitaoka 120a6e
      true;  // Some vertex parameters change during removal.
Shinya Kitaoka 120a6e
             // We need to avoid updating WHILE removing.
Shinya Kitaoka 120a6e
  skeleton->removeVertex(m_svSel);
Shinya Kitaoka 120a6e
  PlasticDeformerStorage::instance()->invalidateSkeleton(
Shinya Kitaoka 120a6e
      m_sd.getPointer(), ::skeletonId(), PlasticDeformerStorage::ALL);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  l_suspendParamsObservation = false;
Shinya Kitaoka 120a6e
  onChange();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  clearSkeletonSelections();  // Remove mesh references - could be
Shinya Kitaoka 120a6e
                              // invalidated...
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Xsheet change notification is necessary to inform the Function Editor that
Shinya Kitaoka 120a6e
  // some
Shinya Kitaoka 120a6e
  // channels (the vertex deformation ones) have been removed
Shinya Kitaoka 120a6e
  TTool::getApplication()
Shinya Kitaoka 120a6e
      ->getCurrentXsheet()
Shinya Kitaoka 120a6e
      ->notifyXsheetChanged();  // NOTE: This COULD invoke invalidate()...
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Rebuild the stage object's keyframes table
Shinya Kitaoka 120a6e
  stageObject()->updateKeyframes();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int PlasticTool::addSkeleton(const PlasticSkeletonP &skeleton) {
Shinya Kitaoka 120a6e
  assert(TTool::isEnabled());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  touchDeformation();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int skelId;
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    if (m_sd->empty())
Shinya Kitaoka 120a6e
      skelId = 1;
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      // Get the first unused skeleton id in m_sd
Shinya Kitaoka 120a6e
      SkD::skelId_iterator st, sEnd;
Shinya Kitaoka 120a6e
      m_sd->skeletonIds(st, sEnd);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      for (skelId = 1; st != sEnd && skelId == *st; ++skelId, ++st)
Shinya Kitaoka 120a6e
        ;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addSkeleton(skelId, skeleton);
Shinya Kitaoka 120a6e
  return skelId;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::addSkeleton(int skelId, const PlasticSkeletonP &skeleton) {
Shinya Kitaoka 120a6e
  assert(TTool::isEnabled());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  touchDeformation();
Shinya Kitaoka 120a6e
  m_sd->attach(skelId, skeleton.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  emit skelIdsListChanged();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::removeSkeleton(int skelId) {
Shinya Kitaoka 120a6e
  // Remove the entire deformation
Shinya Kitaoka 120a6e
  clearSkeletonSelections();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (m_sd) {
Shinya Kitaoka 120a6e
    m_sd->detach(skelId);
Shinya Kitaoka 120a6e
    if (m_sd->empty())
Shinya Kitaoka 120a6e
      stageObject()->setPlasticSkeletonDeformation(
Shinya Kitaoka 120a6e
          PlasticSkeletonDeformationP());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    ::invalidateXsheet();  // Updates m_sd
Shinya Kitaoka 120a6e
    emit skelIdsListChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::deleteSelectedVertex_undo() {
Shinya Kitaoka 120a6e
  if (m_svSel.isEmpty()) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TUndoManager *manager = TUndoManager::manager();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_svSel.contains(0)) {
Shinya Kitaoka 120a6e
    TUndo *op = new RemoveSkeletonUndo(::skeletonId());
Shinya Kitaoka 120a6e
    manager->add(op);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    op->redo();
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    typedef PlasticVertexSelection::objects_container objects_container;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    objects_container vertexIdxs =
Shinya Kitaoka 120a6e
        m_svSel.objects();  // Each undo will reset the vertex selection,
Shinya Kitaoka 120a6e
                            // so we need a copy
Shinya Kitaoka 120a6e
    manager->beginBlock();
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      objects_container::const_iterator vit, viEnd = vertexIdxs.end();
Shinya Kitaoka 120a6e
      for (vit = vertexIdxs.begin(); vit != viEnd; ++vit) {
Shinya Kitaoka 120a6e
        TUndo *op = new RemoveVertexUndo(*vit);
Shinya Kitaoka 120a6e
        manager->add(op);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        op->redo();
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    manager->endBlock();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int PlasticTool::addSkeleton_undo(const PlasticSkeletonP &skeleton) {
Shinya Kitaoka 120a6e
  int skelId;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TUndoManager *manager = TUndoManager::manager();
Shinya Kitaoka 120a6e
  manager->beginBlock();
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    skelId = l_plasticTool.addSkeleton(skeleton);
Shinya Kitaoka 120a6e
    assert(l_plasticTool.deformation());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TUndo *addUndo =
Shinya Kitaoka 120a6e
        new AddSkeletonUndo(skelId, new PlasticSkeleton(*skeleton));
Shinya Kitaoka 120a6e
    manager->add(addUndo);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TUndo *setIdUndo = new SetSkeletonIdUndo(skelId);
Shinya Kitaoka 120a6e
    manager->add(setIdUndo);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    setIdUndo->redo();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  manager->endBlock();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  ::invalidateXsheet();
Shinya Kitaoka 120a6e
  return skelId;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::addSkeleton_undo(int skelId,
Shinya Kitaoka 120a6e
                                   const PlasticSkeletonP &skeleton) {
Shinya Kitaoka 120a6e
  TUndoManager *manager = TUndoManager::manager();
Shinya Kitaoka 120a6e
  manager->beginBlock();
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    l_plasticTool.addSkeleton(skelId, skeleton);
Shinya Kitaoka 120a6e
    assert(l_plasticTool.deformation());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TUndo *addUndo =
Shinya Kitaoka 120a6e
        new AddSkeletonUndo(skelId, new PlasticSkeleton(*skeleton));
Shinya Kitaoka 120a6e
    manager->add(addUndo);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TUndo *setIdUndo = new SetSkeletonIdUndo(skelId);
Shinya Kitaoka 120a6e
    manager->add(setIdUndo);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    setIdUndo->redo();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  manager->endBlock();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  ::invalidateXsheet();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::removeSkeleton_undo(int skelId) {
Shinya Kitaoka 120a6e
  TUndo *op = new RemoveSkeletonUndo(skelId);
Shinya Kitaoka 120a6e
  TUndoManager::manager()->add(op);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  op->redo();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::removeSkeleton_withKeyframes_undo(int skelId) {
Shinya Kitaoka 120a6e
  TUndo *op = new RemoveSkeletonUndo_WithKeyframes(skelId);
Shinya Kitaoka 120a6e
  TUndoManager::manager()->add(op);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  op->redo();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::editSkelId_undo(int skelId) {
Shinya Kitaoka 120a6e
  TUndo *op = new SetSkeletonIdUndo(skelId);
Shinya Kitaoka 120a6e
  TUndoManager::manager()->add(op);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  op->redo();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::draw_build() {
Shinya Kitaoka 120a6e
  double pixelSize = getPixelSize();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Draw original skeleton
Shinya Kitaoka 120a6e
  const PlasticSkeletonP &skeleton = this->skeleton();
Shinya Kitaoka 120a6e
  if (skeleton) {
Shinya Kitaoka 120a6e
    drawOnionSkinSkeletons_build(pixelSize);
Shinya Kitaoka 120a6e
    drawSkeleton(*skeleton, pixelSize);
Shinya Kitaoka 120a6e
    drawSelections(m_sd, *skeleton, pixelSize);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  drawHighlights(m_sd, skeleton.getPointer(), pixelSize);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!skeleton || skeleton->vertices().empty() ||
Shinya Kitaoka 120a6e
      (m_svSel.hasSingleObject() && m_svHigh < 0 && m_seHigh < 0)) {
Shinya Kitaoka 120a6e
    // Draw a handle at current mouse pos (will add a vertex)
Shinya Kitaoka 120a6e
    drawSquare(m_pos, HANDLE_SIZE * pixelSize);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}