Blob Blame Raw
#ifndef PLASTICSKELETONDEFORMATION_H
#define PLASTICSKELETONDEFORMATION_H

#include <memory>

// TnzCore includes
#include "tsmartpointer.h"
#include "tdoubleparam.h"
#include "tdoublekeyframe.h"

// TnzExt includes
#include "ext/plastichandle.h"
#include "ext/plasticskeleton.h"

// tcg includes
#include "tcg/tcg_any_iterator.h"

// Qt includes
#include <QString>

#undef DVAPI
#undef DVVAR
#ifdef TNZEXT_EXPORTS
#define DVAPI DV_EXPORT_API
#define DVVAR DV_EXPORT_VAR
#else
#define DVAPI DV_IMPORT_API
#define DVVAR DV_IMPORT_VAR
#endif

//====================================================

//    Forward declarations

class ParamsObserver;
class ParamChange;

//====================================================

//**************************************************************************************
//    PlasticSkeletonVertexDeformation  declaration
//**************************************************************************************

//! The deformation of a plastic skeleton vertex.
typedef struct DVAPI PlasticSkeletonVertexDeformation : public TPersist {
	PERSIST_DECLARATION(PlasticSkeletonVertexDeformation)

public:
	enum Params {
		ANGLE = 0, //!< Distance from parent vertex (delta)
		DISTANCE,  //!< Angle with parent edge (delta)
		SO,		   //!< Vertex's stacking order
		PARAMS_COUNT
	};

	struct Keyframe {
		TDoubleKeyframe m_keyframes[PARAMS_COUNT];
	};

public:
	TDoubleParamP m_params[PARAMS_COUNT];

public:
	Keyframe getKeyframe(double frame) const;

	void setKeyframe(double frame);
	bool setKeyframe(const Keyframe &values);
	bool setKeyframe(const Keyframe &values, double frame,
					 double easeIn = -1.0, double easeOut = -1.0);

	bool isKeyframe(double frame) const;
	bool isFullKeyframe(double frame) const;
	void deleteKeyframe(double frame);

	void saveData(TOStream &os);
	void loadData(TIStream &is);

} SkVD;

//**************************************************************************************
//    PlasticSkeletonDeformationKeyframe  declaration
//**************************************************************************************

//! The keyframe of a plastic skeleton vertex deformation.
/*!
  \note A deformation keyframe stores vertex deformation keyframes by vertex \a names.
  This is the approach we use to deal with keyframe pasting to different skeletons.
*/
typedef struct PlasticSkeletonDeformationKeyframe {
	std::map<QString, SkVD::Keyframe> m_vertexKeyframes; //!< Keyframes by vertex \a name
	TDoubleKeyframe m_skelIdKeyframe;					 //!< Skeleton id keyframe

} SkDKey;

//**************************************************************************************
//    PlasticSkeletonDeformation  declaration
//**************************************************************************************

/*!
  PlasticSkeletonDeformation models the deformation of a group of PlasticSkeleton instances.

\par Description

  A PlasticSkeleton instance is typically used to act as a deformable object - in other
  words, it defines the 'original' form of a hierarchy of vertices that can be manipulated
  interactively to obtain a 'deformed' configuration.
  \n\n
  PlasticSkeletonDeformation represents a deformation of PlasticSkeleton objects,
  therefore acting primarily as a collection of PlasticSkeletonVertexDeformation
  instances - one per skeleton vertex. The collection is an associative container mapping a
  <I> vertex name <\I> to its deformation; using names as keys is a useful abstraction
  that allows vertex deformations data (eg keyframes) to be copied to skeleton deformations
  whose skeletons have a different internal configuration (ie vertex indices and such).
  \n\n
  Each vertex deformation also stores a unique <I> hook number <\I> that can be used during
  xsheet animation to link stage objects to a skeleton vertex.

\par The Skeletons group

  The PlasticSkeletonDeformation implementation has been extended to work on multiple
  skeletons for one class instance. This class stores a map associating skeleton indices
  to the skeleton instances, that can be used to select a skeleton to be deformed with
  the deformation's data.
  \n\n
  Vertices in different skeleton instances share the same animation if their name is the same.
  \n\n
  This class provides an animatable parameter that is intended to choose the \a active
  skeleton along an xsheet timeline. It is retrievable through the skeletonIdsParam() method.

\par Notable implementation details

  In current implementation, a PlasticSkeletonDeformation keeps shared ownership of the
  skeletons it is attached to. It is therefore intended to be a \a container of said skeletons.
*/
class DVAPI PlasticSkeletonDeformation : public TSmartObject, public TPersist
{
	DECLARE_CLASS_CODE
	PERSIST_DECLARATION(PlasticSkeletonDeformation)

private:
	class Imp;
	std::unique_ptr<Imp> m_imp;

public:
	typedef tcg::any_it<int, int, void *>::bidirectional skelId_iterator;
	typedef tcg::any_it<std::pair<const QString *, SkVD *>,
						std::pair<const QString *, SkVD *>, void *>::bidirectional vd_iterator;
	typedef tcg::any_it<std::pair<int, int>,
						std::pair<int, int>, void *>::bidirectional vx_iterator;

public:
	PlasticSkeletonDeformation();										 //!< Constructs an empty deformation
	PlasticSkeletonDeformation(const PlasticSkeletonDeformation &other); //!< Constructs a deformation \a cloning other's skeletons
	~PlasticSkeletonDeformation();

	PlasticSkeletonDeformation &operator=(const PlasticSkeletonDeformation &other);

	// Skeleton-related methods

	bool empty() const;
	int skeletonsCount() const;

	//! Acquires <I> shared ownership <\I> of the specified skeleton, under given skeletonId
	void attach(int skeletonId, PlasticSkeleton *skeleton);

	//! Releases the skeleton associated to specified skeletonId
	void detach(int skeletonId);

	PlasticSkeletonP skeleton(int skeletonId) const;
	int skeletonId(PlasticSkeleton *skeleton) const;

	//! Returns the ordered range containing the skeleton ids
	void skeletonIds(skelId_iterator &begin, skelId_iterator &end) const;

	TDoubleParamP skeletonIdsParam() const; //!< Returns the skeleton id by frame animatable parameter

	PlasticSkeletonP skeleton(double frame) const; //!< Returns the \a active skeleton by xsheet frame
	int skeletonId(double frame) const;			   //!< Returns the \a active skeleton id by xsheet frame

	// Vertex deformations-related methods

	int vertexDeformationsCount() const;

	SkVD *vertexDeformation(const QString &vertexName) const; //!< Returns the vertex deformation associated to given
															  //!< vertex name. The returned pointer is <I> owned by
															  //!< the deformation - it must \b not be deleted <\I>
	SkVD *vertexDeformation(int skelId, int v) const;

	void vertexDeformations(vd_iterator &begin, vd_iterator &end) const; //!< Returns the ordered range of vertex deformations

	void vdSkeletonVertices(const QString &vertexName,
							vx_iterator &begin, vx_iterator &end) const; //!< Returns the ordered range of skeleton vertices
																		 //!< (at max one per skeleton id) associated to a
																		 //!< vertex name
	// Hook number-related methods

	int hookNumber(const QString &name) const;
	int hookNumber(int skelId, int v) const;

	QString vertexName(int hookNumber) const;
	int vertexIndex(int hookNumber, int skelId) const;

	// Parameters-related methods

	void addObserver(TParamObserver *observer);
	void removeObserver(TParamObserver *observer);

	void setGrammar(TSyntax::Grammar *grammar);

	// Keyframes-related methods

	void getKeyframeAt(double frame, SkDKey &keysMap) const; //!< \note keysMap returned by argument to avoid map
															 //!< copies in case move semantics is not available
	void setKeyframe(double frame);
	bool setKeyframe(const SkDKey &keyframe);
	bool setKeyframe(const SkDKey &keyframe, double frame,
					 double easeIn = -1.0, double easeOut = -1.0);

	bool isKeyframe(double frame) const;
	bool isFullKeyframe(double frame) const;
	void deleteKeyframe(double frame);

	// Interface methods using a deformed copy of the original skeleton (which is owned by this class)

	void storeDeformedSkeleton(int skeletonId, double frame, PlasticSkeleton &skeleton) const;

	void updatePosition(const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton,
						double frame, int v, const TPointD &pos);
	void updateAngle(const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton,
					 double frame, int v, const TPointD &pos);

protected:
	void saveData(TOStream &os);
	void loadData(TIStream &is);

private:
	friend class PlasticSkeleton;

	void addVertex(PlasticSkeleton *sk, int v);	//!< Deals with vertex deformations when v has been added
	void insertVertex(PlasticSkeleton *sk, int v); //!< Deals with vertex deformations when v has been inserted in an edge
	void deleteVertex(PlasticSkeleton *sk, int v); //!< Removes vertex deformation for v, \a before it is deleted
	void vertexNameChange(PlasticSkeleton *sk, int v,
						  const QString &newName); //!< Rebinds a vertex deformation name
	void clear(PlasticSkeleton *sk);			   //!< Clears all vertex deformations

	void loadData_prerelease(TIStream &is); // Toonz 7.0 pre-release loading function. Will be deleted
											// in the next minor release.
};

typedef PlasticSkeletonDeformation SkD;

//===============================================================================

#ifdef WIN32
template class DVAPI TSmartPointerT<PlasticSkeletonDeformation>;
#endif

typedef TSmartPointerT<PlasticSkeletonDeformation> PlasticSkeletonDeformationP;
typedef PlasticSkeletonDeformationP SkDP;

#endif // PLASTICSKELETONDEFORMATION_H