//**************************************************************************/
// Copyright (c) 1998-2006 Autodesk, Inc.
// All rights reserved.
// 
// These coded instructions, statements, and computer programs contain
// unpublished proprietary information written by Autodesk, Inc., and are
// protected by Federal copyright law. They may not be disclosed to third
// parties or copied or duplicated in any form, in whole or in part, without
// the prior written consent of Autodesk, Inc.
//**************************************************************************/
// FILE:        control.h
// DESCRIPTION: Control definitions
// AUTHOR:      Dan Silva and Rolf Berteig
// HISTORY:     created 9 September 1994
//**************************************************************************/

#ifndef __CONTROL__

#define __CONTROL__

#include "maxheap.h"
#include "plugapi.h"
#include "assert1.h"

extern CoreExport void ApplyScaling(Matrix3 &m, const ScaleValue &v);
extern CoreExport void InitControlLists();


class ScaleValue;
class ViewExp;
class INode;
class XFormModes;
class INodeTab;
class View;

CoreExport ScaleValue operator+(const ScaleValue& s0, const ScaleValue& s1);
CoreExport ScaleValue operator-(const ScaleValue& s0, const ScaleValue& s1);
CoreExport ScaleValue operator*(const ScaleValue& s, float f);
CoreExport ScaleValue operator*(float f, const ScaleValue& s);
CoreExport ScaleValue operator+(const ScaleValue& s, float f);
CoreExport ScaleValue operator+(float f, const ScaleValue& s);

class ScaleValue: public MaxHeapOperators {
   public:
   Point3 s;
   Quat q;
   ScaleValue() { /* NO INIT! */ }
   ScaleValue(const Point3& as) { s = as; q = IdentQuat(); }
   ScaleValue(const Point3& as, const Quat& aq) {s = as; q = aq; }
   ScaleValue& operator+=(const ScaleValue& s) 
      { (*this)=(*this)+s; return (*this);}
   ScaleValue& operator*=(const float s) 
      { (*this)=(*this)*s; return (*this);}
   ScaleValue& operator=(const ScaleValue &v) {s=v.s;q=v.q; return (*this);}
   float& operator[](int el) {return s[el];}
   };

// Types of ORTs
#define ORT_BEFORE   1
#define ORT_AFTER 2

// ORTs
#define ORT_CONSTANT       1
#define ORT_CYCLE          2
#define ORT_LOOP           3  // This is cycle with continuity.
#define ORT_OSCILLATE         4
#define ORT_LINEAR            5
#define ORT_IDENTITY       6
#define ORT_RELATIVE_REPEAT      7

//keh set key define 
#define KEY_MODE_NO_BUFFER    1

// This structure is for collecting the return results of
// GetLocalTMComponents().
struct TMComponentsArg: public MaxHeapOperators {
  TMComponentsArg():position(0),rotation(0),scale(0),rotRep(kUnknown) {}
  TMComponentsArg(Point3* pos, Interval* posInv, float* rot, Interval* rotInv,
            ScaleValue* scl, Interval* sclInv)
   : position(pos),posValidity(posInv),rotation(rot),rotValidity(rotInv)
   , scale(scl),sclValidity(sclInv) {}
  enum RotationRep {
   // kXYZ should equals EULERTYPE_XYZ, which is 0.(c.f. euler.h)
   kXYZ,
   kXZY,
   kYZX,
   kYXZ,
   kZXY,
   kZYX,
   kXYX,
   kYZY,
   kZXZ,
   kQuat,
   kUnknown
  };
  Point3*      position;
  Interval*    posValidity;
  // if not null, rotation should be a float[4]
  float*    rotation;
  Interval*    rotValidity;
  RotationRep  rotRep;
  ScaleValue*  scale;
  Interval*    sclValidity;
};

// An object of this class represents a Matrix3. However, its value can be
// obtained only by invoking the operator(). Derived classes may override
// this operator to delay its computation until operator() is called.
//
class Matrix3Indirect: public MaxHeapOperators {
public:
  Matrix3Indirect(){}
  Matrix3Indirect(const Matrix3& m):mat(m){}
  virtual ~Matrix3Indirect(){}
  virtual const Matrix3& operator()() const { return mat; }
  virtual void Set(const Matrix3& m) { mat = m; }
  CoreExport virtual Matrix3Indirect* Clone() const;
  virtual void PreTranslate(const Point3& p){ mat.PreTranslate(p);}
  virtual void PreRotateX(float x){ mat.PreRotateX(x); }
  virtual void PreRotateY(float y){ mat.PreRotateY(y); }
  virtual void PreRotateZ(float z){ mat.PreRotateZ(z); }
  virtual void PreRotate(const Quat& q){PreRotateMatrix(mat,q);}
protected:
  Matrix3   mat;
};

class DelayedMatrix3 : public Matrix3Indirect {
public:
   typedef Matrix3Indirect BaseClass;
   struct DelayedOp : public MaxHeapOperators {
      enum OpCode {
         kPreTrans,
         kPreRotateX,
         kPreRotateY,
         kPreRotateZ,
         kPreRotate
      };
      OpCode code;
      Quat   arg;
	  DelayedOp() {}
      DelayedOp(const Point3& p) : code(kPreTrans), arg(p.x, p.y, p.z, 0.0f) {}
      DelayedOp(const Quat& q) : code(kPreRotate), arg(q) {}
      DelayedOp(float x) : code(kPreRotateX), arg(x, 0.0f, 0.0f, 0.0f) {}
      DelayedOp(int, float y)
         : code(kPreRotateY), arg(0.0f, y, 0.0f, 0.0f) {}
      DelayedOp(int, int, float z)
         : code(kPreRotateZ), arg(0.0f, 0.0f, z, 0.0f) {}
   };
   struct OpQueue : public Tab<DelayedOp> {
      typedef Tab<DelayedOp> BaseClass;
      int head;
      OpQueue() : BaseClass(), head(0) {}
      void Clear() { ZeroCount(); head = 0; }
      int QCount() const { return BaseClass::Count() - head; }
      DelayedOp& Shift() { return BaseClass::operator[](head++); }
      void Push(DelayedOp& op) { Append(1, &op, 4); }
   };

   DelayedMatrix3::DelayedMatrix3()
      : Matrix3Indirect()
      , mMatInitialized(false)
      , mOpQueue()
      {}
   DelayedMatrix3::DelayedMatrix3(const DelayedMatrix3& src)
      : Matrix3Indirect(src.mat)
      , mMatInitialized(src.mMatInitialized) {
      mOpQueue = src.mOpQueue; }

   void EvalMat() {
      if (!mMatInitialized) {
         InitializeMat();
         mMatInitialized = true;
      }
      while (mOpQueue.QCount() > 0) {
         DelayedOp& op = mOpQueue.Shift();
         switch (op.code) {
         case DelayedOp::kPreTrans:
            mat.PreTranslate(op.arg.Vector());
            break;
         case DelayedOp::kPreRotateX:
            mat.PreRotateX(op.arg.x);
            break;
         case DelayedOp::kPreRotateY:
            mat.PreRotateY(op.arg.y);
            break;
         case DelayedOp::kPreRotateZ:
            mat.PreRotateZ(op.arg.z);
            break;
         case DelayedOp::kPreRotate:
            PreRotateMatrix(mat, op.arg);
            break;
         }
      }
      return; }
   void EvalMat() const { const_cast<DelayedMatrix3*>(this)->EvalMat(); }
   size_t PendingOps() const { return mOpQueue.QCount(); }
   virtual void InitializeMat() {
      mat.IdentityMatrix();
      mMatInitialized = true; };

   // Methods of Matrix3Indirect:
   void Set(const Matrix3& m) {
      mat = m;
      mMatInitialized = true;
      mOpQueue.Clear(); }
   CoreExport Matrix3Indirect* Clone() const;
   const Matrix3& operator()() const { EvalMat(); return mat; }
   void PreTranslate(const Point3& p) { DelayedOp op(p); mOpQueue.Push(op); }
   void PreRotateX(float x){ DelayedOp op(x); mOpQueue.Push(op); }
   void PreRotateY(float y){ DelayedOp op(0, y); mOpQueue.Push(op); }
   void PreRotateZ(float z){ DelayedOp op(0, 0, z); mOpQueue.Push(op); }
   void PreRotate(const Quat& q){ DelayedOp op(q); mOpQueue.Push(op); }

private:
   mutable bool mMatInitialized;
   mutable OpQueue mOpQueue;
};

class DelayedNodeMat : public DelayedMatrix3 {
public:
   DelayedNodeMat(INode& n, TimeValue t0)
      : DelayedMatrix3()
      , node(n)
      , t(t0)
      {}
   DelayedNodeMat(const DelayedNodeMat& src)
      : DelayedMatrix3(src)
      , node(src.node)
      , t(src.t)
      {}
   // of Matrix3Indirect:
   void Set(const Matrix3&) {}
   CoreExport Matrix3Indirect* Clone() const;

   // of DelayedMatrix3:
   void InitializeMat() { mat = node.GetNodeTM(t); }
private:
   TimeValue t;
   INode&   node;
};

/*---------------------------------------------------------------------*/

// A list of ease curves.
class EaseCurveList : public ReferenceTarget {
      friend class AddEaseRestore;
      friend class DeleteEaseRestore;

   private:
      Tab<Control*> eases;
      
   public:
      EaseCurveList() {OpenTreeEntry(TRACKVIEW_ANIM, ALL_TRACK_VIEWS);}
      CoreExport ~EaseCurveList();

      CoreExport TimeValue ApplyEase(TimeValue t,Interval &valid);
      CoreExport void AppendEaseCurve(Control *cont);
      CoreExport void DeleteEaseCurve(int i);
      CoreExport void DisableEaseCurve(int i);
      CoreExport void EnableEaseCurve(int i);
      CoreExport BOOL IsEaseEnabled(int i);
      int NumEaseCurves() {return eases.Count();}

      // Animatable
      void GetClassName(MSTR& s) { s= MSTR(_M("EaseCurve")); }  
      Class_ID ClassID() { return Class_ID(EASE_LIST_CLASS_ID,0); }
      SClass_ID SuperClassID() { return EASE_LIST_CLASS_ID; }     
      CoreExport int NumSubs();
      CoreExport Animatable* SubAnim(int i);
      CoreExport MSTR SubAnimName(int i);
      int SubNumToRefNum(int subNum) {return subNum;}
      BOOL BypassTreeView() { return TRUE; }
      CoreExport void DeleteThis();
      ParamDimension* GetParamDimension(int i) {return stdTimeDim;}
      CoreExport BOOL AssignController(Animatable *control,int subAnim);

        using ReferenceTarget::GetInterface;
      CoreExport void* GetInterface(ULONG id);

      CoreExport IOResult Save(ISave *isave);
      CoreExport IOResult Load(ILoad *iload);
      
      // Reference
      CoreExport int NumRefs();
      CoreExport RefTargetHandle GetReference(int i);
      CoreExport void SetReference(int i, RefTargetHandle rtarg);
      CoreExport RefTargetHandle Clone(RemapDir &remap = DefaultRemapDir());
      CoreExport RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, 
            PartID& partID,  RefMessage message);     
   };

class EaseCurveAnimProp : public AnimProperty {
   public:
      EaseCurveList *el;
      EaseCurveAnimProp() { el=NULL; }
      DWORD ID() {return PROPID_EASELIST;}
   };

#define GetEaseListInterface(anim)  ((EaseCurveList*)anim->GetInterface(I_EASELIST))

/*---------------------------------------------------------------------*/
// A list of multiplier curves.
class MultCurveList : public ReferenceTarget {
      friend class AddMultRestore;
      friend class DeleteMultRestore;
   private:
      Tab<Control*> mults;
      
   public:
      MultCurveList() {OpenTreeEntry(TRACKVIEW_ANIM, ALL_TRACK_VIEWS);}
      CoreExport ~MultCurveList();

      CoreExport float GetMultVal(TimeValue t,Interval &valid);
      CoreExport void AppendMultCurve(Control *cont);
      CoreExport void DeleteMultCurve(int i);
      CoreExport void DisableMultCurve(int i);
      CoreExport void EnableMultCurve(int i);
      CoreExport BOOL IsMultEnabled(int i);
      int NumMultCurves() {return mults.Count();}

      // Animatable
      void GetClassName(MSTR& s) { s= MSTR(_M("MultCurve")); }  
      Class_ID ClassID() { return Class_ID(MULT_LIST_CLASS_ID,0); }
      SClass_ID SuperClassID() { return MULT_LIST_CLASS_ID; }     
      CoreExport int NumSubs();
      CoreExport Animatable* SubAnim(int i);
      CoreExport MSTR SubAnimName(int i);
      int SubNumToRefNum(int subNum) {return subNum;}
      BOOL BypassTreeView() { return TRUE; }
      CoreExport void DeleteThis();
      ParamDimension* GetParamDimension(int i) {return stdNormalizedDim;}
      CoreExport BOOL AssignController(Animatable *control,int subAnim);

        using ReferenceTarget::GetInterface;
      CoreExport void* GetInterface(ULONG id);

      CoreExport IOResult Save(ISave *isave);
      CoreExport IOResult Load(ILoad *iload);
      
      // Reference
      CoreExport int NumRefs();
      CoreExport RefTargetHandle GetReference(int i);
      CoreExport void SetReference(int i, RefTargetHandle rtarg);
      CoreExport RefTargetHandle Clone(RemapDir &remap = DefaultRemapDir());
      CoreExport RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, 
            PartID& partID,  RefMessage message);     
   };

class MultCurveAnimProp : public AnimProperty {
   public:
      MultCurveList *ml;
      MultCurveAnimProp() { ml=NULL; }
      DWORD ID() {return PROPID_MULTLIST;}
   };

#define GetMultListInterface(anim)  ((MultCurveList*)anim->GetInterface(I_MULTLIST))

/*---------------------------------------------------------------------*/


//
// For hit testing controller apparatus 
//

class CtrlHitRecord: public MaxHeapOperators {
   friend class CtrlHitLog;
   CtrlHitRecord *next;
   public:
      INode *nodeRef;
      DWORD distance;
      ulong hitInfo;
      DWORD infoExtra;     
      CtrlHitRecord() {next=NULL; distance=0; hitInfo=0; nodeRef=NULL;}
      CtrlHitRecord(CtrlHitRecord *nxt,INode *nr, DWORD d, ulong inf, DWORD extra) {
         next=nxt;nodeRef=nr;distance=d;hitInfo=inf;infoExtra=extra;}
      CtrlHitRecord *Next() {return next;}      
   };                

class CtrlHitLog: public MaxHeapOperators {
   CtrlHitRecord *first;
   int hitIndex;
   bool hitIndexReady;        // CAL-07/10/03: hitIndex is ready to be increased.
   public:
      CtrlHitLog()  { first = NULL; hitIndex = 0; hitIndexReady = false; }
      ~CtrlHitLog() { Clear(); }
      CoreExport void Clear();
      CoreExport void ClearHitIndex(bool ready = false)     { hitIndex = 0; hitIndexReady = ready; }
      CoreExport void IncrHitIndex()      { if (hitIndexReady) hitIndex++; else hitIndexReady = true; }
      CtrlHitRecord* First() { return first; }
      CoreExport CtrlHitRecord* ClosestHit();
      CoreExport void LogHit(INode *nr,DWORD dist,ulong info,DWORD infoExtra); 
   };


// For enumerating IK paramaters
class IKEnumCallback: public MaxHeapOperators {
   public:
      virtual void proc(Control *c, int index)=0;
   };

class IKDeriv: public MaxHeapOperators {
   public:
      virtual int NumEndEffectors()=0;
      virtual Point3 EndEffectorPos(int index)=0;
      virtual void DP(Point3 dp,int index)=0;
      virtual void DR(Point3 dr,int index)=0;
      virtual void NextDOF()=0;
   };

// Flags passed to CompDerivs
#define POSITION_DERIV  (1<<0)
#define ROTATION_DERIV  (1<<1)


// This class is used to store IK parameters that have been
// copied to a clipboard.
class IKClipObject: public MaxHeapOperators {
   public:
      // Identifies the creator of the clip object
      virtual SClass_ID    SuperClassID()=0;
      virtual Class_ID  ClassID()=0;
      
      virtual void DeleteThis()=0;
   };

// Values for 'which' pasted to Copy/PasteIKParams
#define COPYPASTE_IKPOS    1
#define COPYPASTE_IKROT    2

// Passed to InitIKJoints() which is called when importing
// R4 3DS files that have IK joint data.
class InitJointData: public MaxHeapOperators {
   public:
      BOOL active[3];
      BOOL limit[3];
      BOOL ease[3];
      Point3 min, max, damping;
   };

// New for R4: include preferred angle
class InitJointData2 : public InitJointData {
   public:     
      Point3 preferredAngle;
      DWORD flags; // not used (must be 0) - for future expansion
      InitJointData2() {flags=0;}
   };

// The following member been added
// in 3ds max 4.2.  If your plugin utilizes this new
// mechanism, be sure that your clients are aware that they
// must run your plugin with 3ds max version 4.2 or higher.
//
// Extend to include Spring parameters that are missing in InitJointData
// and InitJointData2. It is designed for arguments of type InitJointData2.
// A pointer of InitJointData2 can be tested and downcast to InitJointData3
// via the following two inlines: IsInitJointData3(InitJointData2*) and
// DowncastToJointData3(InitJointData2*).
// 
const DWORD bJointData3 = (1 << 0);
class InitJointData3 : public InitJointData2 {
   public:
   InitJointData3() : InitJointData2() {
   active[0] = active[1] = active[2] = FALSE;
   limit[0] = limit[1] = limit[2] = FALSE;
   ease[0] = ease[1] = ease[2] = FALSE;
   min.Set(0.0f, 0.0f, 0.0f);
   max.Set(0.0f, 0.0f, 0.0f);
   damping.Set(0.0f, 0.0f, 0.0f);
   flags |= bJointData3;
   preferredAngle.Set(0.0f, 0.0f, 0.0f);
   springOn[0] = springOn[1] = springOn[2] = false;
   spring.Set(0.0f, 0.0f, 0.0f);
   // from interpik.h:
#define DEF_SPRINGTENS  (0.02f)
   springTension.Set(DEF_SPRINGTENS, DEF_SPRINGTENS, DEF_SPRINGTENS);
#undef DEF_SPRINGTENS
    }
  bool  springOn[3];
  Point3 spring;
  Point3 springTension;
};

inline bool IsInitJointData3(InitJointData2* jd)
{
  return (jd->flags & bJointData3);
}

inline InitJointData3* DowncastToJointData3(InitJointData2* jd)
{
  return IsInitJointData3(jd) ? (InitJointData3*)jd : NULL;
}
// End of 3ds max 4.2 Extension

// This structure is passed to GetDOFParams().
// Controllers that support IK can provide info about their DOFs
// so that bones can display this information.
// The first 3 DOFs are assumed to be position
// and the next 3 are assumed to be rotation
class DOFParams: public MaxHeapOperators {
   public:
      BOOL display[6];     // Should this DOF be displayed?
      Point3 axis[6];         // DOF axis
      Point3 pos[6];       // Base of axis
      BOOL limit[6];          // is joint limited?
      float min[6];        // min limit
      float max[6];           // max limit
      float curval[6];     // Current value of the parameter
      BOOL sel[6];         // should DOF be highlighted
      BOOL endEffector;    // is there an end effector for this controller
      Matrix3 eeTM;        // world TM of the end effector if present
   };


// These two ways values can be retreived or set.
// For get:
//    RELATIVE = Apply
//    ABSOLUTE = Just get the value
// For set:
//    RELATIVE = Add the value to the existing value (i.e Move/Rotate/Scale)
//    ABSOLUTE = Just set the value
enum GetSetMethod {CTRL_RELATIVE,CTRL_ABSOLUTE};


// Control class provides default implementations for load and save which save the ORT type in these chunks:
#define CONTROLBASE_CHUNK     0x8499
#define INORT_CHUNK           0x3000
#define OUTORT_CHUNK       0x3001
#define CONT_DISABLED_CHUNK      0x3002
#define CONT_FLAGS_CHUNK      0x3003

// Inheritance flags.
#define INHERIT_POS_X   (1<<0)
#define INHERIT_POS_Y   (1<<1)
#define INHERIT_POS_Z   (1<<2)
#define INHERIT_ROT_X   (1<<3)
#define INHERIT_ROT_Y   (1<<4)
#define INHERIT_ROT_Z   (1<<5)
#define INHERIT_SCL_X   (1<<6)
#define INHERIT_SCL_Y   (1<<7)
#define INHERIT_SCL_Z   (1<<8)
#define INHERIT_ALL     511

class Control : public ReferenceTarget {
   public:
      // aszabo|MAr.25.02|Prevents GetInterface(ULONG id) from hiding GetInterface(Interface_ID)
      using ReferenceTarget::GetInterface;

      CoreExport Control();
      virtual ~Control() {};

      virtual void Copy(Control *from)=0;
      virtual void CommitValue(TimeValue t) {}
      virtual void RestoreValue(TimeValue t) {}
      virtual INode* GetTarget() { return NULL; } 
      virtual RefResult SetTarget(INode *targ) {return REF_SUCCEED;}

      // Implemented by transform controllers that have position controller
      // that can be edited in the trajectory branch
      virtual Control *GetPositionController() {return NULL;}
      virtual Control *GetRotationController() {return NULL;}
      virtual Control *GetScaleController() {return NULL;}
      virtual BOOL SetPositionController(Control *c) {return FALSE;}
      virtual BOOL SetRotationController(Control *c) {return FALSE;}
      virtual BOOL SetScaleController(Control *c) {return FALSE;}

      // If a controller has an 'X', 'Y', 'Z', or 'W' controller, it can implement
      // these methods so that its sub controllers can respect track view filters
      virtual Control *GetXController() {return NULL;}
      virtual Control *GetYController() {return NULL;}
      virtual Control *GetZController() {return NULL;}
      virtual Control *GetWController() {return NULL;}

      // Implemented by look at controllers that have a float valued roll
      // controller so that the roll can be edited via the transform type-in
      virtual Control *GetRollController() {return NULL;}
      virtual BOOL SetRollController(Control *c) {return FALSE;}

      // Implemented by any Point3/Point4 controller that wishes to indicate that it is intended
      // to control floating point RGB color values
      virtual BOOL IsColorController() {return FALSE;}

      // Implemented by TM controllers that support 
      // filtering out inheritance
      virtual DWORD GetInheritanceFlags() {return INHERIT_ALL;}
      virtual BOOL SetInheritanceFlags(DWORD f,BOOL keepPos) {return FALSE;} // return TRUE if TM controller supports inheritance

      virtual BOOL IsLeaf() {return TRUE;}
      virtual int IsKeyable() {return 1;}

      // If a controller does not want to allow another controller
      // to be assigned on top of it, it can return FALSE to this method.
      virtual BOOL IsReplaceable() {return TRUE;}     

      // This is called on TM, pos, rot, and scale controllers when their
      // input matrix is about to change. If they return FALSE, the node will
      // call SetValue() to make the necessary adjustments.
      virtual BOOL ChangeParents(TimeValue t,const Matrix3& oldP,const Matrix3& newP,const Matrix3& tm) {return FALSE;}

      // val points to an instance of a data type that corresponds with the controller
      // type. float for float controllers, etc.
      // Note that for SetValue on Rotation controllers, if the SetValue is
      // relative, val points to an AngAxis while if it is absolute it points
      // to a Quat.
      virtual void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE)=0;  
      virtual  void SetValue(TimeValue t, void *val, int commit=1, GetSetMethod method=CTRL_ABSOLUTE)=0;

      // Definition: LocalMatrix = WorldMatrix * ParentWorldMatrix^(-1)
      // This method returns the PRS components of the local
      // matrix. In general, controller cannot decide on the local matrix
      // without knowing the parent matrix. However, many
      // controllers, such as default controllers, are well defined
      // without the parent matrix. In these cases, it is more
      // efficient compute the local components directly without
      // going through the world matrix. Therefore, the argument
      // parentMatrix is a reference to Matrix3Indirect. This would
      // allow clients to supply a "delayed parent matrix," which
      // will be computed only if it is necessary. It returns true
      // for Matrix3, Position, Rotation, or Scale controllers, and
      // return false otherwise.
      // The PRS components will be put in argument cmpts in the
      // respective fields with corresponding validity
      // intervals. NULL pointer, of TMComponentsArg::position for
      // example, indicates that the client is not concerned about
      // the component. When it is not NULL, the corresponding
      // pointer to the validity interval MUST NOT be NULL. When it
      // is not NULL, TMComponentsArg::rotation is a float[4].
      // rotRep tells what the numbers mean.
      // Position, Rotation, or Scale, controllers will put results
      // at the respective component when the corresponding pointer
      // is not NULL.
      // Upon entry, parentMatrix should represent the parent matrix up
      // to the first requested components. For Matrix3 controllers, for
      // example, if cmpts.position==NULL && cmpts.rotation!=NULL, then
      // parentMatrix should be matrix that includes the parent node matrix
      // plus the position of this node. Upon return, this matrix may be
      // modified.
      CoreExport virtual bool GetLocalTMComponents(TimeValue t, TMComponentsArg& cmpts, Matrix3Indirect& parentMatrix);

      // Transform controllers that do not inherit their parent's  transform 
      // should override this method. Returning FALSE will cause SetValue 
      // to be called even in the case when the parent is also being transformed.
      virtual BOOL InheritsParentTransform() { return TRUE; }

      virtual int GetORT(int type) {return (aflag>>(type==ORT_BEFORE?A_ORT_BEFORESHIFT:A_ORT_AFTERSHIFT))&A_ORT_MASK;}
      CoreExport virtual void SetORT(int ort,int type);
      
      // Sets the enabled/disabled state for ORTs
      CoreExport virtual void EnableORTs(BOOL enable);

      // Default implementations of load and save handle loading and saving of out of range type.
      // Call these from derived class load and save.
      // NOTE: Must call these before any of the derived class chunks are loaded or saved.
      CoreExport IOResult Save(ISave *isave);
      CoreExport IOResult Load(ILoad *iload);

      // For IK
      // Note: IK params must be given in the order they are applied to
      // the parent matrix. When derivatives are computed for a parameter
      // that parameter will apply itself to the parent matrix so the next
      // parameter has the appropriate reference frame. If a controller isn't
      // participating in IK then it should return FALSE and the client (usually PRS)
      // will apply the controller's value to the parent TM.
      virtual void EnumIKParams(IKEnumCallback &callback) {}
      virtual BOOL CompDeriv(TimeValue t,Matrix3& ptm,IKDeriv& derivs,DWORD flags) {return FALSE;}
      virtual float IncIKParam(TimeValue t,int index,float delta) {return 0.0f;}
      virtual void ClearIKParam(Interval iv,int index) {return;}
      virtual BOOL CanCopyIKParams(int which) {return FALSE;}
      virtual IKClipObject *CopyIKParams(int which) {return NULL;}
      virtual BOOL CanPasteIKParams(IKClipObject *co,int which) {return FALSE;}
      virtual void PasteIKParams(IKClipObject *co,int which) {}
      virtual void InitIKJoints(InitJointData *posData,InitJointData *rotData) {}
      virtual BOOL GetIKJoints(InitJointData *posData,InitJointData *rotData) {return FALSE;}
      virtual BOOL GetDOFParams(TimeValue t,Matrix3 &ptm,DOFParams &dofs,BOOL nodeSel) {return FALSE;}
      virtual BOOL CreateLockKey(TimeValue t, int which) {return FALSE;}
      virtual void MirrorIKConstraints(int axis,int which,BOOL pasteMirror=FALSE) {}
      virtual BOOL TerminateIK() {return FALSE;} // controllers can act as terminators.

      // New for R4
      virtual void InitIKJoints2(InitJointData2 *posData,InitJointData2 *rotData) {}
      virtual BOOL GetIKJoints2(InitJointData2 *posData,InitJointData2 *rotData) {return FALSE;}

      // Called on a transform controller when the a message is received from a pin node
      virtual RefResult PinNodeChanged(RefMessage message,Interval changeInt, PartID &partID) {return REF_SUCCEED;}

      // Called on a transform controller when one of the node level IK parameters has been changed
      virtual void NodeIKParamsChanged() {}

      // Called in a transform controller when a node invalidates its TM cache
      virtual void TMInvalidated() {}

      // Let's the TM controller determine if it's OK to bind (IK bind) to a particular node.
      virtual BOOL OKToBindToNode(INode *node) {return TRUE;}

      // Ease curves
      virtual BOOL CanApplyEaseMultCurves() {return TRUE;}
      CoreExport TimeValue ApplyEase(TimeValue t,Interval &valid);
      CoreExport void AppendEaseCurve(Control *cont);
      CoreExport void DeleteEaseCurve(int i);
      CoreExport int NumEaseCurves();

      // Multiplier curves    
      CoreExport float GetMultVal(TimeValue t,Interval &valid);
      CoreExport void AppendMultCurve(Control *cont);
      CoreExport void DeleteMultCurve(int i);
      CoreExport int NumMultCurves();

      // These are implemented to handle ease curves. If a controller
      // is a leaf controller, then it MUST NOT BY DEFINITION have any
      // sub controllers or references. If it is a leaf controller, then
      // these are implemented to handle the ease curve list.
      // If it is NOT a leaf controller, then these can be overridden.
      CoreExport int NumRefs();
      CoreExport RefTargetHandle GetReference(int i);
      CoreExport void SetReference(int i, RefTargetHandle rtarg);
      CoreExport int NumSubs();
      CoreExport Animatable* SubAnim(int i);
      CoreExport MSTR SubAnimName(int i);

      // Default implementations of some Animatable methods
      CoreExport void* GetInterface(ULONG id);
      CoreExport int PaintFCurves(        
         ParamDimensionBase *dim,
         HDC hdc,
         Rect& rcGraph,
         Rect& rcPaint,
         float tzoom,
         int tscroll,
         float vzoom,
         int vscroll,
         DWORD flags );
      CoreExport int GetFCurveExtents(
         ParamDimensionBase *dim,
         float &min, float &max, DWORD flags);
      CoreExport virtual void BaseClone(ReferenceTarget *from, ReferenceTarget *to, RemapDir &remap);


      // This is called on transform controller after a node is
      // cloned and the clone process has finished
      virtual void PostCloneNode() {}

      // Slave TM controllers can implement this to prevent plug-ins
      // deleting their node via the DeleteNode API.
      virtual BOOL PreventNodeDeletion() {return FALSE;}

      // New interface for visibility float controllers to allow view dependent visibility
      // The default implementation will call GetValue()
      CoreExport virtual float EvalVisibility(TimeValue t,View &view,Box3 pbox,Interval &valid);
      
      // Called on visibility controllers. Gives them the option to completely hide an object in the viewports
      virtual BOOL VisibleInViewports() {return TRUE;}

      // Called on transform controllers or visibility controllers when a node is cloned and the user has chosen to instance
      virtual BOOL CanInstanceController() {return TRUE;}

      // Should be called by any leaf controller's clone method so
      // that ease and multipier curves are cloned.
      CoreExport void CloneControl(Control *ctrl,RemapDir &remap);

      //-------------------------------------------------------
      // Controllers that wish to have an apparatus available in
      // the scene will implement these methods:
      // NOTE: Most of these methods are duplicated in BaseObject or Object
      // (see object.h for descriptions).
      virtual int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags) { return 0; };
      virtual int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) { return 0; }
      virtual  void GetWorldBoundBox(TimeValue t,INode* inode, ViewExp *vpt, Box3& box) {}

      virtual void ActivateSubobjSel(int level, XFormModes& modes ) {}

      virtual void SelectSubComponent(CtrlHitRecord *hitRec, BOOL selected, BOOL all, BOOL invert=FALSE) {}
      virtual void ClearSelection(int selLevel) {}
      virtual int SubObjectIndex(CtrlHitRecord *hitRec) {return 0;}     
      virtual void SelectAll(int selLevel) {}
      virtual void InvertSelection(int selLevel) {}

      virtual void GetSubObjectCenters(SubObjAxisCallback *cb,TimeValue t,INode *node) {}
      virtual void GetSubObjectTMs(SubObjAxisCallback *cb,TimeValue t,INode *node) {}

      // Modify sub object apparatuses
      virtual void SubMove( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE ){}
      virtual void SubRotate( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Quat& val, BOOL localOrigin=FALSE ){}
      virtual void SubScale( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE ){}      
      
      // Schematic View Animatable Overides...
      CoreExport virtual SvGraphNodeReference SvTraverseAnimGraph(IGraphObjectManager *gom, Animatable *object, int id, DWORD flags);
      CoreExport virtual MSTR SvGetName(IGraphObjectManager *gom, IGraphNode *gNode, bool isBeingEdited);
      CoreExport virtual bool SvHandleDoubleClick(IGraphObjectManager *gom, IGraphNode *gNode);
      CoreExport virtual bool SvCanInitiateLink(IGraphObjectManager *gom, IGraphNode *gNode);
      CoreExport virtual bool SvCanConcludeLink(IGraphObjectManager *gom, IGraphNode *gNode, IGraphNode *gNodeChild);
      CoreExport virtual bool SvLinkChild(IGraphObjectManager *gom, IGraphNode *gNodeThis, IGraphNode *gNodeChild);
      CoreExport virtual bool SvEditProperties(IGraphObjectManager *gom, IGraphNode *gNode);

      // Called when the user rescales time in the time configuration dialog. If FALSE
      // is returned from this method then MapKeys() will be used to perform the scaling. 
      // Controllers can override this method to handle things like rescaling tagents that 
      // MapKeys() won't affect and return TRUE if they don't want map keys to be called.
      virtual BOOL RescaleTime(Interval oseg, Interval nseg) {return FALSE;}

//watje these are to allow a control to get sampled at a different rate than
//what trackview does by default so the controller can speed up redraws
//this is the pixel sample rate for when the curve is drawn
      virtual int GetDrawPixelStep() {return 5;}
//this is the ticks sample rate for when the curve is checked for its y extents
      virtual int GetExtentTimeStep() {return 40;}
   };

// Any controller that does not evaluate itself as a function of it's
// input can subclass off this class.
// GetValueLocalTime() will never ask the controller to apply the value,
// it will always ask for it absolute.
class StdControl : public Control {
   public:     
      virtual void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE)=0;
      virtual  void SetValueLocalTime(TimeValue t, void *val, int commit=1, GetSetMethod method=CTRL_ABSOLUTE)=0;
      CoreExport void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
      CoreExport void SetValue(TimeValue t, void *val, int commit=1, GetSetMethod method=CTRL_ABSOLUTE);
      // Computes the local components without calling parentMatrix
      // for position, rotation, and Scale controllers.
      CoreExport bool GetLocalTMComponents(TimeValue t, TMComponentsArg& cmpts, Matrix3Indirect& parentMatrix);

      virtual void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type)=0;
      
      virtual void *CreateTempValue()=0;
      virtual void DeleteTempValue(void *val)=0;
      virtual void ApplyValue(void *val, void *delta)=0;
      virtual void MultiplyValue(void *val, float m)=0;
   };

// Each super class of controller may have a specific packet defined that
// the 'val' pointer will point to instead of a literal value.
// In reality, probably only the Transform controller will do this.
enum SetXFormCommand { XFORM_MOVE, XFORM_ROTATE, XFORM_SCALE, XFORM_SET };
class SetXFormPacket: public MaxHeapOperators {
   public:
      SetXFormCommand command;
      Matrix3 tmParent;
      Matrix3 tmAxis;      // if command is XFORM_SET, this will contain the new value for the XFORM.    
      Point3 p;
      Quat q;
      AngAxis aa;
      BOOL localOrigin;    
      
      // XFORM_SET
      SetXFormPacket(const Matrix3& mat,const Matrix3& par=Matrix3(1))
         {command=XFORM_SET,tmParent=par,tmAxis=mat;}

      // XFORM_MOVE
      SetXFormPacket(Point3 pt, const Matrix3& par=Matrix3(1), 
                  const Matrix3& a=Matrix3(1))
         {command=XFORM_MOVE;tmParent=par;tmAxis=a;p=pt;localOrigin=FALSE;}

      // XFORM_ROTATE
      SetXFormPacket(Quat qt, BOOL l, const Matrix3& par=Matrix3(1),
                  const Matrix3& a=Matrix3(1))
         {command=XFORM_ROTATE;tmParent=par;tmAxis=a;q=qt;aa=AngAxis(q);localOrigin=l;}
      SetXFormPacket(AngAxis aA, BOOL l, const Matrix3& par=Matrix3(1),
                  const Matrix3& a=Matrix3(1))
         {command=XFORM_ROTATE;tmParent=par;tmAxis=a;q=Quat(aA);aa=aA;localOrigin=l;}

      // XFORM_SCALE
      SetXFormPacket(Point3 pt, BOOL l, const Matrix3& par=Matrix3(1),
                  const Matrix3& a=Matrix3(1))
         {command=XFORM_SCALE;tmParent=par;tmAxis=a;p=pt;localOrigin=l;}

      // Just in case you want to do it by hand...
      SetXFormPacket() {};
   };



// This is a special control base class for controllers that control
// morphing of geomoetry.
//
// The 'val' pointer used with GetValue will point to an object state.
// This would be the result of evaluating a combination of targets and
// producing a new object that is some combination of the targets.
//
// The 'val' pointer used with SetValue will point to a 
// SetMorphTargetPacket data structure. This has a pointer to
// an object (entire pipeline) and the name of the target.

// A pointer to one of these is passed to SetValue
class SetMorphTargetPacket: public MaxHeapOperators {
   public:
      Matrix3 tm;
      Object *obj;
      MSTR name;
      BOOL forceCreate; // Make sure the key is created even if it is at frame 0
      SetMorphTargetPacket(Object *o,MSTR n,Matrix3 &m,BOOL fc=FALSE) {obj = o;name = n;tm = m;forceCreate=fc;}
      SetMorphTargetPacket(Object *o,MSTR n,BOOL fc=FALSE) {obj = o;name = n;tm = Matrix3(1);forceCreate=fc;}
   };

class MorphControl : public Control {
   public:
      
      // Access the object pipelines of the controller's targets. Note
      // that these are pointers to the pipelines, not the result of
      // evaluating the pipelines.
      virtual int NumMorphTargs() {return 0;}
      virtual Object *GetMorphTarg(int i) {return NULL;}
      virtual void DeleteMorphTarg(int i) {}
      virtual void GetMorphTargName(int i,MSTR &name) {name.printf(_M("Target #%d"),i);}
      virtual void SetMorphTargName(int i,MSTR name) {}
      virtual Matrix3 GetMorphTargTM(int i) {return Matrix3(1);}

      // Checks an object to see if it is an acceptable target.
      virtual BOOL ValidTarget(TimeValue t,Object *obj) {return FALSE;}

      // When a REFMSG_SELECT_BRANCH message is received the morph controller should
      // mark the target indicated and be prepared to return its ID from this method.
      virtual int GetFlaggedTarget() {return -1;}

      // Should call these methods on targets
      virtual BOOL HasUVW() { return 1; }
      virtual void SetGenUVW(BOOL sw) {  }
   };

//-------------------------------------------------------------
// Control base class for Master Controllers
//

class MasterPointControl : public Control {
   public:
      // Set the number of sub-controllers
      virtual  void SetNumSubControllers(int num, BOOL keep=FALSE) {}
      // Return the number of sub-controllers
      virtual  int    GetNumSubControllers() { return 0; }
      // Delete all the sub-controllers that are set to TRUE in the BitArray
      virtual void DeleteControlSet (BitArray set) {}
      // Add a new sub-controller
      virtual int  AddSubController(Control* ctrl) { return 0; }
      // Return i'th of sub-controller
      virtual Control* GetSubController(int i) { return NULL; }
      // Set the i'th sub-controller
      virtual  void SetSubController(int i, Control* ctrl) {}
};


//----------------------------------------------------------------//
//
// Some stuff to help with ORTs - these could actually be Interval methods

inline TimeValue CycleTime(Interval i,TimeValue t)
   {
   int res, dur = i.Duration()-1;
   if (dur<=0) return t;      
   res   = (t-i.Start())%dur;
   if (t<i.Start()) {
      return i.End()+res;
   } else {
      return i.Start()+res;
      }
   }

inline int NumCycles(Interval i,TimeValue t)
   {
   int dur = i.Duration()-1;
   if (dur<=0) return 1;
   if (t<i.Start()) {
      return (abs(t-i.Start())/dur)+1;
   } else 
   if (t>i.End()) {
      return (abs(t-i.End())/dur)+1;
   } else {
      return 0;
      }
   }



// Types that use this template must support:
//  T + T, T - T, T * float, T + float 

template <class T> T 
LinearExtrapolate(TimeValue t0, TimeValue t1, T &val0, T &val1, T &endVal)
   {
   return (T)(endVal + (val1-val0) * float(t1-t0));
   }

template <class T> T 
RepeatExtrapolate(Interval range, TimeValue t, 
      T &startVal, T &endVal, T &cycleVal)
   {
   int cycles = NumCycles(range,t);
   T delta;
   if (t<range.Start()) {
      delta = startVal - endVal;
   } else {
      delta = endVal - startVal;
      }
   return (T)(cycleVal + delta * float(cycles));
   }

template <class T> T 
IdentityExtrapolate(TimeValue endPoint, TimeValue t, T &endVal )
   {
   return (T)(endVal + float(t-endPoint));
   }

CoreExport Quat LinearExtrapolate(TimeValue t0, TimeValue t1, Quat &val0, Quat &val1, Quat &endVal);
CoreExport Quat RepeatExtrapolate(Interval range, TimeValue t, 
               Quat &startVal, Quat &endVal, Quat &cycleVal);
CoreExport Quat IdentityExtrapolate(TimeValue endPoint, TimeValue t, Quat &endVal );

CoreExport ScaleValue LinearExtrapolate(TimeValue t0, TimeValue t1, ScaleValue &val0, ScaleValue &val1, ScaleValue &endVal);
CoreExport ScaleValue RepeatExtrapolate(Interval range, TimeValue t, ScaleValue &startVal, ScaleValue &endVal, ScaleValue &cycleVal);
CoreExport ScaleValue IdentityExtrapolate(TimeValue endPoint, TimeValue t, ScaleValue &endVal);


template <class T> T
LinearInterpolate(const T &v0,const T &v1,float u)
   {
   return (T)((1.0f-u)*v0 + u*v1);
   }

inline Quat 
LinearInterpolate(const Quat &v0,const Quat &v1,float u)
   {
   return Slerp(v0,v1,u);
   }

inline ScaleValue 
LinearInterpolate(const ScaleValue &v0,const ScaleValue &v1,float u)
   {
   ScaleValue res;
   res.s = ((float)1.0-u)*v0.s + u*v1.s;
   res.q = Slerp(v0.q,v1.q,u);
   return res;
   }


inline Interval TestInterval(Interval iv, DWORD flags)
   {
   TimeValue start = iv.Start();
   TimeValue end = iv.End();
   if (!(flags&TIME_INCLEFT)) {
      start++;
      }  
   if (!(flags&TIME_INCRIGHT)) {
      end--;
      }
   if (end<start) {
      iv.SetEmpty();
   } else {
      iv.Set(start,end);
      }
   return iv;  
   }

inline Quat ScaleQuat(Quat q, float s)
   {
   float angle;
   Point3 axis;
   AngAxisFromQ(q,&angle,axis);
   return QFromAngAxis(angle*s,axis);
   }

//-------------------------------------------------------------------
// A place to store values during Hold/Restore periods
//
//********************************************************
// TempStore:  This is a temporary implementation:
//  It uses a linear search-
//  A hash-coded dictionary would be faster.
//  (if there are ever a lot of entries)
//********************************************************

struct Slot: public MaxHeapOperators {
   void *key;
   void *pdata;
   int nbytes; 
   Slot *next;
   public:
      Slot() { pdata = NULL; }
      CoreExport ~Slot();

   };

class TempStore: public MaxHeapOperators {
   Slot *slotList;            
   Slot* Find(int n, void *data, void *ptr);
   public:
      TempStore() {  slotList = NULL;  }
      ~TempStore() { ClearAll(); }
      CoreExport void ClearAll();   // empty out the store 
      CoreExport void PutBytes(int n, void *data, void *ptr);
      CoreExport void GetBytes(int n, void *data, void *ptr);
      CoreExport void Clear(void *ptr);  // Remove single entry
      void PutFloat(float  f, void *ptr) {
          PutBytes(sizeof(float),(void *)&f,ptr);
          }
      CoreExport void PutInt(int i, void *ptr) {
          PutBytes(sizeof(int),(void *)&i,ptr);
          }
      CoreExport void GetFloat(float *f, void *ptr) { 
         GetBytes(sizeof(float),(void *)f,ptr);
         }
      CoreExport void GetInt(int *i, void *ptr) { 
         GetBytes(sizeof(int),(void *)i,ptr);
         }
      CoreExport void PutPoint3(Point3  f, void *ptr) {
          PutBytes(sizeof(Point3),(void *)&f,ptr);
          }
      CoreExport void GetPoint3(Point3 *f, void *ptr) { 
         GetBytes(sizeof(Point3),(void *)f,ptr);
         }
      CoreExport void PutPoint4(Point4  f, void *ptr) {
         PutBytes(sizeof(Point4),(void *)&f,ptr);
         }
      CoreExport void GetPoint4(Point4 *f, void *ptr) { 
         GetBytes(sizeof(Point4),(void *)f,ptr);
         }
      CoreExport void PutQuat( Quat  f, void *ptr) {
          PutBytes(sizeof(Quat),(void *)&f,ptr);
          }
      CoreExport void GetQuat( Quat *f, void *ptr) { 
         GetBytes(sizeof(Quat),(void *)f,ptr);
         }
      CoreExport void PutScaleValue( ScaleValue  f, void *ptr) {
          PutBytes(sizeof(ScaleValue),(void *)&f,ptr);
          }
      CoreExport void GetScaleValue( ScaleValue *f, void *ptr) { 
         GetBytes(sizeof(ScaleValue),(void *)f,ptr);
         }
   };


extern CoreExport TempStore tmpStore;   // this should be in the scene data struct.


CoreExport int Animating();    // is the animate switch on??
CoreExport void AnimateOn();  // turn animate on
CoreExport void AnimateOff();  // turn animate off
CoreExport void SuspendAnimate(); // suspend animation (uses stack)
CoreExport void ResumeAnimate();   // resume animation ( " )

CoreExport BOOL AreWeAnimating(const TimeValue &t);
CoreExport BOOL AreWeKeying(const TimeValue &t);


CoreExport TimeValue GetAnimStart();
CoreExport TimeValue GetAnimEnd();
CoreExport void SetAnimStart(TimeValue s);
CoreExport void SetAnimEnd(TimeValue e);

CoreExport Control *NewDefaultFloatController();
CoreExport Control *NewDefaultPoint3Controller();
CoreExport Control *NewDefaultMatrix3Controller();
CoreExport Control *NewDefaultPositionController();
CoreExport Control *NewDefaultRotationController();
CoreExport Control *NewDefaultScaleController();
CoreExport Control *NewDefaultBoolController();
CoreExport Control *NewDefaultColorController();
CoreExport Control *NewDefaultMasterPointController();
CoreExport Control *NewDefaultPoint4Controller();
CoreExport Control *NewDefaultFRGBAController();

CoreExport Control* CreateInterpFloat();
CoreExport Control* CreateInterpPosition();
CoreExport Control* CreateInterpPoint3();
CoreExport Control* CreateInterpRotation();
CoreExport Control* CreateInterpScale();
CoreExport Control* CreatePRSControl();
CoreExport Control* CreateLookatControl();
CoreExport Control* CreateMasterPointControl();
CoreExport Control* CreateInterpPoint4();

CoreExport void SetDefaultController(SClass_ID sid, ClassDesc *desc);
CoreExport ClassDesc *GetDefaultController(SClass_ID sid);

CoreExport void SetDefaultColorController(ClassDesc *desc);
CoreExport void SetDefaultFRGBAController(ClassDesc *desc);
CoreExport void SetDefaultBoolController(ClassDesc *desc);

CoreExport BOOL GetSetKeyMode();
CoreExport void SetSetKeyMode(BOOL onOff);

CoreExport void SuspendSetKeyMode();
CoreExport void ResumeSetKeyMode();
CoreExport BOOL GetSetKeySuspended();
CoreExport BOOL GetSetKeyModeStatus();

CoreExport BOOL IsSetKeyModeFeatureEnabled();

#endif //__CONTROL__
