World of Rigid Bodies (WoRB)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
mexFunction.cpp
Go to the documentation of this file.
00001 /**
00002  *  @file      mexFunction.cpp
00003  *  @brief     The main entry point for the WoRB MATLAB function.
00004  *  @author    Mikica Kocic
00005  *  @version   0.4
00006  *  @date      2012-05-14
00007  *  @copyright GNU Public License.
00008  */
00009 
00010 #include "WoRB.h"
00011 #include "Utilities.h"
00012 #include "WoRB_TestBed.h"
00013 
00014 #include "mex.h"
00015 
00016 #include "mexWoRB.h"
00017 
00018 /////////////////////////////////////////////////////////////////////////////////////////
00019 
00020 /** Represents WoRB test-bed that can be initialized from `mexFunction` arguments.
00021  */
00022 class WoRB_MexFunction : public WoRB_TestBed
00023 {
00024     /** Holds the results returned to MATLAB.
00025      */
00026     Mex::Matrix Result;
00027 
00028 public:
00029 
00030     /** Default constructor. Creates an empty result matrix.
00031      */
00032     WoRB_MexFunction ()
00033         : Result( 0, 0 )
00034     {
00035     }
00036 
00037     /** Initializes parameters and creates bodies from mexFunction arguments.
00038      * The first mex function argument should be a structure with fields as sys params.
00039      * The second mex function argument should be a structure array with body parameters.
00040       */
00041     void Parse( 
00042         int nArgIn, const mxArray* argIn[] // right-hand side (varargin) of the function
00043         )
00044     {
00045         if ( nArgIn >= 1 && ! mxIsStruct( argIn[0] ) ) {
00046             WoRB::SevereError( "WoRB:Init:invarg",
00047                 "The first argument must be a structure with system parameters." );
00048         }
00049 
00050         if ( nArgIn >= 2 && ! mxIsStruct( argIn[1] ) ) {
00051             WoRB::SevereError( "WoRB:Init:invarg", 
00052                 "The second argument must be a structure array of body parameters." );
00053         }
00054 
00055         /////////////////////////////////////////////////////////////////////////////////
00056         // Clear objects from previous simulations and make default parameters.
00057         //
00058             
00059         Result = Mex::Matrix( 0, 0 );  // Clear earlier results
00060         Initialize ();                 // Initialize default parameters
00061         ClearTestBed ();               // Clear existing simulation, if any.
00062 
00063         /////////////////////////////////////////////////////////////////////////////////
00064         // Parse params structure
00065         //
00066         bool dumpInitialState = false;
00067 
00068         if ( nArgIn >= 1 )  
00069         {
00070             const mxArray* params = argIn[0];
00071 
00072             WindowTitle = Mex::String( params, 0, "Title" );
00073 
00074             TestSuite            = Mex::Logical( params, 0, "TestSuite"          );
00075             IsInitialized        = Mex::Logical( params, 0, "IsInitialized"      );
00076             IsRunning            = Mex::Logical( params, 0, "IsRunning"          );
00077             IsPaused             = Mex::Logical( params, 0, "IsPaused"           );
00078             AutoPause            = Mex::Logical( params, 0, "AutoPause"          );
00079             Wireframe            = Mex::Logical( params, 0, "Wireframe"          );
00080             ShowBodyAxes         = Mex::Logical( params, 0, "ShowBodyAxes"       );
00081             ShowFloorMirror      = Mex::Logical( params, 0, "ShowFloorMirror"    );
00082             ShowContacts         = Mex::Logical( params, 0, "ShowContacts"       );
00083             ShowTrajectories     = Mex::Logical( params, 0, "ShowTrajectories"   );
00084             ShowStateVariables   = Mex::Logical( params, 0, "ShowStateVariables" );
00085             ShowHelp             = Mex::Logical( params, 0, "ShowHelp"           );
00086             dumpInitialState     = Mex::Logical( params, 0, "DumpInitialState"   );
00087 
00088             GridTickLength = Mex::Scalar( params, 0, "GridTickLength" );
00089             GridTicks      = int( Mex::Scalar( params, 0, "GridTicks" ) );
00090 
00091             TimeStep             = Mex::Scalar( params, 0, "TimeStep" );
00092             TimeStepsPerFrame    = unsigned( Mex::Scalar( params, 0, "TimeStepsPerFrame" ) );
00093             TimeStepsPerSnapshot = unsigned( Mex::Scalar( params, 0, "TimeStepsPerSnapshot" ) );
00094 
00095             CameraAngle      = Mex::Scalar( params, 0, "CameraAngle"     );
00096             CameraElevation  = Mex::Scalar( params, 0, "CameraElevation" );
00097             CameraZoom       = Mex::Scalar( params, 0, "CameraZoom"      );
00098 
00099             Mex::Matrix lookAt( params, 0, "CameraLookAt" );
00100             lookAt.VerifyDims( 1, 3, "CameraLookAt must be a spatial vector" );
00101             CameraLookAt = WoRB::SpatialVector( lookAt(0), lookAt(1), lookAt(2) );
00102 
00103             FollowObject = unsigned( Mex::Scalar( params, 0, "FollowObject" ) );
00104             FinalTime    = Mex::Scalar( params, 0, "FinalTime" );
00105 
00106             worb.Collisions.Restitution = Mex::Scalar( params, 0, "Restitution" );
00107             worb.Collisions.Relaxation  = Mex::Scalar( params, 0, "Relaxation"  );
00108             worb.Collisions.Friction    = Mex::Scalar( params, 0, "Friction"    );
00109 
00110             Mex::Matrix G( params, 0, "Gravity" );
00111             G.VerifyDims( 1, 3, "Gravity must be a spatial vector" );
00112             worb.Gravity = WoRB::SpatialVector( G(0), G(1), G(2) );
00113         }
00114 
00115         /////////////////////////////////////////////////////////////////////////////////
00116         // Parse bodies array structure
00117         //
00118         if ( nArgIn >= 2 )
00119         {
00120             const mxArray* body = argIn[1];
00121             unsigned bodyCount = unsigned( mxGetNumberOfElements( body ) );
00122 
00123             for ( unsigned i = 0; i < bodyCount; ++i )
00124             {
00125                 Mex::String geometry( body, i, "Geometry" ); // Geometry class
00126                 Mex::Matrix R( body, i, "HalfExtent" ); // Radius or half-extent
00127                 Mex::Matrix M( body, i, "M" ); // Body mass
00128                 Mex::Matrix X( body, i, "X" ); // Position
00129                 Mex::Matrix Q( body, i, "Q" ); // Oritentation
00130                 Mex::Matrix V( body, i, "V" ); // Velocity
00131                 Mex::Matrix W( body, i, "W" ); // Angular velocity
00132 
00133                 Mex::Logical showTrajectory   ( body, i, "ShowTrajectory",   true  );
00134                 Mex::Logical isActive         ( body, i, "CanBeDeactivated", true  );
00135                 Mex::Logical canBeDeactivated ( body, i, "CanBeDeactivated", false );
00136 
00137                 Mex::Matrix cActive  ( body, i, "ActiveColor"   ); // Active color
00138                 Mex::Matrix cInactive( body, i, "InactiveColor" ); // Inactive color
00139 
00140                 // Verify dimensions of body parameters
00141                 //
00142                 M.VerifyDims( 1, 1, "Mass must be a scalar" );
00143                 X.VerifyDims( 1, 3, "Position must be a spatial vector" );
00144                 Q.VerifyDims( 1, 4, "Orientation must be a quaternion" );
00145                 V.VerifyDims( 1, 3, "Velocity must be a spatial vector" );
00146                 W.VerifyDims( 1, 3, "Angular velocity must be a spatial vector" );
00147 
00148                 // Create rigid bodies with geometries given by parameters
00149                 //
00150                 WoRB::GLUT_Renderer* obj = NULL;
00151 
00152                 if ( geometry == "cuboid" )
00153                 {
00154                     R.VerifyDims( 1, 3,
00155                         "Half-extent of a cuboid must be a spatial vector" );
00156 
00157                     obj = new WoRB::Box(
00158                         /* position      */ WoRB::SpatialVector( X(0), X(1), X(2) ), 
00159                         /* orientation   */ WoRB::Quaternion   ( Q(0), Q(1), Q(2), Q(3) ),
00160                         /* velocity      */ WoRB::SpatialVector( V(0), V(1), V(2) ), 
00161                         /* ang. velocity */ WoRB::SpatialVector( W(0), W(1), W(2) ),
00162                         /* half-extent   */ WoRB::SpatialVector( R(0), R(1), R(2) ), 
00163                         /* mass          */ M(0)
00164                     );
00165                 }
00166                 else if ( geometry == "sphere" )
00167                 {
00168                     R.VerifyDims( 1, 1,
00169                         "Half-extent i.e. radius of a sphere must be a scalar" );
00170 
00171                     obj = new WoRB::Ball(
00172                         /* position      */ WoRB::SpatialVector( X(0), X(1), X(2) ), 
00173                         /* orientation   */ WoRB::Quaternion   ( Q(0), Q(1), Q(2), Q(3) ),
00174                         /* velocity      */ WoRB::SpatialVector( V(0), V(1), V(2) ), 
00175                         /* ang. velocity */ WoRB::SpatialVector( W(0), W(1), W(2) ),
00176                         /* radius        */ R(0), 
00177                         /* mass          */ M(0)
00178                     );
00179                 }
00180                 else {
00181                     WoRB::SevereError( "WoRB:Init:invarg", 
00182                         "Invalid body(%u) type '%s'; "
00183                         "allowed types are 'cuboid' or 'sphere'.",
00184                         i, (const char*)geometry );
00185                 }
00186 
00187                 // Optional trajectory flag
00188                 //
00189                 obj->ShowTrajectory = showTrajectory;
00190                 obj->GetBody().SetCanBeDeactivated( canBeDeactivated );
00191                 if ( ! isActive ) {
00192                     obj->GetBody().Deactivate ();
00193                 }
00194 
00195                 // Optional active color
00196                 //
00197                 if ( cActive.IsSize( 1, 4 ) ) {
00198                     obj->ActiveColor = WoRB::GLUT_Renderer::Colorf( 
00199                         cActive(0), cActive(1), cActive(2), cActive(3)
00200                     );
00201                 }
00202 
00203                 // Optional inactive color
00204                 //
00205                 if ( cInactive.IsSize( 1, 4 ) ) {
00206                     obj->InactiveColor = WoRB::GLUT_Renderer::Colorf( 
00207                         cInactive(0), cInactive(1), cInactive(2), cInactive(3)
00208                     );
00209                 }
00210 
00211                 // Add objects to the list
00212                 //
00213                 Objects.push_back( obj );
00214                 worb.Add( obj->GetGeometry () );
00215             }
00216 
00217             // Initialize ODE (calculate derived quantities for recently added objects)
00218             //
00219             worb.InitializeODE ();
00220 
00221             // Clear test-suite configuration request since we've configured test-bed.
00222             //
00223             TestSuite = -1;
00224         }
00225 
00226         /////////////////////////////////////////////////////////////////////////////////
00227         // Report parameters and bodies that we have so far.
00228         //
00229         if ( dumpInitialState )
00230         {
00231             Dump (); 
00232             WoRB::Printf( "\n" );
00233         }
00234     }
00235 
00236     /** Gets the result matrix.
00237      */
00238     mxArray* GetResult ()
00239     {
00240         return Result;
00241     }
00242 
00243     /** Creates the result matrix.
00244      */
00245     void CreateResultMatrix ()
00246     {
00247         // Ensure to have have a finite number of recorded time-steps
00248         //
00249         if ( FinalTime == 0 ) {
00250             return;  
00251         }
00252 
00253         unsigned n_steps = unsigned( FinalTime / TimeStep ) + 1;
00254         Result = Mex::Matrix( n_steps, 11 );
00255 
00256         // Set 'time' column to NaN (indicating row do not have valid data yet).
00257         //
00258         for ( unsigned i = 0; i < n_steps; ++i ) {
00259             Result( i, 0 ) = mxGetNaN ();
00260         }
00261 
00262         WoRB::Printf( "WoRB: Created matrix for results [ %d × %d ]\n", 
00263             Result.GetM (), Result.GetN () );
00264     }
00265 
00266     /** Saves simulated data to be returned to MATLAB as the result.
00267      */
00268     virtual void OnProcessData ()
00269     {
00270         if ( Result.IsEmpty () ) {
00271             return;
00272         }
00273 
00274         unsigned n = worb.TimeStepCount;
00275 
00276         Result( n, 0 ) = worb.Time;
00277         Result( n, 1 ) = worb.Collisions.Count ();
00278 
00279         Result( n, 2 ) = worb.TotalKineticEnergy;
00280         Result( n, 3 ) = worb.TotalPotentialEnergy;
00281 
00282         Result( n, 4 ) = worb.TotalLinearMomentum.x;
00283         Result( n, 5 ) = worb.TotalLinearMomentum.y;
00284         Result( n, 6 ) = worb.TotalLinearMomentum.z;
00285 
00286         Result( n, 7 ) = worb.TotalAngularMomentum.x;
00287         Result( n, 8 ) = worb.TotalAngularMomentum.y;
00288         Result( n, 9 ) = worb.TotalAngularMomentum.z;
00289 
00290         Result( n, 10 ) = Objects.size () == 0 ? 0.0
00291             : Objects.at( Objects.size() - 1 )->GetBody().Position.y;
00292     }
00293 };
00294 
00295 /////////////////////////////////////////////////////////////////////////////////////////
00296 
00297 /** Definition of the static instance wrapper for our GLUT application.
00298  */
00299 template<class T> 
00300 T* WoRB::GLUT_Framework<T>::Application = 0;
00301 
00302 static WoRB_MexFunction Application;
00303 static WoRB::GLUT_Framework<WoRB_MexFunction> glut;
00304 
00305 /////////////////////////////////////////////////////////////////////////////////////////
00306 
00307 /** The main entry routine for the MATLAB function WoRB.
00308  */
00309 void mexFunction
00310 (
00311     int nArgOut, mxArray* argOut[],       // left-hand side (varargout) of the function
00312     int nArgIn, const mxArray* argIn[]    // right-hand side (varargin) of the function
00313     )
00314 {
00315     if ( ! glut.Initialize () ) {
00316         return;
00317     }
00318 
00319     // Prevent clearing MEX-file from memory
00320     //
00321     if ( ! mexIsLocked () ) {
00322         mexLock (); 
00323     }
00324 
00325     // Parse mexFunction input arguments
00326     //
00327     Application.Parse( nArgIn, argIn );
00328 
00329     // 
00330     if ( nArgOut >= 1 ) {
00331         Application.CreateResultMatrix ();
00332     }
00333 
00334     Application.SetupAnimation ();
00335 
00336     glut.Connect( Application );
00337 
00338     Application.Run ();
00339 
00340     glut.Disconnect ();
00341 
00342     if ( nArgOut >= 1 ) {
00343         argOut[0] = Application.GetResult ();
00344     }
00345 }