I don't need to test my programs. I have an error-correcting modem.
Difference between revisions of "Legacy:Useful Maths Functions"
m (→Vector operators) |
m (fixed some of the headings' formatting) |
||
Line 200: | Line 200: | ||
Where URot is an unreal rotation unit (used in [[Legacy:Rotator|rotator]]s). | Where URot is an unreal rotation unit (used in [[Legacy:Rotator|rotator]]s). | ||
− | + | ===arcSin function === | |
Inverse Sin function. Pass the sin of an angle, and it returns the angle: | Inverse Sin function. Pass the sin of an angle, and it returns the angle: | ||
Line 216: | Line 216: | ||
</uscript> | </uscript> | ||
− | + | ===arcCos funtion === | |
Inverse Cosine funtion. Pass the cosine of an angle and the angle is returned: | Inverse Cosine funtion. Pass the cosine of an angle and the angle is returned: | ||
Line 235: | Line 235: | ||
</uscript> | </uscript> | ||
− | + | ===aTan2 funtion === | |
ArcTangent2. Pass the adjacent triangle leg as X and the opposite leg as Y (or X compoent, Y compont of a triangle inscribed in a unit circle).: | ArcTangent2. Pass the adjacent triangle leg as X and the opposite leg as Y (or X compoent, Y compont of a triangle inscribed in a unit circle).: | ||
Line 270: | Line 270: | ||
</uscript> | </uscript> | ||
− | + | ===aSin2 funtion === | |
ArcSin2: a more precise asin. Y=opposite leg, R=radius/hypotenuse. Of course if R is 0, no line exists, so it will return 0. Note that this assumes you put the asin code in your script. | ArcSin2: a more precise asin. Y=opposite leg, R=radius/hypotenuse. Of course if R is 0, no line exists, so it will return 0. Note that this assumes you put the asin code in your script. | ||
Line 290: | Line 290: | ||
</uscript> | </uscript> | ||
− | + | ===aCos2 funtion === | |
a more precise acos. X=adjecent leg, R=opposite. Other rules are similar to ASin2: | a more precise acos. X=adjecent leg, R=opposite. Other rules are similar to ASin2: | ||
Line 344: | Line 344: | ||
</uscript> | </uscript> | ||
− | + | ===Rotate vector === | |
Rotate one vector towards another vector. | Rotate one vector towards another vector. | ||
Line 396: | Line 396: | ||
'''Swamy:''' You may also find this function usefull. It returns a Rotator with the Pitch and Yaw values between the given Vectors | '''Swamy:''' You may also find this function usefull. It returns a Rotator with the Pitch and Yaw values between the given Vectors | ||
− | + | ===Rotations between two Vectors === | |
<uscript> | <uscript> | ||
/** | /** |
Latest revision as of 14:06, 8 February 2011
Some useful functions and operators to fill in some of UnrealScript's gaps. See also:
- Scripting Operators for more information and other examples.
- Global Functions for built-in maths functions
See Legacy:Useful Maths Functions/Cleanup
Contents
Number functions and operators[edit]
divides[edit]
This is an operator that returns true if A is a divisor of B: for example:
( 3 divides 5 ) is false ( 3 divides 6 ) is true
static final operator(24) bool divides ( float A , float B ) { B /= A; return int(B) == B; }
Dante: This won't work in most cases because of precision issues with floats. It should "return abs(int(B) - B) < EPS;" with a small EPS value.
Wormbo: Or simply use the ~= operator. (equality after rounding to only a few digits after the comma)
Tarquin: Why would you use numbers with decimals here anyway? The concept is only useful for whole numbers. The given B should be a whole number.
Wormbo: In that case a simple check for B % A == 0
should do the trick, possibly after overriding the % operator for int operands so the results are accurate for large values as well.
Tarquin: True, but I wrote the divides operator because I wanted my code to be easily legible :)
sgn[edit]
This is a function that crops up in maths from time to time, that has proved useful on occasion in UnrealScript. It returns -1, 0 or +1 depending on the sign of the input.
function float Sgn( float theValue ) { if( theValue == 0 ) return 0; return theValue / Abs(theValue); }
Modulo operator[edit]
UnrealScript already has a modulus operator, %
but its behaviour with negative numbers is not consistent with the mathematical definition:
- UnrealScript says -8 % 10 = -8
- Maths says -8 % 10 = 2
Interestingly enough, this also occurs with C's own % operator, showing the source of the "bug".
static final operator(18) float mod ( float A, float B ) { if( A % B >= 0 ) return A % B ; else return ( A % B ) + B ; }
j3rky: Sorry, but this is complete nonsense. The modulo operator is defined as the remainder after integer division of its first argument by its second argument. The integer remainder of -8 divided by 10 is -8, the behaviour of UnrealScript and, of course, all C and C++ compilers is correct. Your misunderstanding may be based on the fact that the modulo operator for A % B, where A is a POSITIVE integer, can be implemented as while (A >= B) { A -= B; }. However, this doesn't mean that for negative integers it has to be implemented as while (A + B < B) { A += B }.
Chinju: Well, actually, many would consider the remainder after integer division of -8 divided by 10 to be 2, thinking that -8 == -1 * 10 + 2. In fact, (-8)/10 can be implemented as either -1 or 0 and (-8)%10 can be implemented as either -8 or 2 by a compliant C compiler, so even the C standard allows us to use the proposed "mathematical" definition of modulus. That having been said, there isn't one single mathematical definition of the modulus operator, but it's fairly conventional to define it in such a way that 0 <= a mod n < n (by sending a mod n to the equivalence class of integers modulo n containing a, and then designating that class by its unique member in the appropriate range).
j3rky: I think we have to be more accurate here. First of all, there IS a mathematical definition of the modulus operator which has been created a long time before computers were invented. It is based on equivalence classes and can be written out as remainder = A - (B * q), where q is as large as possible without exceeding the magnitude of the true quotient of A / B, and q will have the same sign as A / B. This will always lead to positive remainders, and in our case it would actually be 2 (the remainder can be thought of as "borrowed" from B * q). In computational arithmetics, however, the modulus operator is defined as the remainder of the integer division of A / B, and therefore the remainder will be negative if A < 0. Now the ugly part begins when dealing with floating-point numbers because the floating-point implementation of the modulus operator is not the same as the so called "remainder" operation as defined by IEEE. In IEEE 754 the remainder operation is a rounding division, not a truncating division, and therefore complies with the historical definition based on equivalence classes. In computational arithmetics on the other hand, the floating-point modulus operator is usually implemented analogous to the integer remainder operation. This is especially true for the floating-point % operator in Java and various scripting languages, as well as the fmod function in C (which is also used in UnrealScript).
Foogod: Heh.. Actually, j3rky, I think you just said exactly the same thing as the original text above does: The typical definition of the "modulo" operator in computational arithmetics (remainder) is not the same as the traditional mathematical definition of a "modulus" ("A - (B * q)") (so basically, you just agreed with what you had called "nonsense" earlier). To be strictly clear, in programming, the modulo operator for integers is typically defined by the equivalence: A = (A / B) * B + (A % B). Thus the sign for negative operands depends on what the result of integer division is for negative numbers, which varies from language to language (and in some cases, from computer to computer for the same language). As far as floating point numbers, the typical definition of a modulo operator for floating point numbers derives from the equivalence: A = floor(A / B) * B + (A % B), and thus depends on the definition of the "floor" operation for negative numbers, which also varies from language to language (hopefully, but not always, consistently with integer division).
Regardless of any of this discussion, however, the point remains that both forms of "modulo" operation are useful in different contexts, and therefore it is useful to have the alternative form available, which is why it's here.
Sweavo: If have an integer input and you want to bound your value to a nice binary number, e.g. [0..7] or [0..255] then you can do this efficiently and elegantly using masking:
val_5bits &= 0x001f; val_6bits &= 0x003f; val_6bits &= 0x007f; val_8bits &= 0x00ff;
Whatever val_5bits had in there before, it's had its more significant bits thrown away. Great for keeping track of rotation for example, and has the desirable properties described (as long as your mask value is all binary ones from whenever it starts down till the end).
Modulo operator for Integers[edit]
UnrealScript's modulo operator %
takes float parameters, but this can cause precision errors for large integers. The following implementation overrides the operator for integer parameters and works exactly as the built-in float version:
static final operator(18) int % (int A, int B) { return A - (A / B) * B; }
Note that the implementation exhibits the same "non-mathematical" behavior for negative numbers as the float version. See the previous section for a potential fix.
UArch shouldnt this be A - (A / B) * B;? :P ...fixed
Log[edit]
UnrealScript has a natural log (Loge) function, but no log with a variable base. This function allows that. 10 is the default if 0 or no base is passed:
static final function float Logarithm (float a, optional float Base){ if (Base==0) Base=10; return Loge(a)/Loge(base); }
Next power of 2[edit]
Returns the next power of 2 or the input value if it is already a power of 2:
static final function int NextPowerOfTwo( coerce int in ) { in -= 1; in = in | (in >> 16); in = in | (in >> 8); in = in | (in >> 4); in = in | (in >> 2); in = in | (in >> 1); return in + 1; }
Tarquin: Why not just use log?
Switch`: True, log may be better. Do you mean something like this?
final static function int NextPowerOfTwo( coerce int in ) { return 1 << int(Loge(in-1) / Loge(2)+1); } final static function int PrevPowerOfTwo( coerce int in ) { return 1 << int(Loge(in) / Loge(2)); }
Log method is faster by about 30%.
Foxpaw: This is interesting. Why is the log method faster? Shifting and or-ing seems like it should be MUCH faster. Is the difference in the overhead from the Unrealscript interpreter?
Foogod: In C I'm fairly certain that the bit-shifting approach would be much faster, but in UnrealScript it's very likely that the interpreter overhead would outweigh anything else. We are talking about (at least) 12 interpreter operations for the shifting version as opposed to 6 for the log version, which means half as many runs through a potentially fairly complicated (with OO and all) interpreter loop. The moral of this story: Do as little as you can in UnrealScript and use native functions as much as possible, even expensive natives are better than a bunch of cheap UnrealScript operations.
Sweavo: have you actually tried that or are you theorising? Just interested to know how sure we are of these findings!
Bitwise Switch[edit]
Kohan: This was something I came up with when trying to compare binary to chromosomes; when cells perform meiosis, their chromosomes collide and switch chromatids at the point of collision. I decided to port this to a bitwise function that I figured could be used for some sort of breeding game, I don't know. I just think it's cool.
static final function int BitSwitch( int A, int B, int C ) { return a ^ (a ^ b) & c; }
Now, what this does is it compares A and B and switches their bits when C's respective bit is 1 (C represents random occurences of collision) and returns the resulting A. Example:
01010100 A 10101001 B 10101010 C -------- Switch 11111100
I guess those weren't the best random numbers... Anyway, that's the idea. The human logic is that you are switching bits. In computer terms, it is pretty pointless to switch a 1 and a 1, or a 0 and a 0, so a XOR is performed on A and B, resulting in a list of valid switching bits. Second, this list is ANDed with C, further decreasing the number of bits to be flipped (usually). So, if you were to Switch an A and B with a maximum-numerical-value C, you would effectively switch their values, or, at least, return a value of B. Let us take the above example:
01010100 A 10101001 B 11111111 C -------- Switch 10101001
Well, I do hope I'm not pointless, and I hope someone might someday use this :).
Trigonometic functions[edit]
Unrealscript includes Sin, Cos, Tan, and arcTan functions, defined in the Object class. The sin, cos, and tan assume an angle in radians is passed and aTan will return an angle in radians. Here are some const conversions:
Const RadianToDegree = 57.2957795131; Const DegreeToRadian = 0.01745329252; Const RadianToURot = 10430.3783505; Const URotToRadian = 0.000095873799;
Where URot is an unreal rotation unit (used in rotators).
arcSin function[edit]
Inverse Sin function. Pass the sin of an angle, and it returns the angle:
static final function float ASin ( float A ){ if (A>1||A<-1) //outside domain! return 0; if (A==1) //div by 0 checks return Pi/2.0; if (A==-1) return Pi/-2.0; return ATan(A/Sqrt(1-Square(A))); }
arcCos funtion[edit]
Inverse Cosine funtion. Pass the cosine of an angle and the angle is returned:
static final function float ACos ( float A ) { if (A>1||A<-1) //outside domain! return 0; if (A==0) //div by 0 check return (Pi/2.0); A=ATan(Sqrt(1.0-Square(A))/A); if (A<0) A+=Pi; Return A; }
aTan2 funtion[edit]
ArcTangent2. Pass the adjacent triangle leg as X and the opposite leg as Y (or X compoent, Y compont of a triangle inscribed in a unit circle).:
Note that if Y and X are 0, it will return 0, although in reality there is no answer (no line = no angle). The ratio allows for more exact angles (i.e. anywhere on the unit circle, rather than just 1/2 of it).
final static function float ATan2(float Y,float X) { local float tempang; if(X==0) { //div by 0 checks. if(Y<0) return -pi/2.0; else if(Y>0) return pi/2.0; else return 0; //technically impossible (nothing exists) } tempang=ATan(Y/X); if (X<0) tempang+=pi; //1st/3rd quad //normalize (from -pi to pi) if(tempang>pi) tempang-=pi*2.0; if(tempang<-pi) tempang+=pi*2.0; return tempang; }
aSin2 funtion[edit]
ArcSin2: a more precise asin. Y=opposite leg, R=radius/hypotenuse. Of course if R is 0, no line exists, so it will return 0. Note that this assumes you put the asin code in your script.
final static function float ASin2(float Y,float Rad) { local float tempang; if(Rad==0) return 0; //technically impossible (no hypotenuse = nothing) tempang=ASin(Y/Rad); if (Rad<0) tempang=pi-tempang; //lower quads return tempang; }
aCos2 funtion[edit]
a more precise acos. X=adjecent leg, R=opposite. Other rules are similar to ASin2:
final static function float ACos2(float X,float Rad) { local float tempang; if(Rad==0) return 0; //no possible angle tempang=ACos(X/Rad); if (X<0) tempang*=-1; //left quads return tempang; }
Vector operators[edit]
Some simple vector operators, since most operators that you could want for vector have already been defined.
// Extend vector by an unit length static final preoperator vector ++ ( out vector A ) { return A += Normal(A); } // Shrink operator by an unit length static final preoperator vector -- ( out vector A ) { return A -= Normal(A); } // Same thing, but postop static final postoperator vector ++ ( out vector A ) { local vector B; B = A; A += Normal(A); return B; } static final postoperator vector -- ( out vector A ) { local vector B; B = A; A += Normal(A); return B; }
Rotate vector[edit]
Rotate one vector towards another vector.
//Rotate vector A towards vector B, an amount of degrees. static final function RotateVector( out vector A, vector B, float Degree ) { local float Magnitude; local vector C; Degree = Degree * Pi / 180.0;//Convert to radians. Magnitude = VSize(A); A = Normal(A); B = Normal(B); if( A Dot B == -1.0 )//Vectors are pointing in opposite directions. B.x += 0.0001;//fudge it a little C = Normal(B - (A Dot B) * A);//This forms a right angle with A A = Normal( A * Cos(Degree) + C * Sin(Degree) ) * Magnitude; }
Example of RotateVector:
class MySeekingRocket extends SeekingRocketProj; var() float TurnRate;//in degrees simulated function Timer() { if( Seeking != None && Seeking != Instigator ) { RotateVector( Velocity, Seeking.Location - Location, TurnRate * TimerRate ); SetRotation( rotator( Velocity) ); } } defaultproperties { TurnRate=25.000000 }
Jon You see the rocket's velocity vector is being rotated toward its target direction
25 degrees per second.
Swamy: You may also find this function usefull. It returns a Rotator with the Pitch and Yaw values between the given Vectors
Rotations between two Vectors[edit]
/** * RotBetweenVect * * This function returns a rotator with pitch and yaw values which * would be needed to rotate the first passed vector to the second one * * @param A - First vector * @param B - Second vector * @return DeltaRot - Pitch and Yaw rotation between vector A and B */ function Rotator RotBetweenVect(Vector A, Vector B) { local Rotator DeltaRot; local Vector ATop, BTop; //Top projections of the vectors local Vector ASide, BSide; //Side projections of the vectors ATop = A; BTop = B; ATop.Z = 0; BTop.Z = 0; ASide = A; BSide = B; ASide.Y = 0; BSide.Y = 0; DeltaRot.Yaw = acos(Normal(ATop) dot Normal(BTop)) * RadToUnrRot; DeltaRot.Pitch = acos(Normal(ASide) dot Normal(BSide)) * RadToUnrRot; DeltaRot.Roll = 0; return DeltaRot; }
Floating-point maths[edit]
frexp[edit]
Calculates mantissa (a floating-point value between 0.5 and 1) and exponent (an integer value), such that:
f = mantissa * 2exponent
where f is parameter f, mantissa is the value returned by the function and exponent is set by the function.
static final function float frexp( float f, out int e ) { local float m,ex; local int ei; if( f == 0.0 ) { e = 0; return 0; } else { ex = Ceil(Loge(Abs(f)) / Loge(2)); ei = ex; m = ldexp(f, -ei); while( Abs(m) >= 1.0 ) { ei++; m /= 2.0; } while( Abs(m) < 0.5 ) { ei--; m *= 2.0; } e = ei; return m; } }
ldexp[edit]
Calculates the floating point value corresponding to the given mantissa and exponent, such that:
f = mantissa * 2exponent
where f is the value returned by the function, mantissa is parameter m and exponent is is parameter e.
static final function float ldexp( float m, int e ) { return m * (2**e); }
Suggestions: Firstly, don't use <pre></pre>. Indent your code lines by an arbitary amount of blanks instead (two, for instance) – that'll make Wiki automatically format them in a monospaced font and with linebreaks preserved, and it makes the code samples stand out better. Secondly, avoid tabs – there's no standard way for rendering tabs, so you'll be lucky if it comes out as you wish on other people's computers; better use blanks instead. —Mychaeel
- thanks :-) these were a straight copy-paste from a posting I made in Buf-Coding – Tarquin
- Using <uscript></uscript> is way better now. :-P – Wormbo
- Indeed. :-) By the way, as a potentially useful trivia about <uscript>: It expands tabs to spaces in four-character steps because that's the setting I believe Epic's coders used (and UnrealEd's script editor does as well). Best way is to avoid tabs altogether though and indent with spaces... →Mychaeel
Personally, I don't see any use of unary vector operators. Perhaps the author can explain their purpose? (though I admit it serves as a demonstration of proper usage of pre/post operator defining)– UsAaR33
Corran: Does anyone know what the abs() function does? In object.uc it's in the same section as all the trig functions but I can't seem to work out whay it does.
Tarquin: is it abs(float) or abs(vector)? you should move this to Global Function.
Mychaeel: It returns the absolute value of its argument. That is, the positive value of the given number. (And the trigonometric functions only happen to be close to it because they also take a float as their arguments.)
Ironblayde: For the Sgn() function, wouldn't it be better, if theValue turns out to be nonzero, to simply check whether theValue > 0, and then return 1 or -1 as appropriate? A comparison should be faster than a floating-point divide. There are several little things like that in this page where it looks like small, simple optimizations could be made, for example where things like divisions or modulus operations are performed twice when they need only be done once. Maybe those changes don't make a big enough difference in UnrealScript, I don't know.
Also, I don't know if the link might be useful, but this is kind of a cool page that's got some quick little algorithms on it: http://aggregate.org/MAGIC/
Birelli: A greater than comparison vs. a floating point operation being faster depends a lot on the context of the code. If the floating point processor is just sitting idle, then the given method might literally take no "time" at all to calculate. On the other hand comparison operators are usually highly optimized so they're both probably quite fast depending on the situation. In a general library definition like this though it's impossible to take those things into account.
Foxpaw: Hrmm, you would probrably have to test to find out. It's definately not accurate to say that the floating point divide would take no time if the floating point processor was idle. The FPU on an x86-based computer does not take a "list" of operations to perform and output a list of results, acting asynchronously to the main CPU.
Of course, that's a moot point as a comparison in the x86 architechture takes the form of a subtraction and then a logical test of the sign bit, so with floating point numbers you're going to use the FPU either way. A floating point subtraction is probrably faster than a floating point division, but it's also possible that the time for the FPU to compute is based on a worst case and is not different for different operations. (Unlikely, but possible.)
However, I would speculate that the comparison is almost definately faster as you are not only doing a floating point divide, but also taking the absolute value. This of course is most easily done by just masking off the sign bit, but that still takes at the very least one operation.
Hrm.. however, it's worth noting that the most time consuming part of the entire Sgn() function is the overhead involved in actually calling the function, especially in an object oriented interpreted language like Unrealscript. (in which making a function call is less trivial than it seems, and definately not "free" as some people would have you believe ;)) Putting the code in your function directly instead of making it an operator would be faster.
Hrrrrmmmm. Now that I think about it, the theValue == 0 can probrably be optimized by casting it to bool. In assembly lanuage that would work, but I don't know if Unrealscript has any unnecessary overhead for such casting. I guess you'd have to time it to find out.
How's this:
function float Sgn( float theValue ) { if( Bool(theValue) ) { if ( theValue > 0 ) return 1; else return -1; } return 0; }
Ah, you know it's optimized when it's no longer readable. ;)
Foxpaw: In response to the comment, (that you removed) floating point numbers do have a sign bit and you are correct that that would be faster. However, you cannot do a bitwise and on a floating point number to mask ioff the sign bit like that in Unrealscript. :(
Birelli: I know, that's why I removed it ;). I was thinking in C (I do that a lot) :)
Matariel: Umm guys, bit of a noobie question here but, how come you've listed all of those custom arc functions? Why not just use the arc functions defined in Object? I know that in ut2004 there are arc functions defined in Object; not sure if there are any in UTor ut2003 though :p
Wormbo: They are new to UnrealEngine2.
Matariel: I see. Alright then :D
Solid Snake: See some of the function use a division by 2.0, I think it is faster to simply multiply by 0.5. I've done that in a lot of my code, but never really considered whether it was faster or not. Just as a rule I do that.
Xian: I was curious how an URot unit is converted to Rads (and viceversa). The only conversions I know of are Degs <-> Rads.
Wormbo: See Rotator.