Blender V2.61 - r43446
|
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", ¶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 */