Blender V2.61 - r43446

SCA_RandomActuator.cpp

Go to the documentation of this file.
00001 /*
00002  * Set random/camera stuff
00003  *
00004  *
00005  * ***** BEGIN GPL LICENSE BLOCK *****
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software Foundation,
00019  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00022  * All rights reserved.
00023  *
00024  * The Original Code is: all of this file.
00025  *
00026  * Contributor(s): none yet.
00027  *
00028  * ***** END GPL LICENSE BLOCK *****
00029  */
00030 
00036 #include <stddef.h>
00037 
00038 #include "BoolValue.h"
00039 #include "IntValue.h"
00040 #include "FloatValue.h"
00041 #include "SCA_IActuator.h"
00042 #include "SCA_RandomActuator.h"
00043 #include "math.h"
00044 #include "MT_Transform.h"
00045 
00046 /* ------------------------------------------------------------------------- */
00047 /* Native functions                                                          */
00048 /* ------------------------------------------------------------------------- */
00049 
00050 SCA_RandomActuator::SCA_RandomActuator(SCA_IObject *gameobj, 
00051                                      long seed,
00052                                      SCA_RandomActuator::KX_RANDOMACT_MODE mode,
00053                                      float para1,
00054                                      float para2,
00055                                      const STR_String &propName)
00056     : SCA_IActuator(gameobj, KX_ACT_RANDOM),
00057       m_propname(propName),
00058       m_parameter1(para1),
00059       m_parameter2(para2),
00060       m_distribution(mode)
00061 {
00062     m_base = new SCA_RandomNumberGenerator(seed);
00063     m_counter = 0;
00064     enforceConstraints();
00065 } 
00066 
00067 
00068 
00069 SCA_RandomActuator::~SCA_RandomActuator()
00070 {
00071     m_base->Release();
00072 } 
00073 
00074 
00075 
00076 CValue* SCA_RandomActuator::GetReplica()
00077 {
00078     SCA_RandomActuator* replica = new SCA_RandomActuator(*this);
00079     // replication just copy the m_base pointer => common random generator
00080     replica->ProcessReplica();
00081     return replica;
00082 }
00083 
00084 void SCA_RandomActuator::ProcessReplica()
00085 {
00086     SCA_IActuator::ProcessReplica();
00087     // increment reference count so that we can release the generator at the end
00088     m_base->AddRef();
00089 }
00090 
00091 
00092 
00093 bool SCA_RandomActuator::Update()
00094 {
00095     //bool result = false;  /*unused*/
00096     bool bNegativeEvent = IsNegativeEvent();
00097 
00098     RemoveAllEvents();
00099 
00100 
00101     CValue *tmpval = NULL;
00102 
00103     if (bNegativeEvent)
00104         return false; // do nothing on negative events
00105 
00106     switch (m_distribution) {
00107     case KX_RANDOMACT_BOOL_CONST: {
00108         /* un petit peu filthy */
00109         bool res = !(m_parameter1 < 0.5);
00110         tmpval = new CBoolValue(res);
00111     }
00112     break;
00113     case KX_RANDOMACT_BOOL_UNIFORM: {
00114         /* flip a coin */
00115         bool res; 
00116         if (m_counter > 31) {
00117             m_previous = m_base->Draw();
00118             res = ((m_previous & 0x1) == 0);
00119             m_counter = 1;
00120         } else {
00121             res = (((m_previous >> m_counter) & 0x1) == 0);
00122             m_counter++;
00123         }
00124         tmpval = new CBoolValue(res);
00125     }
00126     break;
00127     case KX_RANDOMACT_BOOL_BERNOUILLI: {
00128         /* 'percentage' */
00129         bool res;
00130         res = (m_base->DrawFloat() < m_parameter1);
00131         tmpval = new CBoolValue(res);
00132     }
00133     break;
00134     case KX_RANDOMACT_INT_CONST: {
00135         /* constant */
00136         tmpval = new CIntValue((int) floor(m_parameter1));
00137     }
00138     break;
00139     case KX_RANDOMACT_INT_UNIFORM: {
00140         /* uniform (toss a die) */
00141         int res; 
00142         /* The [0, 1] interval is projected onto the [min, max+1] domain,    */
00143         /* and then rounded.                                                 */
00144         res = (int) floor( ((m_parameter2 - m_parameter1 + 1) * m_base->DrawFloat())
00145                            + m_parameter1);
00146         tmpval = new CIntValue(res);
00147     }
00148     break;
00149     case KX_RANDOMACT_INT_POISSON: {
00150         /* poisson (queues) */
00151         /* If x_1, x_2, ... is a sequence of random numbers with uniform     */
00152         /* distribution between zero and one, k is the first integer for     */
00153         /* which the product x_1*x_2*...*x_k < exp(-\lamba).                 */
00154         float a, b;
00155         int res = 0;
00156         /* The - sign is important here! The number to test for, a, must be  */
00157         /* between 0 and 1.                                                  */
00158         a = exp(-m_parameter1);
00159         /* a quickly reaches 0.... so we guard explicitly for that.          */
00160         if (a < FLT_MIN) a = FLT_MIN;
00161         b = m_base->DrawFloat();
00162         while (b >= a) {
00163             b = b * m_base->DrawFloat();
00164             res++;
00165         };  
00166         tmpval = new CIntValue(res);
00167     }
00168     break;
00169     case KX_RANDOMACT_FLOAT_CONST: {
00170         /* constant */
00171         tmpval = new CFloatValue(m_parameter1);
00172     }
00173     break;
00174     case KX_RANDOMACT_FLOAT_UNIFORM: {
00175         float res = ((m_parameter2 - m_parameter1) * m_base->DrawFloat())
00176             + m_parameter1;
00177         tmpval = new CFloatValue(res);
00178     }
00179     break;
00180     case KX_RANDOMACT_FLOAT_NORMAL: {
00181         /* normal (big numbers): para1 = mean, para2 = std dev               */
00182 
00183         /* 
00184 
00185            070301 - nzc - Changed the termination condition. I think I 
00186            made a small mistake here, but it only affects distro's where
00187            the seed equals 0. In that case, the algorithm locks. Let's
00188            just guard that case separately.
00189 
00190         */
00191 
00192         float x = 0.0, y = 0.0, s = 0.0, t = 0.0;
00193         if (m_base->GetSeed() == 0) {
00194             /*
00195 
00196               070301 - nzc 
00197               Just taking the mean here seems reasonable.
00198 
00199              */
00200             tmpval = new CFloatValue(m_parameter1);
00201         } else {
00202             /*
00203 
00204               070301 - nzc 
00205               Now, with seed != 0, we will most assuredly get some
00206               sensible values. The termination condition states two 
00207               things: 
00208               1. s >= 0 is not allowed: to prevent the distro from 
00209                  getting a bias towards high values. This is a small
00210                  correction, really, and might also be left out.
00211               2. s == 0 is not allowed: to prevent a division by zero
00212                  when renormalising the drawn value to the desired
00213                  distribution shape. As a side effect, the distro will
00214                  never yield the exact mean. 
00215               I am not sure whether this is consistent, since the error 
00216               cause by #2 is of the same magnitude as the one 
00217               prevented by #1. The error introduced into the SD will be 
00218               improved, though. By how much? Hard to say... If you like
00219               the maths, feel free to analyse. Be aware that this is 
00220               one of the really old standard algorithms. I think the 
00221               original came in Fortran, was translated to Pascal, and 
00222               then someone came up with the C code. My guess it that
00223               this will be quite sufficient here.
00224 
00225              */
00226             do 
00227             {
00228                 x = 2.0 * m_base->DrawFloat() - 1.0;
00229                 y = 2.0 * m_base->DrawFloat() - 1.0;
00230                 s = x*x + y*y;
00231             } while ( (s >= 1.0) || (s == 0.0) );
00232             t = x * sqrt( (-2.0 * log(s)) / s);
00233             tmpval = new CFloatValue(m_parameter1 + m_parameter2 * t);
00234         }
00235     }
00236     break;
00237     case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL: {
00238         /* 1st order fall-off. I am very partial to using the half-life as    */
00239         /* controlling parameter. Using the 'normal' exponent is not very     */
00240         /* intuitive...                                                       */
00241         /* tmpval = new CFloatValue( (1.0 / m_parameter1)                     */
00242         tmpval = new CFloatValue( (m_parameter1) 
00243                                   * (-log(1.0 - m_base->DrawFloat())) );
00244 
00245     }
00246     break;
00247     default:
00248     {
00249         /* unknown distribution... */
00250         static bool randomWarning = false;
00251         if (!randomWarning) {
00252             randomWarning = true;
00253             std::cout << "RandomActuator '" << GetName() << "' has an unknown distribution." << std::endl;
00254         }
00255         return false;
00256     }
00257     }
00258 
00259     /* Round up: assign it */
00260     CValue *prop = GetParent()->GetProperty(m_propname);
00261     if (prop) {
00262         prop->SetValue(tmpval);
00263     }
00264     tmpval->Release();
00265 
00266     return false;
00267 }
00268 
00269 void SCA_RandomActuator::enforceConstraints()
00270 {
00271     /* The constraints that are checked here are the ones fundamental to     */
00272     /* the various distributions. Limitations of the algorithms are checked  */
00273     /* elsewhere (or they should be... ).                                    */
00274     switch (m_distribution) {
00275     case KX_RANDOMACT_BOOL_CONST:
00276     case KX_RANDOMACT_BOOL_UNIFORM:
00277     case KX_RANDOMACT_INT_CONST:
00278     case KX_RANDOMACT_INT_UNIFORM:
00279     case KX_RANDOMACT_FLOAT_UNIFORM:
00280     case KX_RANDOMACT_FLOAT_CONST:
00281         ; /* Nothing to be done here. We allow uniform distro's to have      */
00282         /* 'funny' domains, i.e. max < min. This does not give problems.     */
00283         break;
00284     case KX_RANDOMACT_BOOL_BERNOUILLI: 
00285         /* clamp to [0, 1] */
00286         if (m_parameter1 < 0.0) {
00287             m_parameter1 = 0.0;
00288         } else if (m_parameter1 > 1.0) {
00289             m_parameter1 = 1.0;
00290         }
00291         break;
00292     case KX_RANDOMACT_INT_POISSON: 
00293         /* non-negative */
00294         if (m_parameter1 < 0.0) {
00295             m_parameter1 = 0.0;
00296         }
00297         break;
00298     case KX_RANDOMACT_FLOAT_NORMAL: 
00299         /* standard dev. is non-negative */
00300         if (m_parameter2 < 0.0) {
00301             m_parameter2 = 0.0;
00302         }
00303         break;
00304     case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL: 
00305         /* halflife must be non-negative */
00306         if (m_parameter1 < 0.0) {
00307             m_parameter1 = 0.0;
00308         }
00309         break;
00310     default:
00311         ; /* unknown distribution... */
00312     }
00313 }
00314 
00315 #ifdef WITH_PYTHON
00316 
00317 /* ------------------------------------------------------------------------- */
00318 /* Python functions                                                          */
00319 /* ------------------------------------------------------------------------- */
00320 
00321 /* Integration hooks ------------------------------------------------------- */
00322 PyTypeObject SCA_RandomActuator::Type = {
00323     PyVarObject_HEAD_INIT(NULL, 0)
00324     "SCA_RandomActuator",
00325     sizeof(PyObjectPlus_Proxy),
00326     0,
00327     py_base_dealloc,
00328     0,
00329     0,
00330     0,
00331     0,
00332     py_base_repr,
00333     0,0,0,0,0,0,0,0,0,
00334     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00335     0,0,0,0,0,0,0,
00336     Methods,
00337     0,
00338     0,
00339     &SCA_IActuator::Type,
00340     0,0,0,0,0,0,
00341     py_base_new
00342 };
00343 
00344 PyMethodDef SCA_RandomActuator::Methods[] = {
00345     KX_PYMETHODTABLE(SCA_RandomActuator, setBoolConst),
00346     KX_PYMETHODTABLE_NOARGS(SCA_RandomActuator, setBoolUniform),
00347     KX_PYMETHODTABLE(SCA_RandomActuator, setBoolBernouilli),
00348 
00349     KX_PYMETHODTABLE(SCA_RandomActuator, setIntConst),
00350     KX_PYMETHODTABLE(SCA_RandomActuator, setIntUniform),
00351     KX_PYMETHODTABLE(SCA_RandomActuator, setIntPoisson),
00352 
00353     KX_PYMETHODTABLE(SCA_RandomActuator, setFloatConst),
00354     KX_PYMETHODTABLE(SCA_RandomActuator, setFloatUniform),
00355     KX_PYMETHODTABLE(SCA_RandomActuator, setFloatNormal),
00356     KX_PYMETHODTABLE(SCA_RandomActuator, setFloatNegativeExponential),
00357     {NULL,NULL} //Sentinel
00358 };
00359 
00360 PyAttributeDef SCA_RandomActuator::Attributes[] = {
00361     KX_PYATTRIBUTE_FLOAT_RO("para1",SCA_RandomActuator,m_parameter1),
00362     KX_PYATTRIBUTE_FLOAT_RO("para2",SCA_RandomActuator,m_parameter2),
00363     KX_PYATTRIBUTE_ENUM_RO("distribution",SCA_RandomActuator,m_distribution),
00364     KX_PYATTRIBUTE_STRING_RW_CHECK("propName",0,MAX_PROP_NAME,false,SCA_RandomActuator,m_propname,CheckProperty),
00365     KX_PYATTRIBUTE_RW_FUNCTION("seed",SCA_RandomActuator,pyattr_get_seed,pyattr_set_seed),
00366     { NULL }    //Sentinel
00367 };  
00368 
00369 PyObject* SCA_RandomActuator::pyattr_get_seed(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00370 {
00371     SCA_RandomActuator* act = static_cast<SCA_RandomActuator*>(self);
00372     return PyLong_FromSsize_t(act->m_base->GetSeed());
00373 }
00374 
00375 int SCA_RandomActuator::pyattr_set_seed(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00376 {
00377     SCA_RandomActuator* act = static_cast<SCA_RandomActuator*>(self);
00378     if (PyLong_Check(value))    {
00379         int ival = PyLong_AsSsize_t(value);
00380         act->m_base->SetSeed(ival);
00381         return PY_SET_ATTR_SUCCESS;
00382     } else {
00383         PyErr_SetString(PyExc_TypeError, "actuator.seed = int: Random Actuator, expected an integer");
00384         return PY_SET_ATTR_FAIL;
00385     }
00386 }
00387 
00388 /* 11. setBoolConst */
00389 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setBoolConst,
00390 "setBoolConst(value)\n"
00391 "\t- value: 0 or 1\n"
00392 "\tSet this generator to produce a constant boolean value.\n") 
00393 {
00394     int paraArg;
00395     if(!PyArg_ParseTuple(args, "i:setBoolConst", &paraArg)) {
00396         return NULL;
00397     }
00398     
00399     m_distribution = KX_RANDOMACT_BOOL_CONST;
00400     m_parameter1 = (paraArg) ? 1.0 : 0.0;
00401     
00402     Py_RETURN_NONE;
00403 }
00404 /* 12. setBoolUniform, */
00405 KX_PYMETHODDEF_DOC_NOARGS(SCA_RandomActuator, setBoolUniform,
00406 "setBoolUniform()\n"
00407 "\tSet this generator to produce true and false, each with 50%% chance of occuring\n") 
00408 {
00409     /* no args */
00410     m_distribution = KX_RANDOMACT_BOOL_UNIFORM;
00411     enforceConstraints();
00412     Py_RETURN_NONE;
00413 }
00414 /* 13. setBoolBernouilli,  */
00415 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setBoolBernouilli,
00416 "setBoolBernouilli(value)\n"
00417 "\t- value: a float between 0 and 1\n"
00418 "\tReturn false value * 100%% of the time.\n")
00419 {
00420     float paraArg;
00421     if(!PyArg_ParseTuple(args, "f:setBoolBernouilli", &paraArg)) {
00422         return NULL;
00423     }
00424     
00425     m_distribution = KX_RANDOMACT_BOOL_BERNOUILLI;
00426     m_parameter1 = paraArg; 
00427     enforceConstraints();
00428     Py_RETURN_NONE;
00429 }
00430 /* 14. setIntConst,*/
00431 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntConst,
00432 "setIntConst(value)\n"
00433 "\t- value: integer\n"
00434 "\tAlways return value\n") 
00435 {
00436     int paraArg;
00437     if(!PyArg_ParseTuple(args, "i:setIntConst", &paraArg)) {
00438         return NULL;
00439     }
00440     
00441     m_distribution = KX_RANDOMACT_INT_CONST;
00442     m_parameter1 = paraArg;
00443     enforceConstraints();
00444     Py_RETURN_NONE;
00445 }
00446 /* 15. setIntUniform,*/
00447 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntUniform,
00448 "setIntUniform(lower_bound, upper_bound)\n"
00449 "\t- lower_bound: integer\n"
00450 "\t- upper_bound: integer\n"
00451 "\tReturn a random integer between lower_bound and\n"
00452 "\tupper_bound. The boundaries are included.\n")
00453 {
00454     int paraArg1, paraArg2;
00455     if(!PyArg_ParseTuple(args, "ii:setIntUniform", &paraArg1, &paraArg2)) {
00456         return NULL;
00457     }
00458     
00459     m_distribution = KX_RANDOMACT_INT_UNIFORM;
00460     m_parameter1 = paraArg1;
00461     m_parameter2 = paraArg2;
00462     enforceConstraints();
00463     Py_RETURN_NONE;
00464 }
00465 /* 16. setIntPoisson,       */
00466 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setIntPoisson,
00467 "setIntPoisson(value)\n"
00468 "\t- value: float\n"
00469 "\tReturn a Poisson-distributed number. This performs a series\n"
00470 "\tof Bernouilli tests with parameter value. It returns the\n"
00471 "\tnumber of tries needed to achieve succes.\n")
00472 {
00473     float paraArg;
00474     if(!PyArg_ParseTuple(args, "f:setIntPoisson", &paraArg)) {
00475         return NULL;
00476     }
00477     
00478     m_distribution = KX_RANDOMACT_INT_POISSON;
00479     m_parameter1 = paraArg; 
00480     enforceConstraints();
00481     Py_RETURN_NONE;
00482 }
00483 /* 17. setFloatConst */
00484 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatConst,
00485 "setFloatConst(value)\n"
00486 "\t- value: float\n"
00487 "\tAlways return value\n")
00488 {
00489     float paraArg;
00490     if(!PyArg_ParseTuple(args, "f:setFloatConst", &paraArg)) {
00491         return NULL;
00492     }
00493     
00494     m_distribution = KX_RANDOMACT_FLOAT_CONST;
00495     m_parameter1 = paraArg; 
00496     enforceConstraints();
00497     Py_RETURN_NONE;
00498 }
00499 /* 18. setFloatUniform, */
00500 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatUniform,
00501 "setFloatUniform(lower_bound, upper_bound)\n"
00502 "\t- lower_bound: float\n"
00503 "\t- upper_bound: float\n"
00504 "\tReturn a random integer between lower_bound and\n"
00505 "\tupper_bound.\n")
00506 {
00507     float paraArg1, paraArg2;
00508     if(!PyArg_ParseTuple(args, "ff:setFloatUniform", &paraArg1, &paraArg2)) {
00509         return NULL;
00510     }
00511     
00512     m_distribution = KX_RANDOMACT_FLOAT_UNIFORM;
00513     m_parameter1 = paraArg1;
00514     m_parameter2 = paraArg2;
00515     enforceConstraints();
00516     Py_RETURN_NONE;
00517 }
00518 /* 19. setFloatNormal, */
00519 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatNormal,
00520 "setFloatNormal(mean, standard_deviation)\n"
00521 "\t- mean: float\n"
00522 "\t- standard_deviation: float\n"
00523 "\tReturn normal-distributed numbers. The average is mean, and the\n"
00524 "\tdeviation from the mean is characterized by standard_deviation.\n")
00525 {
00526     float paraArg1, paraArg2;
00527     if(!PyArg_ParseTuple(args, "ff:setFloatNormal", &paraArg1, &paraArg2)) {
00528         return NULL;
00529     }
00530     
00531     m_distribution = KX_RANDOMACT_FLOAT_NORMAL;
00532     m_parameter1 = paraArg1;
00533     m_parameter2 = paraArg2;
00534     enforceConstraints();
00535     Py_RETURN_NONE;
00536 }
00537 /* 20. setFloatNegativeExponential, */
00538 KX_PYMETHODDEF_DOC_VARARGS(SCA_RandomActuator, setFloatNegativeExponential, 
00539 "setFloatNegativeExponential(half_life)\n"
00540 "\t- half_life: float\n"
00541 "\tReturn negative-exponentially distributed numbers. The half-life 'time'\n"
00542 "\tis characterized by half_life.\n")
00543 {
00544     float paraArg;
00545     if(!PyArg_ParseTuple(args, "f:setFloatNegativeExponential", &paraArg)) {
00546         return NULL;
00547     }
00548     
00549     m_distribution = KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL;
00550     m_parameter1 = paraArg; 
00551     enforceConstraints();
00552     Py_RETURN_NONE;
00553 }
00554 
00555 #endif
00556 
00557 /* eof */