|
Blender
V2.59
|
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", ¶Arg)) { 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", ¶Arg)) { 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", ¶Arg)) { 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", ¶Arg1, ¶Arg2)) { 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", ¶Arg)) { 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", ¶Arg)) { 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", ¶Arg1, ¶Arg2)) { 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", ¶Arg1, ¶Arg2)) { 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", ¶Arg)) { 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 */