/*****************************************************************************
 * DynaMechs: A Multibody Dynamic Simulation Library
 *
 * Copyright (C) 1994-2001  Scott McMillan   All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************
 *     File: dmLink.hpp
 *   Author: Scott McMillan
 *  Summary: Class definitions for dmLink.
 *****************************************************************************/

#ifndef _DM_LINK_HPP
#define _DM_LINK_HPP

#include <dm.h>
#include <dmObject.hpp>

//============================================================================
/**

The {\tt dmLink} class is an abstract base class for all derived link classes.
Its main purpose is to support polymorphism across all link classes by
providing the virtual interfaces for all the functions that the {\tt
dmArticulation} class calls in the course of simulating the system.  As such,
nearly all functions specified in the public section are pure virtual functions
that must be defined in the derived link classes (currently, {\tt dmMDHLink},
{\tt dmSphericalLink}, {\tt dmQuaternionLink}, {\tt dmMobileBaseLink}, and {\tt
dmZScrewTxLink}).

The link's state variables (generalized position and velocity variables) can be
set and retrieved using the {\tt setState} and {\tt getState} functions.  The
{\tt q} and {\tt qd} arguments correspond to position and velocity,
respectively.  They are arrays containing $n$ elements where $n$ is the number
of degrees of freedom for this particular type of link (and determined by the
derived classes).  This number is returned when the {\tt getNumDOFs} function
is called.  The joint input vector (containing motor inputs where applicable,
but usually generalized joint forces) can be set by calling {\tt
setJointInput}.  The parameter is also an array of {\tt getNumDOFs} elements
containing the desired values.

A coulomb friction parameter can also be specified and retrieved using the {\tt
setJointFriction} and {\tt getJointFriction} functions.  This scalar parameters
multiplies the joint velocity to produce a force/torque opposing the direction
of motion as follows:
\begin{eqnarray*}
\tau \: = \: \mu_c \: \dot{q}.
\end{eqnarray*}

For dynamics simulation of the link using the AB algorithm, a number of
transformation functions are required of all link types.  Their functions are
listed as follows:
\begin{center}
\begin{tabular}{lcl}
{\tt rtxToInboard}  &  & $^{i-1}f_i = {^iR}^T_{i-1} \: f_i$ \\
{\tt rtxFromInboard}     && $^{i}\omega_{i-1} = {^iR}_{i-1} \: \omega_{i-1}$ \\
{\tt stxToInboard}       && $^{i-1}{\bf f}_i = {^iX}^T_{i-1} \: {\bf f}_i$ \\
{\tt stxFromInboard}     && $^i{\bf v}_{i-1} = {^iX}_{i-1} \: {\bf v}_{i-1}$ \\
{\tt rcongtxToInboardSym}&& $^{i-1}\bar{I}_i =
                      {^iR}^T_{i-1} \: \bar{I}_i \: {^iR}_{i-1}$ \\
{\tt rcongtxToInboardGen}&& $^{i-1}G_i =
                      {^iR}^T_{i-1} \: G_i \: {^iR}_{i-1}$ \\
{\tt scongtxToInboardIrefl} && $^{i-1}{\bf N}_i =
                      {^i{X}}^T_{i-1} \: {\bf N}_i \: {^i{X}}_{i-1}$ \\
\end{tabular}
\end{center}
The first two rotate Cartesian vectors between this link's and the inboard
(parent) link's coordinate systems, while the second two transform (rotate and
translate) spatial vectors.  The last three functions perform congruence
transformations on matrices to the inboard coordinate system.  {\tt
rcongtxToInboardSym} affects a rotational congruence transform on sysmmetric
$3\times 3$ matrices while its counterpart, {\tt rcongtxToInboardGen},
transforms general $3\times 3$ matrices.  The last function, {\tt
scongtxToInboardIrefl}, is a specialized function for the AB algorithm which
efficiently transforms $6\times 6$ reflected articulated-body inertia matrices
taking into account special structure of the matrix based on its joint axes.
This one function is the centerpiece of the efficiency of this implementation
of the AB algorithm.

The next four functions are used to perform the three recursions of the AB
simulation algorithm.  The first forward kinematics recursion is implemented
for each link with the {\tt ABForwardKinematics} function.  This function
should only be called by {\tt dmArticulation::ABForwardKinematics} {\em yeah,
this should be private and {\tt dmArticulation} should be made a friend}.
Again, the {\tt q} and {\tt qd} parameters are the joint position and velocity
vectors computed by the current interation of the numerical integration
algorithm and are used to set the new state of the link.  The {\tt
link\_val\_inboard} is the {\tt ABForKinStruct} (see {\tt dmArticulation}) that
has been filled with the values for the inboard link's state dependent
quantities.  Using these values, the {\tt link\_val\_curr} struct elements are
computed for this link.

The second backward recursion of the AB algorithm is implemented with two
different functions for reasons of efficiency.  If this link has no children,
it is a leaf node in the tree structure, and therefore, the {\tt
dmArticulation::ABBackwardDynamics} function will call the {\tt
ABBackwardDynamicsN} function which will begin the recursion which requires
less computation, by computing {\tt f\_star\_inboard} and {\tt
N\_refl\_inboard}, the AB bias force and inertia that are reflected across this
link's joint and transformed to the inboard link's coordinate system.  The
function {\tt ABBackwardDynamics} performs a full recursive step for a link
with children.  It takes the accumulated results of the child links in {\tt
f\_star\_curr} and {\tt N\_refl\_curr} and computes the same quantities as
before.  The {\tt ABForKinStruct} parameter is the {\tt link\_val\_curr}
computed in the first pass, and is used to call {\tt dmForce::computeForce()}
functions if they have been added to the associated dmRigidBody.

The third forward recursion of the AB algorithm is implemented in the {\tt
ABForwardAcceleration} function.  Its input parameter, {\tt a\_inboard} is the
spatial acceleration of the inboard link.  This function computes and outputs
the spatial acceleration of this link in {\tt a\_curr} and fills in the
derivative of state variables ({\tt qd} and {\tt qdd}).  Note that the velocity
vector, {\tt qd}, is not necessarily the same as that used in {\tt
ABForwardKinematics}.  Rather, it contains the time derivatives of {\tt q}.
See {\tt dmSphericalLink} for an example where they are not the same.

Another set of four functions are required for simulating systems with
kinematic loops.  Three of these function are used in the backward dynamics
recursion of the AB algorithm.  The {\tt XikToInboard} function is used when
eliminating a link from the force-balance equation of its predecessor.  This
function computes the update to the coefficient of the loop-closure constraint
forces of a loop in the predecessor link's force-balance equation (as a result
of the elimination).  The {\tt BtoInboard} function computes the new
coefficient of loop n's constraint forces in the loop-closure constraint
equation for loop k after elimination of the current link in favor of its
predecessor.  The {\tt xformZetak} function computes the new bias term
($\zeta_k$) in loop k's loop-closure constraint equation after elimination of
the link.  Finally, the last function, {\tt ABForwardAccelerations}, is used
in the forward accelerations recursion of the AB algorithm.  It is an
overloaded version of the same function used for tree topologies, requiring
additional parameters containing topology information and computed
loop-closure forces.

Two functions, {\tt pushForceStates} and {\tt popForceStates}, save and
restore the state of all {\tt dmForce} objects attached to the link.  These
functions are provided for adaptive-stepsize integrators which may need to
back out of an integration step.

Another two functions are provided for retrieving the energy of a link.  The
{\tt getPotentialEnergy} function returns the gravitational potential energy of
the link based on the supplied gravity vector and the {\tt ABForKinStruct}
parameter for the link.  The potential energy zero is defined by the plane
passing through the origin of the inertial coordinate system with the gravity
vector as its normal.  The {\tt getKineticEnergy} function returns the kinetic
energy of the link (translational + rotational) based on the supplied {\tt
ABForKinStruct} parameter.

The only function that is implemented in this class is the {\tt
forwardKinematics} function.  It is called by {\tt
dmArticulation::forwardKinematics} function and computes the current link's
position ({\tt link\_val\_curr->p\_ICS}) and orientation ({\tt
link\_val\_curr->R\_ICS}) with respect to the inertial coordinate frame given
the inboard link's position and orientation ({\tt link\_val\_inboard->p\_ICS}
and {\tt link\_val\_inboard->R\_ICS}).

See also: {\tt dmArticulation}, {\tt dmMDHLink}, {\tt dmSphericalLink},
          {\tt dmQuaternionLink}, {\tt dmZScrewTxLink}, {\tt dmMobileBaseLink}.

 */

class DM_DLL_API dmLink : public dmObject
{
public:
   ///
   dmLink();
   ///
   virtual ~dmLink();

   ///
   virtual int getNumDOFs() const = 0;
   ///
   virtual void setState(Float q[], Float qd[]) = 0;
   ///
   virtual void getState(Float q[], Float qd[]) const = 0;
   ///
   virtual void getPose(RotationMatrix R, CartesianVector p) const = 0;

   ///
   virtual void setJointInput(Float joint_input[]) = 0;

   ///
   Float getJointFriction() const {return m_joint_friction;}
   ///
   void setJointFriction(Float u_c) {m_joint_friction = u_c;}

// Link-to-link transformation functions:
   ///
   virtual void rtxToInboard(const CartesianVector curr,
                             CartesianVector prev) const;
   ///
   virtual void rtxFromInboard(const CartesianVector prev,
                               CartesianVector curr) const;
   ///
   virtual void stxToInboard(const SpatialVector curr,
                             SpatialVector prev) const;
   ///
   virtual void stxFromInboard(const SpatialVector prev,
                               SpatialVector curr) const;
   ///
   virtual void rcongtxToInboardSym(const CartesianTensor Curr,
                                    CartesianTensor Prev) const;
   ///
   virtual void rcongtxToInboardGen(const CartesianTensor Curr,
                                    CartesianTensor Prev) const;
   ///
   virtual void scongtxToInboardIrefl(const SpatialTensor N_curr,
                                      SpatialTensor N_prev) const;
   ///
   virtual void XikToInboard(Float **Xik_curr,
                             Float **Xik_prev,
                             int columns_Xik) const = 0;
   ///
   virtual void BToInboard(Float **Bkn,
                           Float **Xik, int cols_Xik,
                           Float **Xin, int cols_Xin) const = 0;
   ///
   virtual void xformZetak(Float *zetak,
                           Float **Xik, int cols_Xik) const = 0;


// Articulated-Body (AB) algorithm functions:
   ///
   virtual void ABForwardKinematics(Float q[],
                                    Float qd[],
                                    const dmABForKinStruct &link_val_inboard,
                                    dmABForKinStruct &link_val_curr) = 0;

   ///
   virtual void ABBackwardDynamics(const dmABForKinStruct &link_val_curr,
                                   SpatialVector f_star_curr,
                                   SpatialTensor N_refl_curr,
                                   SpatialVector f_star_inboard,
                                   SpatialTensor N_refl_inboard) = 0;
   ///
   virtual void ABBackwardDynamicsN(const dmABForKinStruct &link_val_curr,
                                    SpatialVector f_star_inboard,
                                    SpatialTensor N_refl_inboard) = 0;

   ///
   virtual void ABForwardAccelerations(SpatialVector a_inboard,
                                       SpatialVector a_curr,
                                       Float qd[],
                                       Float qdd[]) = 0;
   ///
   virtual void ABForwardAccelerations(SpatialVector a_inboard,
                                       unsigned int *LB,
                                       unsigned int num_elements_LB,
                                       Float ***Xik,
                                       Float **constraint_forces,
                                       unsigned int *num_constraints,
                                       SpatialVector a_curr,
                                       Float qd[],
                                       Float qdd[]) = 0;

// Generic forward kinematics algorithm functions:
   ///
   void forwardKinematics(dmABForKinStruct &link_val_inboard,
                          dmABForKinStruct &link_val_curr) const;

   ///
   virtual void pushForceStates() {};
   ///
   virtual void popForceStates() {};
   ///
   virtual Float getPotentialEnergy(const dmABForKinStruct &link_val,
                                    CartesianVector a_gravity) const = 0;
   ///
   virtual Float getKineticEnergy(const dmABForKinStruct &link_val) const = 0;

// rendering functions:
   ///
   virtual void draw() const = 0;

private:   // not implemented
   dmLink(const dmLink &);
   dmLink &operator=(const dmLink &);

protected:
   CartesianVector m_p;   // position of this link's CS wrt inboard link.
   RotationMatrix  m_R;   // Rotation matrix ^i{\bf R}_{i-1}

   bool  m_joint_limit_flag;      // define dynamic behavior when joint
   Float m_joint_limit_spring;    // limits are reached.
   Float m_joint_limit_damper;
   Float m_joint_friction;        // Coulomb(?) friction coefficient.

   SpatialVector m_zeta;          // Additional variables for the AB algorithm
   SpatialTensor m_N_refl;        // member functions that are not
   SpatialVector m_gamma;         // dependent on the joint type.
};

#endif
