Blender  V2.59
SCA_RandomActuator.cpp
Go to the documentation of this file.
00001 /*
00002  * Set random/camera stuff
00003  *
00004  * $Id: SCA_RandomActuator.cpp 35169 2011-02-25 13:32:11Z jesterking $
00005  *
00006  * ***** BEGIN GPL LICENSE BLOCK *****
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License
00010  * as published by the Free Software Foundation; either version 2
00011  * of the License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software Foundation,
00020  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  *
00022  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00023  * All rights reserved.
00024  *
00025  * The Original Code is: all of this file.
00026  *
00027  * Contributor(s): none yet.
00028  *
00029  * ***** END GPL LICENSE BLOCK *****
00030  */
00031 
00037 #include <stddef.h>
00038 
00039 #include "BoolValue.h"
00040 #include "IntValue.h"
00041 #include "FloatValue.h"
00042 #include "SCA_IActuator.h"
00043 #include "SCA_RandomActuator.h"
00044 #include "math.h"
00045 #include "MT_Transform.h"
00046 
00047 /* ------------------------------------------------------------------------- */
00048 /* Native functions                                                          */
00049 /* ------------------------------------------------------------------------- */
00050 
00051 SCA_RandomActuator::SCA_RandomActuator(SCA_IObject *gameobj, 
00052                                                                          long seed,
00053                                                                          SCA_RandomActuator::KX_RANDOMACT_MODE mode,
00054                                                                          float para1,
00055                                                                          float para2,
00056                                                                          const STR_String &propName)
00057         : SCA_IActuator(gameobj, KX_ACT_RANDOM),
00058           m_propname(propName),
00059           m_parameter1(para1),
00060           m_parameter2(para2),
00061           m_distribution(mode)
00062 {
00063         m_base = new SCA_RandomNumberGenerator(seed);
00064         m_counter = 0;
00065         enforceConstraints();
00066 } 
00067 
00068 
00069 
00070 SCA_RandomActuator::~SCA_RandomActuator()
00071 {
00072         m_base->Release();
00073 } 
00074 
00075 
00076 
00077 CValue* SCA_RandomActuator::GetReplica()
00078 {
00079         SCA_RandomActuator* replica = new SCA_RandomActuator(*this);
00080         // replication just copy the m_base pointer => common random generator
00081         replica->ProcessReplica();
00082         return replica;
00083 }
00084 
00085 void SCA_RandomActuator::ProcessReplica()
00086 {
00087         SCA_IActuator::ProcessReplica();
00088         // increment reference count so that we can release the generator at the end
00089         m_base->AddRef();
00090 }
00091 
00092 
00093 
00094 bool SCA_RandomActuator::Update()
00095 {
00096         //bool result = false;  /*unused*/
00097         bool bNegativeEvent = IsNegativeEvent();
00098 
00099         RemoveAllEvents();
00100 
00101 
00102         CValue *tmpval = NULL;
00103 
00104         if (bNegativeEvent)
00105                 return false; // do nothing on negative events
00106 
00107         switch (m_distribution) {
00108         case KX_RANDOMACT_BOOL_CONST: {
00109                 /* un petit peu filthy */
00110                 bool res = !(m_parameter1 < 0.5);
00111                 tmpval = new CBoolValue(res);
00112         }
00113         break;
00114         case KX_RANDOMACT_BOOL_UNIFORM: {
00115                 /* flip a coin */
00116                 bool res; 
00117                 if (m_counter > 31) {
00118                         m_previous = m_base->Draw();
00119                         res = ((m_previous & 0x1) == 0);
00120                         m_counter = 1;
00121                 } else {
00122                         res = (((m_previous >> m_counter) & 0x1) == 0);
00123                         m_counter++;
00124                 }
00125                 tmpval = new CBoolValue(res);
00126         }
00127         break;
00128         case KX_RANDOMACT_BOOL_BERNOUILLI: {
00129                 /* 'percentage' */
00130                 bool res;
00131                 res = (m_base->DrawFloat() < m_parameter1);
00132                 tmpval = new CBoolValue(res);
00133         }
00134         break;
00135         case KX_RANDOMACT_INT_CONST: {
00136                 /* constant */
00137                 tmpval = new CIntValue((int) floor(m_parameter1));
00138         }
00139         break;
00140         case KX_RANDOMACT_INT_UNIFORM: {
00141                 /* uniform (toss a die) */
00142                 int res; 
00143                 /* The [0, 1] interval is projected onto the [min, max+1] domain,    */
00144                 /* and then rounded.                                                 */
00145                 res = (int) floor( ((m_parameter2 - m_parameter1 + 1) * m_base->DrawFloat())
00146                                                    + m_parameter1);
00147                 tmpval = new CIntValue(res);
00148         }
00149         break;
00150         case KX_RANDOMACT_INT_POISSON: {
00151                 /* poisson (queues) */
00152                 /* If x_1, x_2, ... is a sequence of random numbers with uniform     */
00153                 /* distribution between zero and one, k is the first integer for     */
00154                 /* which the product x_1*x_2*...*x_k < exp(-\lamba).                 */
00155                 float a = 0.0, b = 0.0;
00156                 int res = 0;
00157                 /* The - sign is important here! The number to test for, a, must be  */
00158                 /* between 0 and 1.                                                  */
00159                 a = exp(-m_parameter1);
00160                 /* a quickly reaches 0.... so we guard explicitly for that.          */
00161                 if (a < FLT_MIN) a = FLT_MIN;
00162                 b = m_base->DrawFloat();
00163                 while (b >= a) {
00164                         b = b * m_base->DrawFloat();
00165                         res++;
00166                 };      
00167                 tmpval = new CIntValue(res);
00168         }
00169         break;
00170         case KX_RANDOMACT_FLOAT_CONST: {
00171                 /* constant */
00172                 tmpval = new CFloatValue(m_parameter1);
00173         }
00174         break;
00175         case KX_RANDOMACT_FLOAT_UNIFORM: {
00176                 float res = ((m_parameter2 - m_parameter1) * m_base->DrawFloat())
00177                         + m_parameter1;
00178                 tmpval = new CFloatValue(res);
00179         }
00180         break;
00181         case KX_RANDOMACT_FLOAT_NORMAL: {
00182                 /* normal (big numbers): para1 = mean, para2 = std dev               */
00183 
00184                 /* 
00185 
00186                    070301 - nzc - Changed the termination condition. I think I 
00187                    made a small mistake here, but it only affects distro's where
00188                    the seed equals 0. In that case, the algorithm locks. Let's
00189                    just guard that case separately.
00190 
00191                 */
00192 
00193                 float x = 0.0, y = 0.0, s = 0.0, t = 0.0;
00194                 if (m_base->GetSeed() == 0) {
00195                         /*
00196 
00197                           070301 - nzc 
00198                           Just taking the mean here seems reasonable.
00199 
00200                          */
00201                         tmpval = new CFloatValue(m_parameter1);
00202                 } else {
00203                         /*
00204 
00205                           070301 - nzc 
00206                           Now, with seed != 0, we will most assuredly get some
00207                           sensible values. The termination condition states two 
00208                           things: 
00209                           1. s >= 0 is not allowed: to prevent the distro from 
00210                              getting a bias towards high values. This is a small 
00211                                  correction, really, and might also be left out.
00212                           2. s == 0 is not allowed: to prevent a division by zero
00213                              when renormalising the drawn value to the desired 
00214                                  distribution shape. As a side effect, the distro will
00215                                  never yield the exact mean. 
00216                           I am not sure whether this is consistent, since the error 
00217                           cause by #2 is of the same magnitude as the one 
00218                           prevented by #1. The error introduced into the SD will be 
00219                           improved, though. By how much? Hard to say... If you like
00220                           the maths, feel free to analyse. Be aware that this is 
00221                           one of the really old standard algorithms. I think the 
00222                           original came in Fortran, was translated to Pascal, and 
00223                           then someone came up with the C code. My guess it that
00224                           this will be quite sufficient here.
00225 
00226                          */
00227                         do 
00228                         {
00229                                 x = 2.0 * m_base->DrawFloat() - 1.0;
00230                                 y = 2.0 * m_base->DrawFloat() - 1.0;
00231                                 s = x*x + y*y;
00232                         } while ( (s >= 1.0) || (s == 0.0) );
00233                         t = x * sqrt( (-2.0 * log(s)) / s);
00234                         tmpval = new CFloatValue(m_parameter1 + m_parameter2 * t);
00235                 }
00236         }
00237         break;
00238         case KX_RANDOMACT_FLOAT_NEGATIVE_EXPONENTIAL: {
00239                 /* 1st order fall-off. I am very partial to using the half-life as    */
00240                 /* controlling parameter. Using the 'normal' exponent is not very     */
00241                 /* intuitive...                                                       */
00242                 /* tmpval = new CFloatValue( (1.0 / m_parameter1)                     */
00243                 tmpval = new CFloatValue( (m_parameter1) 
00244                                                                   * (-log(1.0 - m_base->DrawFloat())) );
00245 
00246         }
00247         break;
00248         default:
00249         {
00250                 /* unknown distribution... */
00251                 static bool randomWarning = false;
00252                 if (!randomWarning) {
00253                         randomWarning = true;
00254                         std::cout << "RandomActuator '" << GetName() << "' has an unknown distribution." << std::endl;
00255                 }
00256                 return false;
00257         }
00258         }
00259 
00260         /* Round up: assign it */
00261         CValue *prop = GetParent()->GetProperty(m_propname);
00262         if (prop) {
00263                 prop->SetValue(tmpval);
00264         }
00265         tmpval->Release();
00266 
00267         return false;
00268 }
00269 
00270 void SCA_RandomActuator::enforceConstraints() {
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,100,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 */