World of Rigid Bodies (WoRB)
|
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 }